How to use OpenWeatherMap and get_minute_forecast for accurate hourly precipitation alerts

I spent all weekend fine tuning my OpenWeatherMap integration, just so I can get an accurate, by the minute chance of precipitation notification prior to walking my dogs at lunch. Since I appear to have succeeded, I wanted to share my final outcome, and how I implemented it in HA. Disclaimer: I am very new to Home Assistant, so please bear with me if my code isn’t that great, but it does appear to work!

I had figured out precipitation probability and went over that in another post, but noticed a discrepancy between the chance of rain, and actual rain in the next hour. For example, OWM will say 80% chance of rain, but on the map, “no rain expected in the next hour.” I figured that rain expectation might be based on radar maps of actual precipitation, and would be a much better way to judge whether to venture out into the woods with our dogs.


These are my resulting dashboard widgets, and I have a notification that let’s me know if there’s going to be rain or not using the sensors.

First, I created a text helper called “Precipitation Forecast JSON” with a maximum of 255 characters.

I then built an automation that uses the OWM API to get the minute forecast, then formats the precipitation amounts for the next hour in a condensed version to send to the text helper:

alias: Update Precipitation Forecast JSON
description: ""
triggers:
  - minutes: /5
    trigger: time_pattern
  - event: start
    trigger: homeassistant
actions:
  - target:
      entity_id: weather.openweathermap
    response_variable: minute_forecast_data
    action: openweathermap.get_minute_forecast
    data: {}
  - target:
      entity_id: input_text.minute_forecast_json
    data:
      value: >-
        {% set rain_data =
        minute_forecast_data['weather.openweathermap'].forecast %} {% if
        rain_data is defined and rain_data is not none %}
          {{ (rain_data[:60] | map(attribute='precipitation') | map('float') | map('int') | list | to_json | replace(' ', ''))[:255] }}
        {% else %}
          "[]"
        {% endif %}
    action: input_text.set_value
mode: single

Now that I had an updated text helper with the information formatted in a compressed layout that fit the character limitation, I created a sensor, using moisture dry/wet to use for the notification for my walk. This one was verrrry difficult to figure out, but I finally got this sensor to work:

sensor:
  - platform: template
    sensors:
      rainy_minute_summary:
        friendly_name: "Rainy Minute Summary"
        value_template: >
          {% set data_string = states('input_text.minute_forecast_json') %}
          {% set final_display_state = 'No rain expected' %}
          {% if data_string is string and data_string not in ['unknown', 'unavailable', ''] %}
            {% set values = data_string | from_json | map('float') | list %}
            {% set ns = namespace(rainy_minutes=[]) %}
            {% for i in range(values | length) %}
              {% if values[i] > 0 %}
                {% set ns.rainy_minutes = ns.rainy_minutes + [i] %}
              {% endif %}
            {% endfor %}
            {% if ns.rainy_minutes | length > 0 %}
              {% set final_display_state = 'Rain expected at minutes: ' + (ns.rainy_minutes | join(', ')) %}
            {% endif %}
          {% endif %}
          {{ final_display_state }}
        attribute_templates:
          source: "{{ states('input_text.minute_forecast_json') }}"
          parsed_values: >
            {% set data_string = states('input_text.minute_forecast_json') %}
            {% if data_string is string and data_string not in ['unknown', 'unavailable', ''] %}
              {{ data_string | from_json | map('float') | list }}
            {% else %}
              []
            {% endif %}
          rainy_minutes: >
            {% set data_string = states('input_text.minute_forecast_json') %}
            {% set ns = namespace(rainy_minutes=[]) %}
            {% if data_string is string and data_string not in ['unknown', 'unavailable', ''] %}
              {% set values = data_string | from_json | map('float') | list %}
              {% for i in range(values | length) %}
                {% if values[i] > 0 %}
                  {% set ns.rainy_minutes = ns.rainy_minutes + [i] %}
                {% endif %}
              {% endfor %}
            {% endif %}
            {{ ns.rainy_minutes }}

This lets me know yes/no it will be raining at some point in the next hour, and I could have stopped there, but I was on a roll. This sensor lists the minutes that will be rainy over the next hour:

binary_sensor:
  - platform: template
    sensors:
      precipitation_expected_next_hour:
        friendly_name: "Precipitation Expected Next Hour"
        unique_id: openweathermap_precipitation_expected_next_hour
        device_class: moisture
        icon_template: >
          {% if is_state('binary_sensor.precipitation_expected_next_hour', 'on') %}
            mdi:weather-pouring
          {% else %}
            mdi:water-off
          {% endif %}
        value_template: >
          {% set data_string = states('input_text.minute_forecast_json') %}
          {% set final_state = 'unavailable' %}
          {% if data_string is string and data_string not in ['unknown', 'unavailable', ''] %}
            {% set values = data_string | from_json %}
            {% if values is iterable and values is not string and values is not none %}
              {% set total_precipitation = values | sum %}
              {% set precipitation_detected = total_precipitation > 0 %}
              {% set final_state = 'on' if precipitation_detected else 'off' %}
            {% endif %}
          {% endif %}
          {{ final_state }}
1 Like