UK MET Office Severe Weather Warnings

Looks like the somerset sensor isn’t working as no entity with this name…

Any ideas on what I’ve missed? Thanks

Ok all fixed. Was some syntax errors but mainly I need to start using separate.yaml files to keep it all working.

Thanks guys for making this its super cool!

Glad you got it sorted!

you can set the frequency (it’s optional but not sure what the default is):

sensor:
  platform: feedparser
  scan_interval:
    hours: 1

Although I’m searching the region ‘sw’ I’m also checking the summary for ‘Devon’ - in the same way as posted above for ‘Somerset’

if type == item.summary | regex_findall_index('.*warning of (.*) affecting.*', ignorecase=True) and item.summary | regex_findall('Devon' ignorecase=True) %}

As the current Ice warning for the region sw only applies to Gloucestershire, Swindon and Wiltshire, I was expecting to see the ‘No warnings at present’ message in my alerts popup, as per the code:

{% else %} No warnings at present

However nothing is rendering in my popup at all. I’ve used the same spacing and indentation as per what has been posted above, as far as I can tell. I’ve tried swapping ‘Devon’ for ‘Gloucestershire’, and reloaded my templates, but I’m still not getting the current warning displaying in my popup. Is anyone able to check this with their code and see if the same happens? If not, there must be something not quite right with my code here:

Code
- type: markdown
  content: >
    {% if state_attr('sensor.weather_alerts','entries') != [] %}
      {% for item in state_attr('sensor.weather_alerts','entries') %}
        {% for type, icon in
          [
            ('extreme heat', 'weather-sunny-alert'),
            ('fog', 'weather-fog'),
            ('ice', 'car-traction-control'),
            ('lightning', 'weather-lightning'),
            ('rain', 'weather-pouring'),
            ('rain, wind', 'weather-pouring'),
            ('snow', 'weather-snowy-heavy'),
            ('snow, ice', 'weather-snowy-heavy'),
            ('thunderstorm', 'weather-lightning'),
            ('thunderstorms', 'weather-lightning-rainy'),
            ('wind', 'weather-windy')
          ]
          if type == item.summary | regex_findall_index('.*warning of (.*) affecting.*', ignorecase=True) and item.summary | regex_findall('Devon' ignorecase=True) %}
          {% set color = item.summary.split(' ')[0] %}
          {% set summary = item.summary | regex_findall_index('(.*) affecting South West England: (.*) valid from (.*) to (.*)', ignorecase=True) %}
          {% set link = item.link %}
          {% set time_utc_from = summary[2][0:2] ~':'~ summary[2][2:4] %}
          {% set time_utc_to = summary[3][0:2] ~':'~ summary[3][2:4] %}
          {% set date_from = summary[2][5:8] +'-'+ summary[2][9:11] +'-'+ summary[2][12:] %}
          {% set date_to = summary[3][5:8] +'-'+ summary[3][9:11] +'-'+ summary[3][12:] %}
          {% set time_local_from = ((now().date() ~ ' ' ~ time_utc_from ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}  
          {% set time_local_to = ((now().date() ~ ' ' ~ time_utc_to ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}
    |  |  |

    | -: | -- |

    | <font color = {%- if 'Yellow' == color %}'gold' {%- elif 'Amber' == color %}'darkorange' {%- else %}'firebrick' {%- endif %}>
    <ha-icon icon={{ "'mdi:" + icon + "'" }}>
    </ha-icon></font>
    | <a href='{{ link }}'>**{{ summary[0] }}** </a>
    |

    | from: | **{{ time_local_from }}**, {{ date_from }} |

    | to: | **{{ time_local_to }}**, {{ date_to }} |
        {% endfor %}
    <br>
      {% endfor %}
    {% else %} No warnings at present
    {% endif %}

@Scoff Well spotted. It works when there are no weather alerts at all, but not when there are alerts, just not for your area. Here is the ‘fix’:

Create a new sensor that is specific to Devon:

template: #---------------------------------------------------------------------
  - sensor:
      - name: "Weather Alerts Devon"
        state: >
          {{ state_attr('sensor.weather_alerts','entries')
            | map(attribute='summary')
            | select('search', 'Devon') 
            | list
            | count
          }}

…and then change that line in the card to:

      content: >
        {% if states('sensor.weather_alerts_devon') != '0' %}

Sorry, I didn’t notice this as I use (my version of) sensor.weather_alerts_devon in a conditional card that hides the whole thing when there is no alert.

Please let me know that this works for you.

For anyone else that is using this code, here is the full package (for se/Oxon):

Code
sensor: #-----------------------------------------------------------------------
  - platform: feedparser
    name: "Weather Alerts"
    feed_url: 'http://metoffice.gov.uk/public/data/PWSCache/WarningsRSS/Region/se'
    scan_interval:
      hours: 1
    date_format: '%a, %b %d %I:%M %p'
    inclusions:
      - summary
      - link

template: #---------------------------------------------------------------------
  - sensor:
      - name: "Weather Alerts Oxfordshire"
        state: >
          {{ state_attr('sensor.weather_alerts','entries')
            | map(attribute='summary')
            | select('search', 'Oxfordshire') 
            | list
            | count
          }}

automation: #-------------------------------------------------------------------
  - alias: "Weather: Notify weather warnings"
    id: weather_notify_warnings
    trigger:
      - platform: state
        entity_id: sensor.weather_alerts_oxfordshire
    variables:
      message: |
        {% for item in state_attr('sensor.weather_alerts','entries') %}
          {% for type in [
            ('extreme heat'),('fog'),('ice'),('lightning'),('rain'),('rain, wind'),('snow'),('snow, ice'),('thunderstorm'),('thunderstorms'),('wind')]
            if type == item.summary | regex_findall_index('.*warning of (.*) affecting.*', ignorecase=True) and item.summary | regex_findall('Oxfordshire', ignorecase=True) %}
            {% set summary = item.summary | regex_findall_index('(.*) of (.*) affecting .* valid from (.*) to (.*)', ignorecase=True) %}
            {% set time_utc_from = summary[2][0:2] ~':'~ summary[2][2:4] %}
            {% set time_utc_to = summary[3][0:2] ~':'~ summary[3][2:4] %}
            {% set date_from = summary[2][5:8] +'-'+ summary[2][9:11] +'-'+ summary[2][12:] %}
            {% set date_to = summary[3][5:8] +'-'+ summary[3][9:11] +'-'+ summary[3][12:] %}
            {% set time_local_from = ((now().date() ~ ' ' ~ time_utc_from ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}  
            {% set time_local_to = ((now().date() ~ ' ' ~ time_utc_to ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}
        {{ summary[0] }}
        {{ summary[1] }}
        {{ time_local_from }}➜{{ time_local_to }}
        {{ date_from }}
          {% endfor %}
        {% endfor %}
    condition:
      - "{{ trigger.to_state.state not in ['unknown', 'unavailable','0'] }}"
      - "{{ trigger.from_state.state not in ['unknown', 'unavailable','0'] }}"
    action:
      - service: notify.notify
        data:
          title: "Met Office"
          message: "{{ message }}"
          data:
            tag: weather_alert  # will replace earlier alerts with this tag

…and the card:

type: markdown
content: >
  {% if states('sensor.weather_alerts_oxfordshire') != '0' %}
    {% for item in state_attr('sensor.weather_alerts','entries') %}
      {% for type, icon in
        [
          ('extreme heat', 'weather-sunny-alert'),
          ('fog', 'weather-fog'),
          ('ice', 'car-traction-control'),
          ('lightning', 'weather-lightning'),
          ('rain', 'weather-pouring'),
          ('rain, wind', 'weather-pouring'),
          ('snow', 'weather-snowy-heavy'),
          ('snow, ice', 'weather-snowy-heavy'),
          ('thunderstorm', 'weather-lightning'),
          ('thunderstorms', 'weather-lightning-rainy'),
          ('wind', 'weather-windy')
        ]
        if type == item.summary | regex_findall_index('.*warning of (.*) affecting.*', ignorecase=True) and item.summary | regex_findall('Oxfordshire', ignorecase=True) %}
        {% set color = item.summary.split(' ')[0] %}
        {% set summary = item.summary | regex_findall_index('(.*) affecting London & South East England: (.*) valid from (.*) to (.*)', ignorecase=True) %}
        {% set link = item.link %}
        {% set time_utc_from = summary[2][0:2] ~':'~ summary[2][2:4] %}
        {% set time_utc_to = summary[3][0:2] ~':'~ summary[3][2:4] %}
        {% set date_from = summary[2][5:8] +'-'+ summary[2][9:11] +'-'+ summary[2][12:] %}
        {% set date_to = summary[3][5:8] +'-'+ summary[3][9:11] +'-'+ summary[3][12:] %}
        {% set time_local_from = ((now().date() ~ ' ' ~ time_utc_from ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}  
        {% set time_local_to = ((now().date() ~ ' ' ~ time_utc_to ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}
  |  |  |

  | -: | -- |

  | <font color = {%- if 'Yellow' == color %}'gold' {%- elif 'Amber' == color %}'darkorange' {%- else %}'firebrick' {%- endif %}>
  <ha-icon icon={{ "'mdi:" + icon + "'" }}>
  </ha-icon></font>
  | <a href='{{ link }}'>**{{ summary[0] }}** </a>
  |

  | from: | **{{ time_local_from }}**, {{ date_from }} |

  | to: | **{{ time_local_to }}**, {{ date_to }} |
      {% endfor %}
  <br>
    {% endfor %}
  {% else %} No warnings at present
  {% endif %}

btw, how did you hide your code behind that drop-down arrow? Edit: worked it out - thanks!

1 Like

Ahh I see, thanks very much for that, i’ll get the additional devon specific sensor set up, and amend my card to reference that sensor accordingly. :+1:

This is great, I just had to modified automation a bit as it produces to many empty lines. Also added some emojis :wink:

- alias: "Weather: Notify weather warnings"
  id: weather_notify_warnings
  trigger:
    - platform: state
      entity_id: sensor.weather_alerts_oxfordshire
  variables:
    message: |
      {%- for item in state_attr('sensor.weather_alerts','entries') %}
        {%- for type in [
          ('extreme heat'),('fog'),('ice'),('lightning'),('rain'),('rain, wind'),('snow'),('snow, ice'),('thunderstorm'),('thunderstorms'),('wind')]
          if type == item.summary | regex_findall_index('.*warning of (.*) affecting.*', ignorecase=True) and item.summary | regex_findall('Oxfordshire', ignorecase=True) %}
          {%- set summary = item.summary | regex_findall_index('(.*) of (.*) affecting .* valid from (.*) to (.*)', ignorecase=True) %}
          {%- set time_utc_from = summary[2][0:2] ~':'~ summary[2][2:4] %}
          {%- set time_utc_to = summary[3][0:2] ~':'~ summary[3][2:4] %}
          {%- set date_from = summary[2][5:8] +'-'+ summary[2][9:11] +'-'+ summary[2][12:] %}
          {%- set date_to = summary[3][5:8] +'-'+ summary[3][9:11] +'-'+ summary[3][12:] %}
          {%- set time_local_from = ((now().date() ~ ' ' ~ time_utc_from ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}  
          {%- set time_local_to = ((now().date() ~ ' ' ~ time_utc_to ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}
          <b>⚠️ {{ summary[0] }}</b>
          {{ summary[1] }}
          {{ time_local_from }} ➜ {{ time_local_to }}
          {%- if date_from == date_to %}
            {{ date_from }}
          {%- else %}
            {{ date_from }} ➜ {{ date_to }}
          {%- endif %}
        {%- endfor %}
      {%- endfor %}
  condition:
    - "{{ trigger.to_state.state not in ['unknown', 'unavailable','0'] }}"
    - "{{ trigger.from_state.state not in ['unknown', 'unavailable','0'] }}"
  action:
    - service: notify.notify
      data:
        title: "⛅ Met Office"
        message: "{{ message }}"
        data:
          tag: weather_alert  # will replace earlier alerts with this tag

Interesting - I don’t get any empty lines at all.

Maybe that’s Android issue. BTW empty lines are between each item (when you have at least 2 warnings) so they are initially not well visible but when you put the message part code into the dev tools template, you may see it @jchh

No, I haven’t had extra lines with 2 simultaneous warnings (at least I didn’t notice it), but will double check the next time I get 2 warnings.

Dev tools does not always display exactly the same as in the card.

The card works perfectly fine, it is just a notification on the mobile phone (at least Android)

You can always get rid of extra lines, just use >- instead of |
If you want to force a new line when you do that, you need to have 2 returns then.

1 Like

got it - I don’t have an Android phone.

If anyone would like to easily use warnings data in any card I suggest to add attributes data to your region template sensor like below:

- sensor:
  - name: "Weather Alerts Oxfordshire"
    state: >
      {{ state_attr('sensor.weather_alerts','entries')
        | map(attribute='summary')
        | select('search', 'Manchester') 
        | list
        | count
      }}
    attributes:
      warnings: >-
        {%- set data = namespace(warnings=[]) %}
        {%- for item in state_attr('sensor.weather_alerts','entries') %}
        {%- for type, icon in
            [
              ('extreme heat', 'weather-sunny-alert'),
              ('fog', 'weather-fog'),
              ('ice', 'car-traction-control'),
              ('lightning', 'weather-lightning'),
              ('rain', 'weather-pouring'),
              ('rain, wind', 'weather-pouring'),
              ('snow', 'weather-snowy-heavy'),
              ('snow, ice', 'weather-snowy-heavy'),
              ('thunderstorm', 'weather-lightning'),
              ('thunderstorms', 'weather-lightning-rainy'),
              ('wind', 'weather-windy')
            ]
          if type == item.summary | regex_findall_index('.*warning of (.*) affecting.*', ignorecase=True) and item.summary | regex_findall('Oxfordshire', ignorecase=True) %}
          {%- set summary = item.summary | regex_findall_index('(.*) of (.*) affecting .* valid from (.*) to (.*)', ignorecase=True) %}
          {%- set color = item.summary.split(' ')[0] %}
          {%- set time_utc_from = summary[2][0:2] ~':'~ summary[2][2:4] %}
          {%- set time_utc_to = summary[3][0:2] ~':'~ summary[3][2:4] %}
          {%- set date_from = summary[2][5:8] +'-'+ summary[2][9:11] +'-'+ summary[2][12:] %}
          {%- set date_to = summary[3][5:8] +'-'+ summary[3][9:11] +'-'+ summary[3][12:] %}
          {%- set time_local_from = ((now().date() ~ ' ' ~ time_utc_from ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}  
          {%- set time_local_to = ((now().date() ~ ' ' ~ time_utc_to ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}

          {%- set data.warnings = data.warnings + [
            {
              "summary": summary[0],
              "type": type,
              "color": color,
              "time_utc_from": time_utc_from,
              "time_utc_to": time_utc_to,
              "time_local_from": time_local_from,
              "time_local_to": time_local_to,
              "date_from": date_from,
              "date_to": date_to,
              "url": item.link,
              "icon": "mdi:" + icon
            }
          ]
          %}
        {%- endfor %}
        {%- endfor %}
        {{ data.warnings }}

You will get additional list of warnings in sensor attributes with easily accesible data :slight_smile:

Thanks for providing this. I’ve just set up a notification automation for my weather alerts. Do you know if It’s possible to include the link URL within the notification, so that you can click on it and go to the specific weather warning on the MET Office website? I can do this successfully on my dashboard using a markdown card for the weather alert, by using:

{% set link = item.link %}

and then

<a href="{{ link }}" target="_blank">

however it doesn’t look like you can template the link in the notify notification in the same way?

Yes, you can.

Here is my version. Just check the link part out.

- type: markdown
  content: >
    {% if states('sensor.weather_alerts_oxfordshire') != '0' %}
      {% for item in state_attr('sensor.weather_alerts','entries') %}
        {% for type, icon in
          [
            ('extreme heat', 'weather-sunny-alert'),
            ('fog', 'weather-fog'),
            ('ice', 'car-traction-control'),
            ('lightning', 'weather-lightning'),
            ('rain', 'weather-pouring'),
            ('rain, wind', 'weather-pouring'),
            ('snow', 'weather-snowy-heavy'),
            ('snow, ice', 'weather-snowy-heavy'),
            ('thunderstorm', 'weather-lightning'),
            ('thunderstorms', 'weather-lightning-rainy'),
            ('wind', 'weather-windy')
          ]
          if type == item.summary | regex_findall_index('.*warning of (.*) affecting.*', ignorecase=True) and item.summary | regex_findall('Oxfordshire', ignorecase=True) %}
          {% set color = item.summary.split(' ')[0] %}
          {% set summary = item.summary | regex_findall_index('(.*) affecting London & South East England: (.*) valid from (.*) to (.*)', ignorecase=True) %}
          {% set link = item.link %}
          {% set time_utc_from = summary[2][0:2] ~':'~ summary[2][2:4] %}
          {% set time_utc_to = summary[3][0:2] ~':'~ summary[3][2:4] %}
          {% set date_from = summary[2][5:8] +'-'+ summary[2][9:11] +'-'+ summary[2][12:] %}
          {% set date_to = summary[3][5:8] +'-'+ summary[3][9:11] +'-'+ summary[3][12:] %}
          {% set time_local_from = ((now().date() ~ ' ' ~ time_utc_from ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}  
          {% set time_local_to = ((now().date() ~ ' ' ~ time_utc_to ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}
    |  |  |

    | -: | -- |

    | <font color = {%- if 'Yellow' == color %}'gold' {%- elif 'Amber' == color %}'darkorange' {%- else %}'firebrick' {%- endif %}>
    <ha-icon icon={{ "'mdi:" + icon + "'" }}>
    </ha-icon></font>
    | <a href='{{ link }}'>**{{ summary[0] }}** </a>
    |

    | from: | **{{ time_local_from }}**, {{ date_from }} |

    | to: | **{{ time_local_to }}**, {{ date_to }} |
        {% endfor %}
    <br>
      {% endfor %}
    {% else %} No warnings at present
    {% endif %}

and the sensors. Note the feedparser one include the link…

sensor: #-----------------------------------------------------------------------
  - platform: feedparser
    name: "Weather Alerts"
    feed_url: 'http://metoffice.gov.uk/public/data/PWSCache/WarningsRSS/Region/se'
    scan_interval:
      hours: 1
    date_format: '%a, %b %d %I:%M %p'
    inclusions:
      - summary
      - link


template: #---------------------------------------------------------------------
  - sensor:
      - name: "Weather Alerts Oxfordshire" # for conditional card & markdown card as weather_alerts can be !=0 but have no Oxon content
        state: >
          {{ state_attr('sensor.weather_alerts','entries')
            | map(attribute='summary')
            | select('search', 'Oxfordshire') 
            | list
            | count
          }}

Thanks @jchh - Can you show me how you are referencing the link within your notification automation please?

ugh - my mistake - I don’t currently have the url in my notification - sorry. Here it is anyway.

automation:
  - alias: "Weather: Notify weather warnings"
    id: weather_notify_warnings
    trigger:
      - platform: state
        entity_id: sensor.weather_alerts_oxfordshire
    variables:
      message: |
        {% for item in state_attr('sensor.weather_alerts','entries') %}
          {% for type in [
            ('extreme heat'),('fog'),('ice'),('lightning'),('rain'),('rain, wind'),('snow'),('snow, ice'),('thunderstorm'),('thunderstorms'),('wind')]
            if type == item.summary | regex_findall_index('.*warning of (.*) affecting.*', ignorecase=True) and item.summary | regex_findall('Oxfordshire', ignorecase=True) %}
            {% set summary = item.summary | regex_findall_index('(.*) of (.*) affecting .* valid from (.*) to (.*)', ignorecase=True) %}
            {% set time_utc_from = summary[2][0:2] ~':'~ summary[2][2:4] %}
            {% set time_utc_to = summary[3][0:2] ~':'~ summary[3][2:4] %}
            {% set date_from = summary[2][5:8] +'-'+ summary[2][9:11] +'-'+ summary[2][12:] %}
            {% set date_to = summary[3][5:8] +'-'+ summary[3][9:11] +'-'+ summary[3][12:] %}
            {% set time_local_from = ((now().date() ~ ' ' ~ time_utc_from ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}
            {% set time_local_to = ((now().date() ~ ' ' ~ time_utc_to ~ "+00:00") | as_datetime | as_local).strftime('%H:%M') %}
        {{ summary[0] }}
        {{ summary[1] }}
        {{ time_local_from }}➜{{ time_local_to }}
        {{ date_from }}
          {% endfor %}
        {% endfor %}
    condition:
      - "{{ trigger.to_state.state not in ['unknown', 'unavailable','0'] }}"
      - "{{ trigger.from_state.state not in ['unknown', 'unavailable','0'] }}"
    action:
      - service: notify.notify
        data:
          title: "Met Office"
          message: "{{ message }}"
          data:
            tag: weather_alert  # will replace earlier alerts with this tag

:slight_smile: No problem. I’ll keep searching to see if there is a way to include the clickable url. I can include the non-clickable plain text url by referencing {{ link }} within the message, but not sure how to make it clickable. I tried an actionable notification instead, however targetting the relevant URL from the alert attribute is proving difficult, but I’m sure there will be a way somehow…