Pollen Sensor for Germany based on DWD (Deutscher Wetter Dienst)

Hello,

did it! Was just a silly fault from ym side.

Below the code for Baden-Württemberg

Regards, Ralf

#################################################################
# Packages/Pollen
#################################################################

 
### DWD Pollenwarnungen
sensor:        
  - platform: rest
    scan_interval: 43200 
    resource: https://opendata.dwd.de/climate_environment/health/alerts/s31fg.json
    name: pollen_111_birke # Baden-Württemberg
    value_template: '{% for region in value_json.content -%}{%- if region.partregion_id == 111 %}{{region.Pollen.Birke}}{% endif -%}{%- endfor %}'
    json_attributes:
      - next_update
  
  - platform: rest
    scan_interval: 43200   
    resource: https://opendata.dwd.de/climate_environment/health/alerts/s31fg.json
    name: pollen_111_graeser # Baden-Württemberg
    value_template: '{% for region in value_json.content -%}{%- if region.partregion_id == 111 %}{{region.Pollen.Graeser}}{% endif -%}{%- endfor %}'
    json_attributes:
    - next_update
      
  - platform: template
    sensors:
      pollen_111_graeser_today:
        friendly_name: 'Gräser Heute'
        value_template: >
          {{ states('sensor.pollen_111_graeser')
               |regex_findall_index("'today': '[-0-9]+'")
               |regex_findall_index("[-0-9]+") }}
        entity_id:
         - sensor.pollen_111_graeser
      pollen_111_graeser_tomorrow:
        friendly_name: 'Gräser Morgen'
        value_template: >
          {{ states('sensor.pollen_111_graeser')
               |regex_findall_index("'tomorrow': '[-0-9]+'")
               |regex_findall_index("[-0-9]+") }}
        entity_id:
         - sensor.pollen_111_graeser
      pollen_111_graeser_dayaftertomorrow:
        friendly_name: 'Gräser Übermorgen'
        value_template: >
          {{ states('sensor.pollen_111_graeser')
               |regex_findall_index("'dayafter_to': '[-0-9]+'")
               |regex_findall_index("[-0-9]+") }}
        entity_id:
         - sensor.pollen_111_graeser
      pollen_111_birke_today:
        friendly_name: 'Birke Heute'
        value_template: >
          {{ states('sensor.pollen_111_birke')
               |regex_findall_index("'today': '[-0-9]+'")
               |regex_findall_index("[-0-9]+") }}
        entity_id:
         - sensor.pollen_111_birke
      pollen_111_birke_tomorrow:
        friendly_name: 'Birke Morgen'
        value_template: >
          {{ states('sensor.pollen_111_birke')
               |regex_findall_index("'tomorrow': '[-0-9]+'")
               |regex_findall_index("[-0-9]+") }}
        entity_id:
         - sensor.pollen_111_birke
      pollen_111_birke_dayaftertomorrow:
        friendly_name: 'Birke Übermorgen'
        value_template: >
          {{ states('sensor.pollen_111_birke')
               |regex_findall_index("'dayafter_to': '[-0-9]+'")
               |regex_findall_index("[-0-9]+") }}
        entity_id:
         - sensor.pollen_111_birke
1 Like

Great, thanks a lot for sharing this! My nose is my best sensor atm, but i’ll gladly add this to my setup :wink:
Gruß aus Bonn!

Can you write once, which values ​​correspond to which load levels. And as you have the whole thing in the yaml - File represented?

Some weeks ago I already created a custom component for DWD pollen information. You might want to check it out at https://github.com/marcschumacher/dwd_pollen. Let me know if you like it and feel free to open issues!

2 Likes

[jackeroo_marc] I tried your platform. It works great. Had the fear that it would not work without the requirements under Hass.io in Docker. Fortunately, that’s not the case. Great!

Find my official announcement here: New custom component DWD Pollen sensor (Deutscher Wetterdienst, German Weather Service)

1 Like

Hi Marc, nice I will try this also.

