UK MET Office Severe Weather Warnings

The Yaml as requested. Posted the last comment too soon sorry.

Same warning as you really :joy: clunky, with room for improvement. They all have been working fine for ages though.

🌨️ Whether warnings as calendars 🌨️ As I said before this could really do with having a way to update events, as currently if a weather warning changes then it will duplicate it. I'm not sure what timescale Met office posts the warnings in, maybe it's just a simple case as looking at future events and re-writing those.

Edit: TIL there is no delete calendar event… so scratch that for now

###############################################################
###### RSS Feedparser sensor for my region
###############################################################
sensor:
- platform: feedparser
  name: "Weather Alerts"
  feed_url: 'https://www.metoffice.gov.uk/public/data/PWSCache/WarningsRSS/Region/yh'
  date_format: '%a, %b %d %I:%M %p'
  inclusions:
    - summary
    - link

###############################################################
###### Template Sensor to check for my location
###############################################################
template:
- sensor:
    - name: "Weather Alerts Yorkshire & Humber"
      state: >
        {{ state_attr('sensor.weather_alerts','entries')
          | map(attribute='summary')
          | select('search', 'North Yorkshire') 
          | list
          | count
        }}

###############################################################
###### Automation to monitor the state of yorkshire and humber and create a calendar event
###############################################################
alias: Create Calendar Event - Weather Alerts
description: ""
triggers:
  - trigger: state
    entity_id:
      - sensor.weather_alerts_yorkshire_humber
    from: null
    to: null
conditions: []
actions:
  - variables:
      alerts: >-
        {% set year = now().year %} {% set ns = namespace(alerts=[]) %} {% if
        state_attr('sensor.weather_alerts','entries') != 0 %}
                    {% for item in state_attr('sensor.weather_alerts','entries') %}
                      {% for type, icon in [('rain', '🌧️'), ('thunderstorms', '⛈️'),
                                            ('wind', '🌪️'), ('snow', '❄️'), ('snow, ice', '❄️ 🧊'),
                                            ('lightning', '🌩️'), ('ice', '🧊'),
                                            ('fog', '🌫️'), ('extreme heat', '🥵'), ('thunderstorm', '⛈️')] if type == item.summary | regex_findall_index('.*warning of (.*) affecting.*', ignorecase=True) %}
                        {% set color = item.summary.split(' ')[0] %}
                        {% set summary = item.summary | regex_findall_index('(.*) affecting Yorkshire & Humber: (.*) valid from (.*) to (.*)', ignorecase=True) %}
                        {% set time_from = as_timestamp(strptime(summary[2], "%H%M %a %d %b")) | timestamp_custom('%m-%d %H:%M:%S') %}
                        {% set time_to = as_timestamp(strptime(summary[3], "%H%M %a %d %b")) | timestamp_custom('%m-%d %H:%M:%S') %}
                        {% set ns.alerts = ns.alerts + [{ "title": icon+" "+summary[0], "message": summary[1], "from": year~"-"~time_from, "to": year~"-"~time_to  }] %}
                      {% endfor %}
                    {% endfor %}
        {% endif %}

        {{ ns.alerts }}
  - repeat:
      for_each: "{{ alerts|list }}"
      sequence:
        - if:
            - condition: template
              value_template: "{{ \"North Yorkshire\" in repeat.item.message }}"
          then:
            - action: script.create_a_calendar_event
              metadata: {}
              data:
                all_day: false
                title: "{{ repeat.item.title }}"
                description: "{{ repeat.item.message }}"
                calendar: calendar.weather_alerts
                start_date: "{{ repeat.item.from }}"
                end_date: "{{ repeat.item.to }}"
