How to send notifications for DWD weather warnings

Dear Home Assistant-community,

currently I am using the official integration for the German weather service: DWD
After configuring one or multiple location in HA, you have two sensors for each location:
sensor.dwd_<location>_current_warning_level
sensor.dwd_<location>_advance_warning_level

Each of those sensors provide several attributes incl. warnings like the following from just right now:

region_name: <location name>
region_id: <unique location id>
last_update: 2022-12-06T12:12:25.862000+00:00
warning_count: 2
warning_1_name: GERINGE GLÄTTE
warning_1_type: 84
warning_1_level: 1
warning_1_headline: Amtliche WARNUNG vor GERINGER GLÄTTE
warning_1_description: Es besteht geringe Glättegefahr.
warning_1_instruction: Hinweis auf Rutschgefahr. 
Handlungsempfehlungen: 
Verhalten im Straßenverkehr anpassen
warning_1_start: 2022-12-06T23:00:00+00:00
warning_1_end: 2022-12-07T09:00:00+00:00
warning_1_parameters: null
warning_1_color: #ffeb3b
warning_1: 
start_time: '2022-12-06T23:00:00+00:00'
end_time: '2022-12-07T09:00:00+00:00'
event: GERINGE GLÄTTE
event_code: 84
headline: Amtliche WARNUNG vor GERINGER GLÄTTE
description: Es besteht geringe Glättegefahr.
instruction: |-
  Hinweis auf Rutschgefahr. 
  Handlungsempfehlungen: 
  Verhalten im Straßenverkehr anpassen
urgency: immediate
level: 1
parameters: null
color: '#ffeb3b'

warning_2_name: FROST
warning_2_type: 22
warning_2_level: 1
warning_2_headline: Amtliche WARNUNG vor FROST
warning_2_description: Es tritt leichter Frost zwischen 0 °C und -3 °C auf.
warning_2_instruction: null
warning_2_start: 2022-12-06T19:00:00+00:00
warning_2_end: 2022-12-07T09:00:00+00:00
warning_2_parameters: 
minimum temperature: 0 - -3 [°C]

warning_2_color: #ffeb3b
warning_2: 
start_time: '2022-12-06T19:00:00+00:00'
end_time: '2022-12-07T09:00:00+00:00'
event: FROST
event_code: 22
headline: Amtliche WARNUNG vor FROST
description: Es tritt leichter Frost zwischen 0 °C und -3 °C auf.
instruction: null
urgency: immediate
level: 1
parameters:
  minimum temperature: 0 - -3 [°C]
color: '#ffeb3b'

attribution: Data provided by DWD
icon: mdi:close-octagon-outline
friendly_name: dwd_<location> Current Warning Level

As I want to inform my whole family, I want to write an automation which sends a notification for each warning to all registered phones - and there is my question: How to loop those warnings and create a notification for each warning on all devices? :slight_smile:

Thanks in advance for your assistance!

Best regards,
Dominik

Welcome to the forums!

This is my automation which is based on this topic:


automation:

