Hi - great work maintaining this - I find the pollen card very helpful.
I would love to scrape the thunderstorm asthma warning from the same page as well - I’ve tried (and failed) using the css selector in chrome to replicate what you’ve done but I’m out of my depth.
I had a quick go but failed too. I think I’d failed previously too which is why I didn’t put it up.
I feel like this is right but it’s not working for me.
- unique_id: melbourne_thunderstorm_asthma_today
name: Thunderstorm Asthma Forecast Today
select: '#tae-div > div > div > div > div > div > div.uk-grid-match.uk-child-width-1-2\@s.uk-text-center.uk-grid-collapse.uk-grid > div.uk-first-column > div:nth-child(1) > div:nth-child(2) > div'
value_template: '{{ value| trim}}'
attributes:
- name: Website Last Updated
select: '#district-pollen-div > div > div > div > div > div > div.uk-grid-match.uk-child-width-1-2\@s.uk-text-center.uk-grid-collapse.uk-grid > div:nth-child(1) > div.ta-notice'
value_template: "{{ value| replace('Last updated:', '')}}"
icon: >-
{% if value == 'Low' %}
mdi:emoticon-happy
{% elif value == 'Moderate' %}
mdi:emoticon-neutral
{% elif value == 'High' %}
mdi:emoticon-sad
{% elif value == 'Extreme' %}
mdi:emoticon-angry
{% else %}
mdi:help-circle
{% endif %}
I’ve been trying to hack something together this morning to use the asthma forecast.
The HTML nested within the JSON makes it hard to work with.
my solution at the moment is this:
I’m using a shell command to extract just the asthma forecast HTML from the API (this is div 5) asthma_refresh: 'curl "https://api.pollenforecast.com.au/app/json/app_data.php?app=1&version=2" | jq .div5 >/config/www/asthma.html'
I’m running this as a service every 30 mins.
I’m then running multiscrape over the output:
- resource: http://[IP address]/local/asthma.html
scan_interval: 3600 #3600 sec = hr
name: Asthma forecast
sensor:
- unique_id: asthma_forecast_scrape
name: Asthma forecast
select: '#\\\"ta-forecast-table\\\" > tbody > tr:nth-child(1) > td.\\\"center\\\" > p'
attributes:
- name: Website Last Updated
select: 'body > div > div > div:nth-child(8) > p'
This is not a neat way to do it, but it does give me a sensor in homeassistant.
Nice progress. Better than my attempts. Yeah I had the same struggles with the html nested in the json.
I managed to get the forecast html in as text using the rest sensor, then I was tinkering with some very gross parsing of the HTML as text, despite finding some hilarious warnings.
Then I was going to try to somehow parse the html from each of the above json_attributes it into some simplified json that could be stored nested and then queried. I dunno if that makes sense.
Output of above template starting to look a bit like json pairs.
Thunderstorm Asthma Forecast :
District Risk of thunderstorm asthma
: Central : low
East Gippsland : low
Mallee : low
North Central : low
North East : low
Northern Country : low
South West : low
West and South Gippsland : low
Wimmera : low
Note: The above epidemic thunderstorm asthma forecast information was provided by the Victorian Department of Health and Human Services and the Bureau of Meteorology. For more information pertaining to these forecasts visit this website. Last updated: 2021-11-22 14:00:00; Current date: 2021-11-22
Update #2: On my “steaming pile of poo solution”
With the same rest sensor above, you can then create a template sensor like this to do some dogy parsing of the html into json
This is the main post which I will try to keep up to date with a working solution.
The solution has been breaking periodically due to changes in the unofficial API and the pretty fragile nature of my solution. I’m no coding guru I just slapped together something pretty hacky.
But don’t worry I’m pretty invested in keeping it working if possible (self-interest;).
I’ve added change notes at the bottom of this post, so hopefully you can check if you are up to date with changes before flagging an issue.
Then two template sensors in your config parses the html with regex (probably fragile and a bad idea).
The ‘melbourne_pollen_forecast_api’ sensor covers the basics and feeds the card.
The ‘melbourne_pollen_forecast_api_6day_summary_stats’ sensor has some summary stats about the 6 days ahead, which you can use to build up alerts and announcments. For example I have this announced when rising in the morning and at bedtime. It helps me plan ahead. It’s still not quite right.
six_day_outlook_description: The 6 day pollen outlook level is Low. There are 1 moderate days, 0 high days and 0 extreme days. Tommorrow is Low, Saturday is Low, and Sunday is Low.
- platform: template
sensors:
melbourne_pollen_forecast_api:
friendly_name: Melbourne Pollen Forecast via API
value_template: ''
attribute_templates:
melbourne_6day_pollen_forecast: >-
{% set forecastpageHTML = states.sensor.melbourne_pollen_api_html.attributes.forecastpage %}
{% set LowerForecastpageHTML = forecastpageHTML|lower %}
{{
'{\n' +
LowerForecastpageHTML
|replace('monday', 'Mon')
|replace('tuesday', 'Tue')
|replace('wednesday', 'Wed')
|replace('thursday', 'Thu')
|replace('friday', 'Fri')
|replace('saturday', 'Sat')
|replace('sunday', 'Sun')
|replace('melbourne pollen forecast', '"days": [')
|replace('<h2 ', '*nl*{*nl*"day_short_name":"<h1 ')
|replace('</h2>', '</h2>",*nl* ')
|replace('<p', '"date":"<p')
|replace('p>', '<\/p>",*nl* ')
|replace('<div class="pollen-forecast-level', '"pollen_level":"<<div class="pollen-forecast-level')
|replace('low', 'Low"},')
|replace('moderate', 'Moderate"},')
|replace('high', 'High"},')
|replace('extreme', 'Extreme"},')
|striptags
|replace('*nl*', '\n') + ']\n}'
}}
my_district_asthma_forecast_today: >-
{% set myDistrict = 'Central' %}
{% set asthmaForecastHTML = states.sensor.melbourne_pollen_api_html.attributes.div8 %}
{% set myDistrictasthmaForecast = asthmaForecastHTML
|replace('\r\n','')
|replace('>' + myDistrict + '</td>', '>x' + myDistrict + 'x<')
| regex_findall(find='>x' + myDistrict + 'x<'+'([\\s\\S]*?)</p>', ignorecase=False)
|striptags
|replace(' ','')
%}
{{myDistrictasthmaForecast}}
#Change myDistrict value above to one of:
# Central
# East Gippsland
# Mallee
# North Central
# North East
# Northern Country
# South West
# West and South Gippsland
# Wimmera
melbourne_pollen_forecast_api_6day_summary_stats:
friendly_name: Melbourne Pollen Forecast 6 Day Summary Stats
#Todo: Comment code properly
value_template: >-
{% set count_low = namespace(count = 0) %}
{% for days in states.sensor.melbourne_pollen_forecast_api.attributes.melbourne_6day_pollen_forecast.days %}
{%- if days.pollen_level == "Low" -%}
{% set count_low.count = count_low.count+1 %}
{%- endif -%}
{% endfor %}
{% set count_moderate = namespace(count = 0) %}
{% for days in states.sensor.melbourne_pollen_forecast_api.attributes.melbourne_6day_pollen_forecast.days %}
{%- if days.pollen_level == "Moderate" -%}
{% set count_moderate.count = count_moderate.count+1 %}
{%- endif -%}
{% endfor %}
{% set count_high = namespace(count = 0) %}
{% for days in states.sensor.melbourne_pollen_forecast_api.attributes.melbourne_6day_pollen_forecast.days %}
{%- if days.pollen_level == "High" -%}
{% set count_high.count = count_high.count+1 %}
{%- endif -%}
{% endfor %}
{% set count_extreme = namespace(count = 0) %}
{% for days in states.sensor.melbourne_pollen_forecast_api.attributes.melbourne_6day_pollen_forecast.days %}
{%- if days.pollen_level == "Extreme" -%}
{% set count_extreme.count = count_extreme.count+1 %}
{%- endif -%}
{% endfor %}
{% set overall_pollen_rating = (count_low.count*1 + count_moderate.count*2 + count_high.count*3 + count_extreme.count*4)/6 %}
{%- if overall_pollen_rating <= 1 -%}
{% set overall_pollen_level = 'Low' %}
{%- elif overall_pollen_rating <= 2 -%}
{% set overall_pollen_level = 'Moderate' %}
{%- elif overall_pollen_rating <= 3 -%}
{% set overall_pollen_level = 'High' %}
{%- elif overall_pollen_rating <= 4 -%}
{% set overall_pollen_level = 'Extreme' %}
{%- endif -%}
{{overall_pollen_level}}
attribute_templates:
count_of_low_days: >-
{% set count_low = namespace(count = 0) %}
{% for days in states.sensor.melbourne_pollen_forecast_api.attributes.melbourne_6day_pollen_forecast.days %}
{%- if days.pollen_level == "Low" -%}
{% set count_low.count = count_low.count+1 %}
{%- endif -%}
{% endfor %}
{{count_low.count}}
count_of_moderate_days: >-
{% set count_moderate = namespace(count = 0) %}
{% for days in states.sensor.melbourne_pollen_forecast_api.attributes.melbourne_6day_pollen_forecast.days %}
{%- if days.pollen_level == "Moderate" -%}
{% set count_moderate.count = count_moderate.count+1 %}
{%- endif -%}
{% endfor %}
{{count_moderate.count}}
count_of_high_days: >-
{% set count_high = namespace(count = 0) %}
{% for days in states.sensor.melbourne_pollen_forecast_api.attributes.melbourne_6day_pollen_forecast.days %}
{%- if days.pollen_level == "High" -%}
{% set count_high.count = count_high.count+1 %}
{%- endif -%}
{% endfor %}
{{count_high.count}}
count_of_extreme_days: >-
{% set count_extreme = namespace(count = 0) %}
{% for days in states.sensor.melbourne_pollen_forecast_api.attributes.melbourne_6day_pollen_forecast.days %}
{%- if days.pollen_level == "Extreme" -%}
{% set count_extreme.count = count_extreme.count+1 %}
{%- endif -%}
{% endfor %}
{{count_extreme.count}}
six_day_outlook_description: >-
The 6 day pollen outlook level is {{states.sensor.melbourne_pollen_forecast_api_6day_summary_stats.state}}.
There are {{states.sensor.melbourne_pollen_forecast_api_6day_summary_stats.attributes.count_of_moderate_days}} moderate days,
{{states.sensor.melbourne_pollen_forecast_api_6day_summary_stats.attributes.count_of_high_days}} high days and
{{states.sensor.melbourne_pollen_forecast_api_6day_summary_stats.attributes.count_of_extreme_days}} extreme days.
Tommorrow is {{states.sensor.melbourne_pollen_forecast_api.attributes.melbourne_6day_pollen_forecast.days[1].pollen_level}},
Saturday is
{% for days in states.sensor.melbourne_pollen_forecast_api.attributes.melbourne_6day_pollen_forecast.days %}
{%- if days.day_short_name == "Sat" -%}
{{days.pollen_level}}
{%- endif -%}
{% endfor %}, and
Sunday is
{% for days in states.sensor.melbourne_pollen_forecast_api.attributes.melbourne_6day_pollen_forecast.days %}
{%- if days.day_short_name == "Sun" -%}
{{days.pollen_level}}
{%- endif -%}
{% endfor %}.
Then lots of jiggery-pokery in lovelace using the custom button-card (via HACS):
I have manged to get both the API and HtML working.
But for some reason when I try and add it to Lovelave, the code fails
ButtonCardJSTemplateError: TypeError: Cannot read properties of undefined (reading '0') in 'return (states['sensor.melbourne_pollen_forecast_api'].attributes.melbourne_6day_pollen_forecast.d...'
I pretty much copied the lovelace card, so not sure what exactly, i am doing wrong
Thanks as always