mode: single
###############################################################
###### Script I created to make sure a calendar event isn't duplicated, used in automation above
###############################################################
sequence:
  - action: calendar.get_events
    metadata: {}
    data:
      duration:
        hours: 120
        minutes: 0
        seconds: 0
      start_date_time: "{{ (now() - timedelta(days=1) ).strftime('%Y-%m-%dT%H:%M:%S') }}"
    response_variable: events
    target:
      entity_id: calendar.weather_alerts
  - variables:
      does_event_exist: >-
        {% set event_list = events[calendar].events %} {% set ns =
        namespace(name=false, start=false, end=false) %} {% for event in
        event_list %}
          {% if(event.summary == title) %}
            {% set ns.name = true %}
          {% endif %}
          {% if(strptime(event.start[:18], '%Y-%m-%dT%H:%M:%S') == strptime(start_date, '%Y-%m-%d %H:%M:%S') ) %}
            {% set ns.start = true %}
          {% endif %}
          {% if(strptime(event.end[:18], '%Y-%m-%dT%H:%M:%S') == strptime(end_date, '%Y-%m-%d %H:%M:%S') ) %}
            {% set ns.end = true %}
          {% endif %}      
        {% endfor %}

        {{ ns.name and ns.start and ns.end }}
  - if:
      - condition: template
        value_template: "{{ not does_event_exist }}"
    then:
      - action: calendar.create_event
        metadata: {}
        data:
          summary: "{{ title }}"
          description: "{{ description }}"
          start_date_time: "{{ start_date }}"
          end_date_time: "{{ end_date }}"
        target:
          entity_id: "{{ calendar }}"
    alias: If event doesn't exist
fields:
  title:
    selector:
      text: null
    name: Title
    required: true
    description: "The title of the event "
  description:
    selector:
      text: null
    name: Description
    required: true
    description: "The description of the event. "
  calendar:
    selector:
      entity:
        filter:
          domain: calendar
    name: Calendar
    required: true
    description: "Which calendar to add the event too. "
  start_date:
    selector:
      datetime: {}
    name: Start Date
    required: false
    description: "The date and time the event begins. "
  end_date:
    selector:
      datetime: {}
    name: End Date
    required: false
    description: "The date and time the event ends. Must be after the start date. "
  all_day:
    selector:
      boolean: {}
    default: false
    name: All Day Event
    required: true
    description: "Whether the event is all day or not. Default: Off"
alias: Create a Calendar Event If One Doesn't Exist
description: Creates a calendar event

👚 Thin/Thick clothing dry time 👚 This was working "okay" in summer, in winter it's not worked as well not that I get chance to dry the clothes outside much at the minute.

The original script is from this thread: To Line Dry or Not

I use the illuminance integration as I don’t have an outdoor light sensor, so I calculate the sunshine_percent from that which is probably why it fails quite often. Either way it’s been an ok indication of whether I should put the clothes on the line. I also use my rain sensors below too to make a better decision.

I also haven’t gotten around to updating this to make it show hours, currently the calculation comes out in seconds.

###############################################################
###### To line dry or not template sensor. 
###############################################################
template:
  - trigger:
      - platform: time_pattern
        minutes: /6
      - platform: homeassistant
        event: start
    action:
      - service: weather.get_forecasts
        data:
          type: hourly
        target:
          entity_id: weather.###
        response_variable: hourly
    unique_id: weather_forcast
    sensor:
  ## Line Drying
  - trigger:
      - platform: time_pattern
        id: "update"
        minutes: "/6" # repeat every 15 minutes
    sensor:
      - name: Time to line dry a towel
        unique_id: time_to_line_dry_a_towel
        unit_of_measurement: "min"
        device_class: duration
        state_class: "measurement"
        state: >
          {% set clothing_value = 0.25 %}
          {% set illuminance = states('sensor.outdoor_illuminance')|int(0) %}
          {% set sunshine_percent = (illuminance / 150000) * 100 %}
          {% set current_temp = states('sensor.average_outdoor_temperature')|float(0) or states('sensor.###_temperature')|float(0) %}        
          {% set current_humidity = states('sensor.average_outdoor_humidity')|float(0) or states('sensor.###_humidity')|float(0) %}            
          {% set temp = current_temp|round(2) + 273.15 %}
          {% set wind = states('sensor.###_wind_speed')|float(0)|round(2) *  0.2777777778 %}
          {% set humidity = current_humidity|round(2) / 100 %}
          {% set pressure = states('sensor.###_pressure')|float(0) / 10 %}
          {% set rn = 9.4*(sunshine_percent / 100) %}

          {% set delta=((5336/temp)**(21.07-(5336/temp))) %}
          {% set lamda=2.501-0.002361*(temp-273) %}
          {% set y=(0.0016286*pressure)/lamda %}
          {% set ea=1**(21.07-(5336/temp)) %}
          {% set d=(1-humidity)*ea %}
          {% set e=(delta*rn+y*(6.43*(1+0.536*wind)*d))/(lamda*(delta+y)) %}
          {{ ((clothing_value/(e))*1440)|round(2) }}
      - name: Time to line dry a t-shirt
        unique_id: time_to_line_dry_a_t_shirt
        unit_of_measurement: "min"
        device_class: duration
        state_class: "measurement"
        state: >
          {% set clothing_value = 0.075 %}
          {% set illuminance = states('sensor.outdoor_illuminance')|int(0) %}
          {% set sunshine_percent = (illuminance / 150000) * 100 %}
          {% set current_temp = states('sensor.average_outdoor_temperature')|float(0) or states('sensor.###_temperature')|float(0) %}        
          {% set current_humidity = states('sensor.average_outdoor_humidity')|float(0) or states('sensor.###_humidity')|float(0) %}  
          {% set temp = current_temp|round(2) + 273.15 %}
          {% set wind = states('sensor.###_wind_speed')|float(0)|round(2) *  0.2777777778 %}
          {% set humidity = current_humidity|round(2) / 100 %}
          {% set pressure = states('sensor.###_pressure')|float(0) / 10 %}
          {% set rn = 9.4*(sunshine_percent / 100) %}

          {% set delta=((5336/temp)**(21.07-(5336/temp))) %}
          {% set lamda=2.501-0.002361*(temp-273) %}
          {% set y=(0.0016286*pressure)/lamda %}
          {% set ea=1**(21.07-(5336/temp)) %}
          {% set d=(1-humidity)*ea %}
          {% set e=(delta*rn+y*(6.43*(1+0.536*wind)*d))/(lamda*(delta+y)) %}
          {{ ((clothing_value/(e))*1440)|round(2) }}