- id: '202211101953'
  alias: Wetterwarnungen
  description: /packages/wetter/wetterwarnung.yaml
  mode: parallel
  max: 10
  
  trigger:
  
    - platform: state
      entity_id:
        - sensor.wetter_stadt_tralala_current_warning_level
        - sensor.wetter_stadt_tralala_advance_warning_level
      id: stadt_tralala
      
    - platform: state
      entity_id:
        - sensor.wetter_stadt_summsumm_current_warning_level
        - sensor.wetter_stadt_summsumm_advance_warning_level
      id: stadt_summsumm
  
  condition:
  
    - "{{ trigger.from_state.state != 'unavailable' }}"
    - |-
        {{ trigger.to_state.attributes.warning_count !=
        trigger.from_state.attributes.warning_count }}
  
  action:
  
    - service: notify.gmail
      data:
        title: |-
          {% set current = 'sensor.wetter_' ~ trigger.id ~ '_current_warning_level' %}
          {% set advance = 'sensor.wetter_' ~ trigger.id ~ '_advance_warning_level' %}
          {% set current_count = state_attr(current, 'warning_count') %}
          {% set advance_count = state_attr(advance, 'warning_count') %}
  
          {% if current_count in [0, none] and advance_count in [0, none] %}
            Entwarnung fĂźr {{ trigger.to_state.attributes.friendly_name }}
          {% else %}
          {% set region_name = state_attr(current, 'region_name') if 'current' in trigger.entity_id else state_attr(advance, 'region_name') %}
          {{ region_name }}: WETTERWARNUNG ({{ current_count }})
          {% endif %}
        message: |-
          {% set current = 'sensor.wetter_' ~ trigger.id ~ '_current_warning_level' %}
          {% set advance = 'sensor.wetter_' ~ trigger.id ~ '_advance_warning_level' %}
          {% set current_count = state_attr(current, 'warning_count') %}
          {% set advance_count = state_attr(advance, 'warning_count') %}
  
          {% if current_count in [0, none] and advance_count in [0, none] %}
          Die Wetterlage hat sich wieder normalisiert.
          {% else %}
            {% for i in range(current_count) %}
              {% set region_name = state_attr(current, 'region_name') %}
              {% set headline = state_attr(current, 'warning_' ~ loop.index ~ '_headline') %}
              {% set description = state_attr(current, 'warning_' ~ loop.index ~ '_description') %}
              {% set instruction = state_attr(current, 'warning_' ~ loop.index ~ '_instruction') %}
              {% set level = state_attr(current, 'warning_' ~ loop.index ~ '_level') %}
              {% set color = state_attr(current, 'warning_' ~ loop.index ~ '_color') %}
              {% set update = (state_attr(current, 'last_update')|as_timestamp(now()) )|timestamp_custom('%d.%m.%Y, %R', true, 'N/A') %}
              {% set time_start = (state_attr(current, 'warning_' ~ loop.index ~ '_start')|as_timestamp(now()) )|timestamp_custom('%d.%m.%Y, %R', true, 'N/A') %}
              {% set time_end = (state_attr(current, 'warning_' ~ loop.index ~ '_end')|as_timestamp(now()) )|timestamp_custom('%d.%m.%Y, %R', true, 'N/A') %}
              {% set weekday_start = (state_attr(current, 'warning_' ~ loop.index ~ '_start')|as_timestamp(now().timestamp()) )|timestamp_custom('%w', true, now().weekday() )|int %}
              {% set weekday_end = (state_attr(current, 'warning_' ~ loop.index ~ '_end')|as_timestamp(now().timestamp()) )|timestamp_custom('%w', true, now().weekday() )|int %}
  
            {{ region_name }}: {{ headline }}
            {{ ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_start-1] ~ ', ' ~ time_start ~ ' - ' ~ ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_end-1] ~ ', ' ~ time_end }}
  
            {{ description|trim }}
            {{ instruction|trim if instruction != none }}
  
            Letzte Aktualisierung: {{ update }}
            {% if not loop.last %}***{% endif %}
            {% endfor %}
            {% if current_count not in [0, none] and advance_count not in [0, none] %}***{% endif %}
            {% for i in range(advance_count) %}
              {% set region_name = state_attr(advance, 'region_name') %}
              {% set headline = state_attr(advance, 'warning_' ~ loop.index ~ '_headline') %}
              {% set description = state_attr(advance, 'warning_' ~ loop.index ~ '_description') %}
              {% set instruction = state_attr(advance, 'warning_' ~ loop.index ~ '_instruction') %}
              {% set level = state_attr(advance, 'warning_' ~ loop.index ~ '_level') %}
              {% set color = state_attr(advance, 'warning_' ~ loop.index ~ '_color') %}
              {% set update = (state_attr(advance, 'last_update')|as_timestamp(now()) )|timestamp_custom('%d.%m.%Y, %R', true, 'N/A') %}
              {% set time_start = (state_attr(advance, 'warning_' ~ loop.index ~ '_start')|as_timestamp(now()) )|timestamp_custom('%d.%m.%Y, %R', true, 'N/A') %}
              {% set time_end = (state_attr(advance, 'warning_' ~ loop.index ~ '_end')|as_timestamp(now()) )|timestamp_custom('%d.%m.%Y, %R', true, 'N/A') %}
              {% set weekday_start = (state_attr(advance, 'warning_' ~ loop.index ~ '_start')|as_timestamp(now().timestamp()) )|timestamp_custom('%w', true, now().weekday() )|int %}
              {% set weekday_end = (state_attr(advance, 'warning_' ~ loop.index ~ '_end')|as_timestamp(now().timestamp()) )|timestamp_custom('%w', true, now().weekday() )|int %}
            {{ region_name }}: {{ headline }}
            {{ ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_start-1] ~ ', '
                ~ time_start ~ ' - ' ~ ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_end-1] ~ ', ' ~ time_end }}
  
            {{ description|trim }}
            {{ instruction|trim if instruction != none }}
  
            Letzte Aktualisierung: {{ update }}
            {% if not loop.last %}***{% endif %}
            {% endfor %}
          {% endif %}
















####

What is the definition of warnwetter_stadt_summsumm_current_warning_level and warnwetter_stadt_summsumm_advance_warning_level?

Aslo what about case when one warning change into another warning? In that case “warning_count” will stay the same and you will not trigger notification.

„summsumm“ is my home city and „tralala“ my job city, the corresponding sensors are:

sensor:
  - platform: dwd_weather_warnings
    region_name: 12345678 ## warncellID
    name: Wetter Stadt Tralala