As far as I can see in your sensor.py there will just be the values from 0-6 as return? Currently I’m using a template sensor in addition to the REST sensor to give me a readable output instead of the dwd index:

- platform: template
  scan_interval: 1800
  sensors:
    pollen_92_graeser_today:
      friendly_name: 'Gräser'
      value_template: >
        {% if (states('sensor.pollen_92_graeser')|regex_findall_index("'today': '[-0-9]+'")|regex_findall_index("[-0-9]+") == "0-1") %} Keine bis geringe Belastung
        {% elif (states('sensor.pollen_92_graeser')|regex_findall_index("'today': '[-0-9]+'")|regex_findall_index("[-0-9]+") == "1") %} Geringe Belastung
        {% elif (states('sensor.pollen_92_graeser')|regex_findall_index("'today': '[-0-9]+'")|regex_findall_index("[-0-9]+") == "1-2") %} Geringe bis mittlere Belastung
        {% elif (states('sensor.pollen_92_graeser')|regex_findall_index("'today': '[-0-9]+'")|regex_findall_index("[-0-9]+") == "2") %} Mittlere Belastung
        {% elif (states('sensor.pollen_92_graeser')|regex_findall_index("'today': '[-0-9]+'")|regex_findall_index("[-0-9]+") == "2-3") %} Mittlere bis hohe Belastung
        {% elif (states('sensor.pollen_92_graeser')|regex_findall_index("'today': '[-0-9]+'")|regex_findall_index("[-0-9]+") == "3") %} Hohe Belastung
        {% else %} Keine Belastung {% endif %}

But maybe I can also get this now in manipulation the sensor.py. I will see :slight_smile: Else I will do it the same way as with the REST sensor.

Edit: RTFM to myself :smiley: I saw in the documentation that there will be this readable format in the description attribute. This will help.

I created my own Sensor with a costum card in Lovelave:

lkjGRSUQda
in configuration.yaml:

sensor:
#DWD Pollen
  - platform: rest
    scan_interval: 43200
    name: "DWD Pollen"
    resource: https://opendata.dwd.de/climate_environment/health/alerts/s31fg.json
    value_template: "{{ value_json.last_update }}"
    json_attributes_path: "$.content[3]"
    json_attributes:
        - region_name
        - Pollen

#one Template Example  
  - platform: template
    sensors:
        #DWD Pollen
        dwd_hasel:
          friendly_name: "Hasel"
          value_template: >
                        {% set Hasel = state_attr('sensor.dwd_pollen', 'Pollen')['Hasel']['today'] %}
                        {% if Hasel == "3" or Hasel == "2-3" %} mittlere bis hohe Belastung
                        {% elif Hasel == "2" or Hasel == "1-2" %} geringe bis mittlere Belastung
                        {% elif Hasel == "1" or Hasel == "0-1" %} keine bis geringe Belastung 
                        {% else %} keine Belastung {% endif %}
          icon_template: >
                        {% set Hasel = state_attr('sensor.dwd_pollen', 'Pollen')['Hasel']['today'] %}
                        {% if Hasel == "3" or Hasel == "2-3" %} emoticon-dead-outline
                        {% elif Hasel == "2" or Hasel == "1-2" %} mdi:emoticon-sad-outline
                        {% elif Hasel == "1" or Hasel == "0-1" %} mdi:emoticon-neutral-outline
                        {% else %} mdi:emoticon-happy-outline {% endif %}

And in Lovelace:

 resources:
  - url: /hacsfiles/lovelace-multiple-entity-row/multiple-entity-row.js
    type: module   


 - type: entities
    entities:
      - entity: sensor.dwd_pollen
        type: custom:multiple-entity-row
        name: "Pollenflug"
        icon: "mdi:tree"
        show_state: false
        state_color: false
        entities:
          - entity: sensor.dwd_ambrosia
            icon: true
          - entity: sensor.dwd_beifuss
            icon: true
          - entity: sensor.dwd_birke
            icon: true
          - entity: sensor.dwd_esche
            icon: true
          - entity: sensor.dwd_graeser
            icon: true
          - entity: sensor.dwd_hasel
            icon: true
          - entity: sensor.dwd_roggen
            icon: true