🌧️ Rain and Temperature Average Forecasts 🌡️ These I quite like to give an average of the future conditions rather than having to look at the forecast. I use them on my home page to tell me weather rain is more than likely continuing or stopping in the next 6/12 hours and similar for temperature, I can check whether it's going to get warmer/cooler.
###############################################################
###### Rain and Temperature Sensor Average Forecasts
###############################################################
template:
  - trigger:
      - platform: time_pattern
        minutes: /6
      - platform: homeassistant
        event: start
    action:
      - service: weather.get_forecasts
        data:
          type: hourly
        target:
          entity_id: ###
        response_variable: hourly
    unique_id: weather_forcast
    sensor:
      - name: Next 6 Hours Chance of Rain
        unique_id: next_6_hours_rain
        unit_of_measurement: "%"
        state_class: "measurement"
        state: >-
          {% set forecast = hourly['###'].forecast %}
          {{ 
            ((
              forecast[5].precipitation_probability | float +
              forecast[4].precipitation_probability | float +
              forecast[3].precipitation_probability | float +
              forecast[2].precipitation_probability | float +
              forecast[1].precipitation_probability | float + 
              forecast[0].precipitation_probability | float) / 6
            )|round(2)
          }}
      - name: Next 12 Hours Chance of Rain
        unique_id: next_12_hours_rain
        unit_of_measurement: "%"
        state_class: "measurement"
        state: >-
          {% set forecast = hourly['###'].forecast %}
          {{ 
            ((           
              forecast[11].precipitation_probability | float +
              forecast[10].precipitation_probability | float +
              forecast[9].precipitation_probability | float +
              forecast[8].precipitation_probability | float +
              forecast[7].precipitation_probability | float + 
              forecast[6].precipitation_probability | float) / 6
            )|round(2)
          }}
      - name: Next 6 Hours Average Temperature Forecast
        unique_id: next_6_hours_temperature
        unit_of_measurement: "°C"
        state_class: "measurement"
        state: >-
          {% set forecast = hourly['###'].forecast %}
          {{ 
            ((
              forecast[5].temperature | float +
              forecast[4].temperature | float +
              forecast[3].temperature | float +
              forecast[2].temperature | float +
              forecast[1].temperature | float + 
              forecast[0].temperature | float) / 6
            )|round(2)
          }}
      - name: Next 12 Hours Average Temperature Forecast
        unique_id: next_12_hours_temperature
        unit_of_measurement: "°C"
        state_class: "measurement"
        state: >-
          {% set forecast = hourly['###'].forecast %}
          {{ 
            ((           
              forecast[11].temperature | float +
              forecast[10].temperature | float +
              forecast[9].temperature | float +
              forecast[8].temperature | float +
              forecast[7].temperature | float + 
              forecast[6].temperature | float) / 6
            )|round(2)
          }}