If a state trigger has no to: or from: defined, any change of the sensor incl. change of its attributes will trigger an automation.

But what if weather warning sensor is updated and to_state and from_state are both 1?

Same, see above.

Problem is, it will not be triggered becuse of condition that will not be satisfied.

If for example warning is changed from frost to low temperature. This will be false:
trigger.to_state.attributes.warning_count != trigger.from_state.attributes.warning_count

As count will not be changed, but it is a new warning.

Maybe an edge case - maybe not, in my region it wouldn’t be so tragic either. If weather conditions alternate, this would have to happen within the same second. This has not happened here so far.
If the weather conditions overlap, you have changing warning counts anyway. But if it bothers you, you can delete the condition. Without it, there were too many notifications for me.

Deleting would not be the solution, I am more thinking how to cover that case. Maybe by comparing some other attributes.

You can compare the name of the warning (‘warning_1_name’) when the state has changed and it still counts 1 warning.
So it will only notify you when it is a different warning.

Hm and what if warning name remains same, just start and end time is changed or description?

Compare for the description and the start and end time as well in a condition.

It is a bit tricky as you can have x descriptions, x start and end time…

Why not? If I understand you right, you want to be noticed about every change?

You are right. Will try that out and see if I will be annoyed with too many notifications.

This is my end result. I am sending push notifications to my mobile phone on every change.

alias: "Notify: Weather warning for Tralala"
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.warnwetter_stadt_tralala_current_warning_level
      - sensor.warnwetter_stadt_tralala_advance_warning_level
    id: stadt_tralala
condition:
  - condition: template
    value_template: "{{ trigger.from_state.state != 'unavailable' }}"
action:
  - service: notify.mobile_app_alan_s22
    data:
      message: |-
        {% set current = 'sensor.warnwetter_' ~ trigger.id ~
        '_current_warning_level' %}

        {% set advance = 'sensor.warnwetter_' ~ trigger.id ~
        '_advance_warning_level' %}

        {% set current_count = state_attr(current, 'warning_count') %}
        {% set advance_count = state_attr(advance, 'warning_count') %}

        {% if current_count in [0,none] and advance_count in [0, none] %}
          Die Wetterlage hat sich wieder normalisiert.
        {% else %}
          {% for i in range(current_count) %}
            {% set description = state_attr(current, 'warning_' ~ loop.index ~ '_description') %}
            {% set time_start = (state_attr(current, 'warning_' ~ loop.index ~ '_start')|as_timestamp(now()) )|timestamp_custom('%d.%m.%Y, %R', true, 'N/A') %}
            {% set time_end = (state_attr(current, 'warning_' ~ loop.index ~ '_end')|as_timestamp(now()) )|timestamp_custom('%d.%m.%Y, %R', true, 'N/A') %}
            {% set weekday_start = (state_attr(current, 'warning_' ~ loop.index ~ '_start')|as_timestamp(now().timestamp()) )|timestamp_custom('%w', true, now().weekday() )|int %}
            {% set weekday_end = (state_attr(current, 'warning_' ~ loop.index ~ '_end')|as_timestamp(now().timestamp()) )|timestamp_custom('%w', true, now().weekday() )|int %}
            {{ ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_start-1] ~ ', ' ~ time_start ~ ' - ' ~ ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_end-1] ~ ', ' ~ time_end }} {{ description|trim }}
          {% endfor %}
          {% for i in range(advance_count) %}
            {% set description = state_attr(advance, 'warning_' ~ loop.index ~ '_description') %}
            {% set time_start = (state_attr(advance, 'warning_' ~ loop.index ~ '_start')|as_timestamp(now()) )|timestamp_custom('%d.%m.%Y, %R', true, 'N/A') %}
            {% set time_end = (state_attr(advance, 'warning_' ~ loop.index ~ '_end')|as_timestamp(now()) )|timestamp_custom('%d.%m.%Y, %R', true, 'N/A') %}
            {% set weekday_start = (state_attr(advance, 'warning_' ~ loop.index ~ '_start')|as_timestamp(now().timestamp()) )|timestamp_custom('%w', true, now().weekday() )|int %}
            {% set weekday_end = (state_attr(advance, 'warning_' ~ loop.index ~ '_end')|as_timestamp(now().timestamp()) )|timestamp_custom('%w', true, now().weekday() )|int %}
            {{ ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_start-1] ~ ', ' ~ time_start ~ ' - ' ~ ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_end-1] ~ ', ' ~ time_end }} {{ description|trim }}
          {% endfor %}
        {% endif %}
      title: |-
        {% set current = 'sensor.warnwetter_' ~ trigger.id ~
        '_current_warning_level' %}

        {% set advance = 'sensor.warnwetter_' ~ trigger.id ~
        '_advance_warning_level' %}

        {% set current_count = state_attr(current, 'warning_count') %}
        {% set advance_count = state_attr(advance, 'warning_count') %}

        {% if current_count in [0, none] and advance_count in [0, none] %}
          Entwarnung fĂźr {{ trigger.to_state.attributes.friendly_name }}
        {% else %}
          {% set region_name = state_attr(current, 'region_name') if 'current' in trigger.entity_id else state_attr(advance, 'region_name') %}
          {{ region_name }}: Wetterwarnung ({{ current_count }})
        {% endif %}
      data:
        ttl: 0
        priority: high
