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

I saw several pollen sensor implementations, official components or here in the forum. But none showed data for Germany so I came up with my own solution.

The DWD Pollen data is published every day under this url:
https://opendata.dwd.de/climate_environment/health/alerts/s31fg.json

In it one can search the subregion_id and the Allergens he is interested in.

I put this in a Rest Sensor and pull out the values for today, tomorrow and the day after tomorrow with a template sensor. But it does not quite work yet.
Can somebody help me out?^^

The rest sensor currently show information in the form of:
{'tomorrow': '2', 'today': '2', 'dayafter_to': '1-2'}

- platform: rest
  scan_interval: 43200   
  resource: https://opendata.dwd.de/climate_environment/health/alerts/s31fg.json
  name: pollen_101_graeser # Rhein, Pfalz, Nahe und Mosel
  value_template: '{% for region in value_json.content -%}{%- if region.partregion_id == 101 %}{{region.Pollen.Graeser}}{% endif -%}{%- endfor %}'
- platform: rest
  scan_interval: 43200 
  resource: https://opendata.dwd.de/climate_environment/health/alerts/s31fg.json
  name: pollen_101_birke # Rhein, Pfalz, Nahe und Mosel
  value_template: '{% for region in value_json.content -%}{%- if region.partregion_id == 101 %}{{region.Pollen.Birke}}{% endif -%}{%- endfor %}'
- platform: rest
  scan_interval: 43200  
  resource: https://opendata.dwd.de/climate_environment/health/alerts/s31fg.json
  name: pollen_101_roggen # Rhein, Pfalz, Nahe und Mosel
  value_template: '{% for region in value_json.content -%}{%- if region.partregion_id == 101 %}{{region.Pollen.Roggen}}{% endif -%}{%- endfor %}'
- platform: template
  sensors:
    pollen_101_graeser_today:
      friendly_name: 'Gräser Heute'
      value_template: '{{ states.sensor.pollen_101_graeser.value_json.today}}'
    pollen_101_graeser_tomorrow:
      friendly_name: 'Gräser Morgen'
      value_template: '{{ states.sensor.pollen_101_graeser.value_json.tomorrow}}'
    pollen_101_graeser_dayaftertomorrow:
      friendly_name: 'Gräser Übermorgen'
      value_template: '{{ states.sensor.pollen_101_graeser.value_json.dayafter_to}}'
    pollen_101_birke_today:
      friendly_name: 'Birke Heute'
      value_template: '{{ states.sensor.pollen_101_birke.value_json.today}}'
    pollen_101_birke_tomorrow:
      friendly_name: 'Birke Morgen'
      value_template: '{{ states.sensor.pollen_101_birke.value_json.tomorrow}}'
    pollen_101_birke_dayaftertomorrow:
      friendly_name: 'Birke Ăśbermorgen'
      value_template: '{{ states.sensor.pollen_101_birke.value_json.dayafter_to}}'
    pollen_101_roggen_today:
      friendly_name: 'Roggen Heute'
      value_template: '{{ states.sensor.pollen_101_roggen.value_json.today}}'
    pollen_101_roggen_tomorrow:
      friendly_name: 'Roggen Morgen'
      value_template: '{{ states.sensor.pollen_101_roggen.value_json.tomorrow}}'
    pollen_101_roggen_dayaftertomorrow:
      friendly_name: 'Roggen Ăśbermorgen'
      value_template: '{{ states.sensor.pollen_101_roggen.value_json.dayafter_to}}'
4 Likes

Yeah, the state of the rest sensors may look like a map/dictionary, but it’s really a string. I ran into this before. The way I solved the problem was by using the regex_findall_index filter. E.g.:

- platform: template
  sensors:
    pollen_101_graeser_today:
      friendly_name: 'Gräser Heute'
      value_template: >
        {{ states('sensor.pollen_101_graeser')
             |regex_findall_index("'today': '[-0-9]+'")
             |regex_findall_index("[-0-9]+") }}

Works like a charm. Thank you!

1 Like

Nice! Even though this can be made to work with the REST platform, I think this deserves to be a platform on its own. It should not be hard to implement.

I completely agree. But as I never contributed code to an open source project, yet alone in Python, I wanted to try out the “ugly way” first.
I think I will find some free time to tackle this in a few weeks.
One thing which could be improved is the updating. On the site the next update is stated. It is easily parsed and could be used to update the sensor only once a day when there really is news data.

I hacked together a Python library that simply parses the JSON file. It caches the data until the “next update” time has passed. This could be used as the basis for a HA sensor platform.

2 Likes

@eifinger (someone else) may you please post your complete tenplate sensor here.
I am also searching for a solution for germany but unfortunately don’t know python (yet) to code this.

Thanks, Ralf

@Ralf you can find this part of my config on github:
https://github.com/eifinger/homeassistant-config/blob/master/sensor.yaml#L575-L672

Thanks very much for the link!

Unfortunately, i only get “Unknown” as values.
Just changed the id (from 101 to 110 mathcing my area) but everything else is still same as your code.
Any hints?

Thanks in advance!
/ Ralf

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”.