###############################################################
###### Mushroom Badge to display the temperatures from the template
###### Limits visibility to really cold, or overly hot. 
###############################################################

type: custom:mushroom-template-badge
content: |-
  {% set six = states(entity)|float(0) %}
  {% set twelve = states(entity.replace("6","12"))|float(0) %}


  {% if(six > 18) %}
    {{ six|int(0) }}°C {% if six > twelve %}- Cooling{% else %}- Warming{% endif %}
  {% elif(twelve > 18) %}
    {{ twelve|int(0) }}°C
  {% elif(six < 5) %}
    {{ twelve|int(0) }}°C
  {% elif(twelve < 5) %}
    {{ six|int(0) }}°C {% if six > twelve %}- Cooling{% else %}- Warming{% endif %}
  {% else %}
    Cold
  {% endif %}
icon: mdi:thermometer
color: |-
  {% set six = states(entity)|float(0) %}
  {% set twelve = states(entity.replace("6","12"))|float(0) %}

  {% if(six > 18) %}
    orange
  {% elif(twelve > 18) %}
    red 
  {% elif(six < 5) %}
    cyan 
  {% elif(twelve < 5) %}
    blue 
  {% else %}
    black
  {% endif %}
label: |-
  {% set six = states(entity)|float(0) %}
  {% set twelve = states(entity.replace("6","12"))|float(0) %}


  {% if(six > 18) %}
    6 Hour Temp
  {% elif(twelve > 18) %}
    12 Hour Temp
  {% elif(six < 5) %}
    6 Hour Temp
  {% elif(twelve < 5) %}
    12 Hour Temp
  {% else %}
    Cold
  {% endif %}
tap_action:
  action: navigate
  navigation_path: /lovelace/weather
entity: sensor.next_6_hours_average_temperature_forecast
visibility:
  - condition: or
    conditions:
      - condition: numeric_state
        entity: sensor.next_6_hours_average_temperature_forecast
        above: 18
      - condition: numeric_state
        entity: sensor.next_12_hours_average_temperature_forecast
        above: 18
      - condition: numeric_state
        entity: sensor.next_6_hours_average_temperature_forecast
        below: 5
      - condition: numeric_state
        entity: sensor.next_12_hours_average_temperature_forecast
        below: 5

###############################################################
###### Mushroom Card Template Badges to display the chance of Rain
###### Limits visibility to percentages I've settled at when Rain occured from what pirate weather said.
###############################################################
type: custom:mushroom-template-badge
content: |-
  {% set six = states(entity)|float(0) %}
  {% set twelve = states(entity.replace("6","12"))|float(0) %}


  {% if(six > 20) %}
    {{ six|int(0) }}% {% if six > twelve %}- Passing{% else %}- Continuing{% endif %}
  {% elif(twelve > 20) %}
    {{ twelve|int(0) }}%
  {% else %}
    No Rain
  {% endif %}
icon: mdi:weather-rainy
color: |-
  {% set six = states(entity)|float(0) %}
  {% set twelve = states(entity.replace("6","12"))|float(0) %}

  {% if(six > 20) %}
    cyan
  {% elif(twelve > 20) %}
    lightblue 
  {% else %}
    black
  {% endif %}
label: |-
  {% set six = states(entity)|float(0) %}
  {% set twelve = states(entity.replace("6","12"))|float(0) %}


  {% if(six > 20) %}
   Rain in 6 hours
  {% elif(twelve > 20) %}
    Rain in 12 hours
  {% else %}
    No Rain
  {% endif %}
tap_action:
  action: navigate
  navigation_path: /lovelace/rain
entity: sensor.next_6_hours_chance_of_rain
visibility:
  - condition: or
    conditions:
      - condition: numeric_state
        entity: sensor.next_6_hours_chance_of_rain
        above: 20
      - condition: numeric_state
        entity: sensor.next_12_hours_chance_of_rain
        above: 20