mode: parallel
max: 10

The only problem is, I am getting a lot of new lines after description text.

You can solve this by starting your lines with {%- instead of {% . You can try it in the developer tools using the templating section :slight_smile:

I also tried to solve the issue using the following configuration:

- alias: Benachrichtigung fĂźr Wetter-Warnungen
  id: 'notification_weather_warnings'

  trigger:
   - platform: state
     entity_id: 
       - sensor.dwd_<location>_current_warning_level
       - sensor.dwd_<location>_advance_warning_level

  condition:
   - condition: state
     entity_id: input_boolean.disable_notification_automation
     state: 'off'

   - condition: state
     entity_id: input_boolean.disable_weather_warning_automations
     state: 'off'

  action:
    - if:
        - condition: template
          alias: "Anzahl Warnungen prĂźfen"
          value_template: '{{ state_attr(trigger.entity_id, "warning_count")|trim |int >= 1}}'

      then:
        - repeat:
            count: '{{ state_attr(trigger.entity_id, "warning_count")|trim |int }}'
            sequence:
              - if:
                  - condition: or
                    conditions:
                    - condition: template
                      alias: "Änderungen in der Beschreibung prüfen"
                      value_template: '{{ trigger.from_state.attributes["warning_" ~ repeat.index ~ "_description"] |trim != trigger.to_state.attributes["warning_" ~ repeat.index ~ "_description"] |trim }}'

                    - condition: template
                      alias: "Änderungen von Startzeit prüfen"
                      value_template: '{{ trigger.from_state.attributes["warning_" ~ repeat.index ~ "_start"] |trim != trigger.to_state.attributes["warning_" ~ repeat.index ~ "_start"] |trim }}'

                    - condition: template
                      alias: "Änderungen von Endzeit prüfen"
                      value_template: '{{ trigger.from_state.attributes["warning_" ~ repeat.index ~ "_end"] |trim != trigger.to_state.attributes["warning_" ~ repeat.index ~ "_end"] |trim }}'

                then:
                  - service: notify.mobile_app_dominiks_iphone
                    data: 
                      message: >
                        {%- set time_start = state_attr(trigger.entity_id, "warning_" ~ repeat.index ~ "_start") | as_timestamp %}
                        {%- set weekday_start = time_start | timestamp_custom("%w", True) | int %}
                        {%- set time_end = state_attr(trigger.entity_id, "warning_" ~ repeat.index ~ "_end") | as_timestamp %}
                        {%- set weekday_end = time_end | timestamp_custom("%w", True) | int %}
                        {{ state_attr(trigger.entity_id, "warning_" ~ repeat.index ~ "_description")|trim }} ({{ ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_start-1] ~ ", " ~ time_start | timestamp_custom("%H:%M") ~ " - " ~ ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag'][weekday_end-1] ~ ", " ~ time_end | timestamp_custom("%H:%M") }})
                      title: '{{ state_attr(trigger.entity_id, "warning_" ~ repeat.index ~ "_headline")|trim }}'
                      data:
                        subtitle: '{{ state_attr(trigger.entity_id, "region_name")|trim }}'
                        tag: 'notification_weather_warnings_{{ trigger.entity_id }}'
                        group: 'notification_weather_warnings_{{ state_attr(trigger.entity_id, "region_name")|trim }}'

                  - delay: 00:00:20

      else:
           - service: notify.mobile_app_dominiks_iphone
             data:
               message: "clear_notification"
               data:
                 tag: 'notification_weather_warnings_{{ trigger.entity_id }}'

My only issue is, it’s sending the notifications for all warnings but they are not stacked. In case two warnings exist for one location, they are both sent but only one (the last?) notification remains in my iOS notification list. Maybe someone has an idea to solve this?

The advantage of this configuration is, you can easily add/remove entities you want to be alerted for in the trigger section and the rest remains unchanged

Interesting code, will take if/else part. Thank you.

Not sure what you mean. In my case on Android they are stacked and I can see them all.

For 3 conditions that you are using to trigger the change (description, start and end), you can just use one called “warning”, it is a parent node i.e. “warning_1”.

1 Like

Do you know how to clear notifications only once as pushing it to clear notification is also consuming Notification Rate Limit?