Greeting

2 Likes

I added a sensor, which is updating at 0 o’clock to the tomorrow pollution and at 11 o’clock to the new data!

        dwd_date:
          friendly_name: "DWD Datum"
          value_template: >
                        {{ "today" if (as_timestamp(strptime(states('sensor.dwd_pollen'), '%Y-%m-%d %H:%M Uhr')) | timestamp_custom('%Y-%m-%d')) ==  states('sensor.date') 
                        else "tomorrow" }}
        dwd_hasel:
          friendly_name: "Hasel"
          value_template: >
                        {% set Hasel = state_attr('sensor.dwd_pollen', 'Pollen')['Hasel'][states('sensor.dwd_date')] %}
                        {% if Hasel == "3" or Hasel == "2-3" %} mittlere bis hohe Belastung
                        {% elif Hasel == "2" or Hasel == "1-2" %} geringe bis mittlere Belastung
                        {% elif Hasel == "1" or Hasel == "0-1" %} keine bis geringe Belastung 
                        {% else %} keine Belastung {% endif %}
          icon_template: >
                        {% set Hasel = state_attr('sensor.dwd_pollen', 'Pollen')['Hasel'][states('sensor.dwd_date')] %}
                        {% if Hasel == "3" or Hasel == "2-3" %} mdi:emoticon-dead-outline
                        {% elif Hasel == "2" or Hasel == "1-2" %} mdi:emoticon-sad-outline
                        {% elif Hasel == "1" or Hasel == "0-1" %} mdi:emoticon-neutral-outline
                        {% else %} mdi:emoticon-happy-outline {% endif %}


2 Likes

This is awesome. Exactly what i was looking for. However, where did you select the region? I’m trying to find where you put that but i cant find it… Unless im blind :confused:

The number is the region key:

resource: https://opendata.dwd.de/climate_environment/health/alerts/s31fg.json
  name: pollen_101_graeser # Rhein, Pfalz, Nahe und Mosel

You have to find your number on the DWD website. In my case it is 121 for “South of Munich” or “Oberbayern, Allgäu”.

When you go on the opendata link, you got a JSON String. I used the content number for the different region, not like the other variant of this sensor here in this thread, which is using the partregion_id into the content path.

I extract from JSON String, for my region, into a sensor attribute for a template sensor. Like above.

As an example for my region “Westl. Niedersachsen/Bremen” it is the content number “3”:

It may work for some time, but it will fail over time. The data is sorted by the number you choose, but the content may change, if other data will get available.

Right now you have 26 or 27 “content numbers”, if more info from other or new weather stations comes available, they will be added to this file, so the numbering will change.

Tbh I don’t see any advantage at all to simply use a number over an id, that is unique and therefore lasting much longer. :slight_smile: And you already got where you needed to be to find out the correct partregion_id. :slight_smile:

Ok, the problem is, I don’t know how to extract the partregion_id for a REST Sensor, to use it like me :thinking:

Where did you see a REST sensor in this component? Oh, you use a REST sensor, you don’t use this component… :crazy_face:

Please, explain a little further what you’re doing or trying to do, shouldn’t be that big of a problem to get what we want out of this json-file. :slight_smile:

I am using this, to extract the pollution for my region into the attributes of my dwd_pollen REST-Sensor. And with this sensor I go into a template for the state and icon for each pollen typ.

    json_attributes_path: "$.content[3]"
    json_attributes:
        - region_name
        - Pollen

The problem is, what you described, is when the content number change. So I need a way to extract the JSON Path for my region with region_id and not with the content number. It would be nice, when the pollen path is into the sensor attributes of my dwd_pollen sensor. Like now :crazy_face:

I tried to extract this over the region_id, but my knowledge about JSON isn’t that big

What I still don’t get, 2nd screenshot.
If I blow this into a new yaml sitting in a sensors directory (using sensor: !include_dir_list sensors/)
why the heck does it moan about:
a) 2 lines in a yaml starting " platform: rest" <== editor moans about that one
b) having a line " platform: rest" and somewhere later " platform template" <== editor moans aswell