🕰️ Daylights Savings Sensor 🕰️ Original Thread: [Daylight Savings Time](https://community.home-assistant.io/t/daylight-savings-time-dst-template-and-automation/351889)
template:
  - trigger:
      - platform: time
        at: "00:00:00"
      - platform: homeassistant
        event: start
      - platform: event
        event_type: call_service
        event_data:
          domain: template
          service: reload
    sensor:
      - unique_id: dst_next
        name: "Daylight Savings Next"
        device_class: timestamp
        state: >
          {%- macro hms(t) %}
          {%- set dststr = t.dst() | string if t.dst() is not none else "00:00:00" %}
          {%- set h, m, s = dststr.split(':') | map('int') %}
          {{- h * 60 * 60 + m * 60 + s }}   
          {%- endmacro %}

          {%- macro is_dst(t) %}
          {{- hms(t) | int != 0 }}
          {%- endmacro %}

          {%- macro finddst(t, kwarg, rng) %}
          {%- set ns = namespace(previous=is_dst(t), found=none) %}
          {%- for i in range(rng) %}
            {%- set ts = t + timedelta(**{kwarg:i}) %}
            {%- if ns.previous != is_dst(ts) and ns.found is none %}
              {%- set ns.found = i %}
            {%- endif %}
          {%- endfor %}
          {{- ns.found }}
          {%- endmacro %}

          {%- set t = now().replace(hour=0, minute=0, second=0, microsecond=0) %}
          {%- set d = finddst(t, 'days', 366) | int - 1 %}
          {%- set h = finddst(t + timedelta(days=d), 'hours', 25) | int - 1 %}
          {%- set m = finddst(t + timedelta(days=d, hours=h), 'minutes', 61) | int %}
          {{ (t + timedelta(days=d, hours=h, minutes=m)) }}

      - unique_id: dst_phrase
        name: "Daylight Savings Phrase"
        state: >
          {%- macro hms(t) %}
          {%- set dststr = t.dst() | string if t.dst() is not none else "00:00:00" %}
          {%- set h, m, s = dststr.split(':') | map('int') %}
          {{- h * 60 * 60 + m * 60 + s }}   
          {%- endmacro %}

          {%- macro phrase(seconds, name, divisor, mod=None) %}
            {%- set value = ((seconds | abs // divisor) % (mod if mod else divisor)) | int %}
            {%- set end = 's' if value > 1 else '' %}
            {{- '{} {}{}'.format(value, name, end) if value > 0 else '' }}
          {%- endmacro %}

          {%- macro total(seconds) %}
          {%- set values = [ 
            phrase(seconds, 'hour', 60*60, 60*60*24),
            phrase(seconds, 'minute', 60, 60),
            ] | select('!=','') | list %}
          {{- values[:-1] | join(', ') ~ ' and ' ~ values[-1] if values | length > 1 else values | first | default}}
          {%- endmacro %}

          {%- macro finddelta(t, kwarg, rng) %}
          {%- set ns = namespace(previous=hms(t), found=none) %}
          {%- for i in range(rng) %}
            {%- set ts = t + timedelta(**{kwarg:i}) %}
            {%- if ns.previous != hms(ts) and ns.found is none %}
              {%- set before = hms(ts - timedelta(days=1)) | int %}
              {%- set after = hms(ts) | int %}
              {%- set ns.found =  after - before %}
            {%- endif %}
          {%- endfor %}
          {{ ns.found }}
          {%- endmacro %}

          {%- set t = now().replace(hour=0, minute=0, second=0, microsecond=0) %}
          {%- set delta = finddelta(t, 'days', 365) | int %}
          {% if delta > 0 %}
            lose {{ total(delta | abs) }}
          {% else %}
            gain {{ total(delta | abs) }}
          {% endif %}

  - sensor:
      - unique_id: dst_days
        name: "Daylight Savings Days Until"
        unit_of_measurement: days
        state: >
          {% set next = states('sensor.daylight_savings_next') | as_datetime %}
          {% set today = now().replace(hour=0, minute=0, second=0, microsecond=0) %}
          {{ (next - today).days if next is not none else 0 }}
        availability: >
          {{ states('sensor.daylight_savings_next') not in ['unknown', 'unavailable'] }}

Don’t think I’ve missed any. The lightning sensor is just blitzortung integration and the custom card “blitzortung-lightning-card”. The “Outside Rain %” is just something I’m messing around with currently. I’ve got 2 soil moisture sensors sat outside open to the air and when they get wet that percent rises. It was working ok until it decided to get frosty and dewy on a morning :joy:. If you need help with anything let me know. I’ll update if I change how I do the calendar as events, as I think I might clear any future events from the day the event changes and then re-create the new ones. Thinking that might solve the duplicate issue and allow me to keep a history.

1 Like