HA is something a real lottery, especially if being new with it collection interesting stuff from within this community. Since even if things are marked “solved” this doesn’t mean it’ll work at all.

The worst one was the one mention at pt. a) since the HA editor flagged this RED while the settings>server>check configuration validated it green. Means nice to have such checks but as long as these tend to act as a lottery either the whole configuration is sort of rolling dice.

Just besode the fact that I got everything running by now and simply want to thank for the detailed explanation incl. the one with the content number some lines below. Works like a dream.

Even though the topic is quite old, I wanted to share my slightly improved sensor since I managed to solve the issue of dealing with the array and only picking the item for the partregion_id I’m interested in (by using JSONPath) and not using an unreliable array index which can change.

sensor:
  # DWD Pollen, see: https://opendata.dwd.de/climate_environment/health/alerts/Beschreibung_pollen_s31fg.pdf
  # (partregion_id=111 for "Oberrhein und unteres Neckartal")
  - platform: rest
    scan_interval: 3600
    name: "DWD Pollen"
    resource: https://opendata.dwd.de/climate_environment/health/alerts/s31fg.json
    json_attributes_path: "$..content[?(@.partregion_id==111)].Pollen"
    json_attributes:
      - Erle
      - Beifuss
      - Ambrosia
      - Birke
      - Esche
      - Hasel
      - Graeser
      - Roggen
    value_template: "{{ value_json.last_update }}"
  - platform: template
    sensors:
      dwd_pollen_erle:
        icon_template: "mdi:tree-outline"
        friendly_name: "Erle"
        value_template: >-
          {% set dwd_state = state_attr('sensor.dwd_pollen', 'Erle')['today'] %}
          {% if dwd_state == "3" %}6{% elif dwd_state == "2-3"%}5{% elif dwd_state == "2"%}4{% elif dwd_state == "1-2"%}3{% elif dwd_state == "1"%}2{% elif dwd_state == "0-1"%}1{% else %}0{% endif %}
        attribute_templates:
          today: >-
            {% set dwd_state = state_attr('sensor.dwd_pollen', 'Erle')['today'] %}
            {% if dwd_state == "3" %}6{% elif dwd_state == "2-3"%}5{% elif dwd_state == "2"%}4{% elif dwd_state == "1-2"%}3{% elif dwd_state == "1"%}2{% elif dwd_state == "0-1"%}1{% else %}0{% endif %}
          tomorrow: >-
            {% set dwd_state = state_attr('sensor.dwd_pollen', 'Erle')['tomorrow'] %}
            {% if dwd_state == "3" %}6{% elif dwd_state == "2-3"%}5{% elif dwd_state == "2"%}4{% elif dwd_state == "1-2"%}3{% elif dwd_state == "1"%}2{% elif dwd_state == "0-1"%}1{% else %}0{% endif %}

You have to replace the 111 in the json_attributes_path of the first REST sensor with your partregion_id. This REST sensor then has one attribute for each pollen type. The second sensor (template type) is just an example, you have to repeat this for every pollen type, it’s sadly quite repetitive. Its value is a number between 0 and 6 (indicating the intensity today) and the attributes contain todays’ and tomorrows’ intensity. So you’re quite flexible with using it in a Lovelace card.

5 Likes

That’s a great snippet to use, thanks a lot! :slight_smile:

But as far as I can see, this doesn’t work, does it? :wink: Either you call the rest sensor “DWD Pollenbelastung” or you change the template sensors from
{% set dwd_state = state_attr('sensor.dwd_pollenbelastung', 'Erle')['today'] %}
to
{% set dwd_state = state_attr('sensor.dwd_pollen', 'Erle')['today'] %}

Either way great contribution! :slight_smile:

@dyfcom
I am very sorry, I totally missed your reply and didn’t answer. Sorry for that! Fortunately @rkallensee did a great job with his snippet. :slight_smile:

Thanks for the hint - absolutely true. :sweat_smile: I fixed my example.

2 Likes