Template weather provider from UK Met Office DataHub API

The UK Met Office publishes its weather forecasts via two APIs: DataPoint (old, with a deprecation timeline) and DataHub (new, still being developed).

The official Met Office HA integration uses DataPoint. Due to a Python version incompatibility between the DataPoint libraries and HA’s requirements, the official integration was disabled for 2024.2 (although re-enabled in the .2 point release), so with help from others pointing out DataHub and the Template Weather Provider integration, I got a free DataHub API key and cobbled together a DataHub-based weather entity. Here are all the bits (click each section to expand):

First, three RESTful sensors to pull in the hourly, three-hourly and daily forecasts. I use the RESTFul integration as opposed to the sensor platform, so these are under the rest: config
- resource: https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/hourly?datasource=BD1&includeLocationName=true&latitude=[MY_LATITUDE]&longitude=[MY_LONGITUDE]&excludeParameterMetadata=true
  headers:
    apikey: "MY_VERY_LONG_API_KEY"
  scan_interval: 3600
  sensor:
    - name: Local Datahub Hourly
      value_template: "{{ value_json['features'][0]['properties']['modelRunDate'] }}"
      json_attributes_path: $.features.0.properties
      json_attributes:
        - timeSeries

- resource: https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/three-hourly?datasource=BD1&includeLocationName=true&latitude=[MY_LATITUDE]&longitude=[MY_LONGITUDE]&excludeParameterMetadata=true
  headers:
    apikey: "MY_VERY_LONG_API_KEY"
  scan_interval: 3600
  sensor:
    - name: Local Datahub 3 Hourly
      value_template: "{{ value_json['features'][0]['properties']['modelRunDate'] }}"
      json_attributes_path: $.features.0.properties
      json_attributes:
        - timeSeries

- resource: https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/daily?datasource=BD1&includeLocationName=true&latitude=[MY_LATITUDE]&longitude=[MY_LONGITUDE]&excludeParameterMetadata=true
  headers:
    apikey: "MY_VERY_LONG_API_KEY"
  scan_interval: 3600
  sensor:
    - name: Local Datahub Daily
      value_template: "{{ value_json['features'][0]['properties']['modelRunDate'] }}"
      json_attributes_path: $.features.0.properties
      json_attributes:
        - timeSeries
Next, a macro (reusable template) in custom_templates/met_office_codes.jinja to convert DataHub's "significant weather codes" into the weather conditions HA is expecting
{% macro code2ha(x) -%}
{{ {
   0: "clear-night",
   1: "sunny",
   2: "partlycloudy",
   3: "partlycloudy",
   4: "Not used",
   5: "fog",
   6: "fog",
   7: "cloudy",
   8: "cloudy",
   9: "rainy",
   10: "rainy",
   11: "rainy",
   12: "rainy",
   13: "pouring",
   14: "pouring",
   15: "pouring",
   16: "snowy-rainy",
   17: "snowy-rainy",
   18: "snowy-rainy",
   19: "hail",
   20: "hail",
   21: "hail",
   22: "snowy",
   23: "snowy",
   24: "snowy",
   25: "snowy",
   26: "snowy",
   27: "snowy",
   28: "lightning-rainy",
   29: "lightning-rainy",
   30: "lightning"}.get(x, "n/a") -}}
{% endmacro %}
Another macro in custom_templates/direction.jinja to generate wind direction compass descriptions from bearing degrees, written to be flexible in the number of points and outputs — I use it elsewhere to generate a set of eight arrow symbols
{% macro dir(bearing=0,tokens=('N','NNE','NE','ENE','E','ESE','SE','SSE','S','SSW','SW','WSW','W','WNW','NW','NNW'),range=360,centre=True) -%}
{% set ns=namespace(tokens=tokens) -%}
{% if centre -%}
{% set ns.tokens = [tokens[0]] -%}
{% for t in tokens[1:] -%}
{% set ns.tokens = ns.tokens + [t] + [t] -%}
{% endfor -%}
{% set ns.tokens = ns.tokens + [tokens[0]] -%}
{% endif -%}
{% set divs = ns.tokens|count -%}
{% set bclip = ((0,bearing,range-1)|sort)[1] -%}
{{ ns.tokens[(bclip * divs // range)|int] -}}
{% endmacro -%}
Some template sensors (under template: config) to pull the current condition from the forecast.

The forecast data often extends a couple of hours into the past, so the current_weather pulls out the data point closest to the current time. Its state is the index in the forecast list of that data point, referred to in its attribute.

- sensor:
    - name: Local current weather
      state: >
        {% set tsl = state_attr('sensor.local_datahub_hourly','timeSeries')|map(attribute='time')|map('as_timestamp')|list %}
        {% set ts = tsl|select('>=',(now()-timedelta(minutes=30))|as_timestamp())|first %}
        {{ tsl.index(ts) }}
      attributes:
        status: "{{ state_attr('sensor.local_datahub_hourly','timeSeries')[this.state|int(0)] }}"
      availability: "{{ states('sensor.local_datahub_hourly') not in ('unavailable', 'unknown') }}"

- sensor:
    - name: Local weather condition
      state: >
        {% from 'met_office_codes.jinja' import code2ha %}
        {{ code2ha(state_attr('sensor.local_current_weather','status')['significantWeatherCode']) }}
      attributes:
        timestamp: "{{ state_attr('sensor.local_current_weather','status')['time'] }}"
      availability: "{{ states('sensor.local_current_weather') not in ('unavailable', 'unknown') }}"

- sensor:
    - name: Local wind bearing
      state: >
        {% from 'direction.jinja' import dir %}
        {{ dir(state_attr('sensor.local_current_weather','status')['windDirectionFrom10m']) }}
      attributes:
        timestamp: "{{ state_attr('sensor.local_current_weather','status')['time'] }}"
      availability: "{{ states('sensor.local_current_weather') not in ('unavailable', 'unknown') }}"
The big one: the template weather entity.

This is under the weather: config, and I’ve put it in weather.yaml with an !include in the main config. Once it’s working, any changes can be pulled in with a reload of template entities.

I use my own outside temperature sensor for the current temperature. If you want to use the supplied data, use this in place of my line:

temperature_template: "{{ state_attr('sensor.local_current_weather','status')['screenTemperature'] }}"

The entity expects a twice-daily (day/night) forecast, which I generate from the three-hourly by taking the 00:00 and 12:00 points, and additionally finding the min and max temperature, average pressure, windspeed etc from the surrounding points.

Units are set to match the incoming data, and HA will display as per your settings. Most of the work here is mapping the DataHub keys to the appropriate weather entity keys. All forecasts are filtered to include future points only.

There is a lot of checking for the existence of the template sensors above to prevent errors on startup due to the order of evaluation.

- platform: template
  name: "Met Office Datahub"
  unique_id: met_office_datahub
  condition_template: "{{ states('sensor.local_weather_condition') }}"
  temperature_template: "{{ states('sensor.outside_temperature')|float(0) }}"
  apparent_temperature_template: >
    {% if state_attr('sensor.local_current_weather','status') is mapping %}
      {{ state_attr('sensor.local_current_weather','status').get('feelsLikeTemperature', 0) }}
    {% else %}
      0
    {% endif %}
  temperature_unit: "°C"
  humidity_template: >
    {% if state_attr('sensor.local_current_weather','status') is mapping %}
      {{ state_attr('sensor.local_current_weather','status').get('screenRelativeHumidity', 0) }}
    {% else %}
      0
    {% endif %}
  attribution_template: "Met Office DataHub plus local"
  pressure_template: >
    {% if state_attr('sensor.local_current_weather','status') is mapping %}
      {{ state_attr('sensor.local_current_weather','status').get('mslp', 0) }}
    {% else %}
      0
    {% endif %}
  pressure_unit: "Pa"
  visibility_template: >
    {% if state_attr('sensor.local_current_weather','status') is mapping %}
      {{ state_attr('sensor.local_current_weather','status').get('visibility', 0) }}
    {% else %}
      0
    {% endif %}
  visibility_unit: "m"
  wind_speed_template: >
    {% if state_attr('sensor.local_current_weather','status') is mapping %}
      {{ state_attr('sensor.local_current_weather','status').get('windSpeed10m', 0) }}
    {% else %}
      0
    {% endif %}
  wind_speed_unit: "m/s"
  wind_bearing_template: "{{ states('sensor.local_wind_bearing') }}"
  forecast_hourly_template: >
    {% from 'met_office_codes.jinja' import code2ha %}
    {% from 'direction.jinja' import dir %}
    {% set dh = state_attr('sensor.local_datahub_hourly','timeSeries') %}
    {% set ns = namespace(forecast=[]) %}
    {% for ts in dh -%}
      {% if ts['time']|as_datetime > now() %}
        {% set tsd = { 'datetime': (ts['time']|as_datetime).isoformat(),
                       'condition': code2ha(ts['significantWeatherCode']),
                       'precipitation_probability': ts['probOfPrecipitation'],
                       'wind_bearing': dir(ts['windDirectionFrom10m']),
                       'humidity': ts['screenRelativeHumidity'],
                       'pressure': ts['mslp'],
                       'uv_index': ts['uvIndex'],
                       'temperature': ts['screenTemperature']|round(0),
                       'apparent_temperature': ts['feelsLikeTemperature']|round(0),
                       'wind_speed': ts['windSpeed10m']|round(0) } -%}
        {% set ns.forecast = ns.forecast + [tsd] -%}
      {% endif %}
    {% endfor %}
    {{ ns.forecast }}
  forecast_twice_daily_template: >
    {% from 'met_office_codes.jinja' import code2ha %}
    {% set dh = state_attr('sensor.local_datahub_3_hourly','timeSeries') %}
    {% set ns = namespace(forecast=[]) %}
    {% for ts in dh -%}
      {% if 'T00' in ts['time'] or 'T12' in ts['time'] and ts['time']|as_datetime > now() -%}
        {% set index = (dh|map(attribute='time')|list).index(ts['time']) -%}
        {% set dhr = dh[((0,index-1,dh|count)|sort)[1]:((0,index+2,dh|count)|sort)[1]] %}
        {% set tsd = { 'datetime': (ts['time']|as_datetime).isoformat(),
                       'is_daytime': 'T12' in ts['time'],
                       'condition': code2ha(ts['significantWeatherCode']),
                       'humidity': dhr|map(attribute='screenRelativeHumidity')|average|round(0),
                       'pressure': dhr|map(attribute='mslp')|average|round(0),
                       'temperature': dhr|map(attribute='maxScreenAirTemp')|max|round(0),
                       'templow': dhr|map(attribute='minScreenAirTemp')|min|round(0),
                       'precipitation_probability': dhr|map(attribute='probOfPrecipitation')|average|round(0),
                       'wind_speed': dhr|map(attribute='windSpeed10m')|average|round(0) } -%}
        {% set ns.forecast = ns.forecast + [tsd] -%}
      {% endif -%}
    {% endfor %}
    {{ ns.forecast }}
  forecast_daily_template: >
    {% from 'met_office_codes.jinja' import code2ha %}
    {% set dh = state_attr('sensor.local_datahub_daily','timeSeries') %}
    {% set ns = namespace(forecast=[]) %}
    {% for ts in dh -%}
      {% if ts['time']|as_datetime > now() %}
        {% set tsd = { 'datetime': (ts['time']|as_datetime).isoformat(),
                       'condition': code2ha(ts.get('daySignificantWeatherCode', ts.get('nightSignificantWeatherCode', 'n/a'))),
                       'humidity': ts['middayRelativeHumidity'],
                       'pressure': ts['middayMslp'],
                       'temperature': ts['dayMaxScreenTemperature']|round(0),
                       'templow': ts['nightMinScreenTemperature']|round(0),
                       'wind_speed': ts['midday10MWindSpeed']|round(0) } -%}
        {% set ns.forecast = ns.forecast + [tsd] -%}
      {% endif %}
    {% endfor %}
    {{ ns.forecast }}

The finished result — click the cog icon to set the units you prefer:

Obviously, the long-term ideal would be to have a proper weather integration using DataHub. Hopefully someone will conjure one up so I don’t have to learn how to write integrations :smiley:

12 Likes

nicely done!

Bravo @Troon!. Now I need a few days to figure out my weather sources and update

Thanks @Troon.

One thing for others who are going to play with this. The API key can be tricky, I had trouble with the 55 day trial, but the free subscription tier worked out.

Hi there @Troon

I’m not getting data into the Local Datahub sensors from the Met Office, I have tried both the API Key for the Atmospheric (Free Trail 1GB) and the Site Specific (Free 55 calls) and HA is showing Unknown as a status.

I may be missing something very stupid in the entry of Lat/Long (I removed the [ ]) in the config.yaml, or in the API Key (I kept the " ").

Below is the config.yaml entry with a slightly desensitised Lat/Long and a bit of the API Key cut out :

rest:
- resource: https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/hourly?datasource=BD1&includeLocationName=true&latitude=51.58000000000000&longitude=0.00022000000000000000&excludeParameterMetadata=true
  headers:
    apikey: "eyJ4NXQiOiJOak16WWpreVlUZGlZVGM0TUdSalpEaGtaV1psWWpjME5UTXhORFV4TlRZM1ptRTRZV1JrWWc9PSIsImtpZCI6ImdhdGV3YXlfY2VydGlmaWNhdGVfYWxpYXMiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzaW1vbkBzdHdpbGxpYW1zLmNvLnVrQGNhcmJvbi5zdXBlciIsImFwcGxpY2F0aW9uIjp7Im93bmVyIjoic2ltb25Ac3R3aWxsaWFtcy5jby51ayIsInRpZXJRdW90YVR5cGUiOm51bGwsInRpZXIiOiJVbmxpbWl0ZWQiLCJuYW1lIjoiYnBmX2ZlYXR1cmUtOTMyZmE0ZDYtNmQ51ldG9mZmljZS5jbG91ZDo0NDNcL29hdXRoMlwvdG9rZW4iLCJ0aWVySW5mbyI6eyJ3ZGhfYnBmXzU1Ijp7InRpZXJRdW90YVR5cGUiOiJyZXF1ZXN0Q291bnQiLCJncmFwaFFMTWF4Q29tcGxleGl0eSI6MCwiZ3JhcGhRTE1heERlcHRoIjowLCJzdG9wT25RdW90YVJlYWNoIjp0cnVlLCJzcGlrZUFycmVzdExpbWl0IjowLCJzcGlrZUFycmVzdFVuaXQiOiJzZWMifX0sImtleXR5cGUiOiJQUk9EVUNUSU9OIiwic3Vic2NyaWJlZEFQSXMiOlt7InN1YnNjcmliZXJUZW5hbnREb21haW4iOiJjYXJib24uc3VwZXIiLCJuYW1lIjoiTU9TaXRlU3BlY2lmaWNCbGVuZGVkUHJvYmFiaWxpc3RpY0ZvcmVjYXN0IiwiY29udGV4dCI6IlwvbW8tc2l0ZS1zcGVjaWZpYy1ibGVuZGVkLXByb2JhYmlsaXN0aWMtZm9yZWNhc3RcLzEuMC4wIiwicHVibGlzaGVyIjoiV0RIX0NJIiwidmVyc2lvbiI6IjEuMC4wIiwic3Vic2NyaXB0aW9uVGllciI6IndkaF9icGZfNTUifV0sInRva2VuX3R5cGUiOiJhcGlLZXkiLCJpYXQiOjE3MTU2MDUyMzksImp0aSI6IjIzMGQyZTFmLTdjNWItNDI2Ny04MGEzLWFjNmQxOTdmNDc0YSJ9.TKkt6RxLaPXtwR8gYWG0jJUqdeQnOwq0dvn-qJr5IAlXvvir4aChsV3cpiQMaCTvD3a8D2h8ti7Lr9tMpdBKD11cF1qX-aOVfblrAUFnAY4E4AjQXTc5w2fjYxCrSO-HvTSFuLK7iXkLtFzdXhpPuzn1sfpsGB9E0ZpAEUrvz2nEkrErsqb1RCP0HmtK-5u06LMVd8ijR8SoxCe14IkWHoDSk3BOADOxphrg9iMnsYUtDQ0U9oBOwvXeQZVx6zHiN4VAJIMkhhu2S-zPBZfB0uIRKiu7adthicX2CwWesukKADp8ujSK48h-iWbeJHosrUsRnZko3ZCN3QAAIug3Gg=="
  scan_interval: 3600
  sensor:
    - name: Local Datahub Hourly
      value_template: "{{ value_json['features'][0]['properties']['modelRunDate'] }}"
      json_attributes_path: $.features.0.properties
      json_attributes:
        - timeSeries

- resource: https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/three-hourly?datasource=BD1&includeLocationName=true&latitude=51.58000000000000&longitude=0.00022000000000000000&excludeParameterMetadata=true
  headers:
    apikey: "eyJ4NXQiOiJOak16WWpreVlUZGlZVGM0TUdSalpEaGtaV1psWWpjME5UTXhORFV4TlRZM1ptRTRZV1JrWWc9PSIsImtpZCI6ImdhdGV3YXlfY2VydGlmaWNhdGVfYWxpYXMiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzaW1vbkBzdHdpbGxpYW1zLmNvLnVrQGNhcmJvbi5zdXBlciIsImFwcGxpY2F0aW9uIjp7Im93bmVyIjoic2ltb25Ac3R3aWxsaWFtcy5jby51ayIsInRpZXJRdW90YVR5cGUiOm51bGwsInRpZXIiOiJVbmxpbWl0ZWQiLCJuYW1lIjoiYnBmX2ZlYXR1cmUtOTMyZmE0ZDYtNmQ51ldG9mZmljZS5jbG91ZDo0NDNcL29hdXRoMlwvdG9rZW4iLCJ0aWVySW5mbyI6eyJ3ZGhfYnBmXzU1Ijp7InRpZXJRdW90YVR5cGUiOiJyZXF1ZXN0Q291bnQiLCJncmFwaFFMTWF4Q29tcGxleGl0eSI6MCwiZ3JhcGhRTE1heERlcHRoIjowLCJzdG9wT25RdW90YVJlYWNoIjp0cnVlLCJzcGlrZUFycmVzdExpbWl0IjowLCJzcGlrZUFycmVzdFVuaXQiOiJzZWMifX0sImtleXR5cGUiOiJQUk9EVUNUSU9OIiwic3Vic2NyaWJlZEFQSXMiOlt7InN1YnNjcmliZXJUZW5hbnREb21haW4iOiJjYXJib24uc3VwZXIiLCJuYW1lIjoiTU9TaXRlU3BlY2lmaWNCbGVuZGVkUHJvYmFiaWxpc3RpY0ZvcmVjYXN0IiwiY29udGV4dCI6IlwvbW8tc2l0ZS1zcGVjaWZpYy1ibGVuZGVkLXByb2JhYmlsaXN0aWMtZm9yZWNhc3RcLzEuMC4wIiwicHVibGlzaGVyIjoiV0RIX0NJIiwidmVyc2lvbiI6IjEuMC4wIiwic3Vic2NyaXB0aW9uVGllciI6IndkaF9icGZfNTUifV0sInRva2VuX3R5cGUiOiJhcGlLZXkiLCJpYXQiOjE3MTU2MDUyMzksImp0aSI6IjIzMGQyZTFmLTdjNWItNDI2Ny04MGEzLWFjNmQxOTdmNDc0YSJ9.TKkt6RxLaPXtwR8gYWG0jJUqdeQnOwq0dvn-qJr5IAlXvvir4aChsV3cpiQMaCTvD3a8D2h8ti7Lr9tMpdBKD11cF1qX-aOVfblrAUFnAY4E4AjQXTc5w2fjYxCrSO-HvTSFuLK7iXkLtFzdXhpPuzn1sfpsGB9E0ZpAEUrvz2nEkrErsqb1RCP0HmtK-5u06LMVd8ijR8SoxCe14IkWHoDSk3BOADOxphrg9iMnsYUtDQ0U9oBOwvXeQZVx6zHiN4VAJIMkhhu2S-zPBZfB0uIRKiu7adthicX2CwWesukKADp8ujSK48h-iWbeJHosrUsRnZko3ZCN3QAAIug3Gg=="
  scan_interval: 3600
  sensor:
    - name: Local Datahub 3 Hourly
      value_template: "{{ value_json['features'][0]['properties']['modelRunDate'] }}"
      json_attributes_path: $.features.0.properties
      json_attributes:
        - timeSeries

- resource: https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/daily?datasource=BD1&includeLocationName=true&latitude=51.58000000000000&longitude=0.00022000000000000000&excludeParameterMetadata=true
  headers:
    apikey: "eyJ4NXQiOiJOak16WWpreVlUZGlZVGM0TUdSalpEaGtaV1psWWpjME5UTXhORFV4TlRZM1ptRTRZV1JrWWc9PSIsImtpZCI6ImdhdGV3YXlfY2VydGlmaWNhdGVfYWxpYXMiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzaW1vbkBzdHdpbGxpYW1zLmNvLnVrQGNhcmJvbi5zdXBlciIsImFwcGxpY2F0aW9uIjp7Im93bmVyIjoic2ltb25Ac3R3aWxsaWFtcy5jby51ayIsInRpZXJRdW90YVR5cGUiOm51bGwsInRpZXIiOiJVbmxpbWl0ZWQiLCJuYW1lIjoiYnBmX2ZlYXR1cmUtOTMyZmE0ZDYtNmQ51ldG9mZmljZS5jbG91ZDo0NDNcL29hdXRoMlwvdG9rZW4iLCJ0aWVySW5mbyI6eyJ3ZGhfYnBmXzU1Ijp7InRpZXJRdW90YVR5cGUiOiJyZXF1ZXN0Q291bnQiLCJncmFwaFFMTWF4Q29tcGxleGl0eSI6MCwiZ3JhcGhRTE1heERlcHRoIjowLCJzdG9wT25RdW90YVJlYWNoIjp0cnVlLCJzcGlrZUFycmVzdExpbWl0IjowLCJzcGlrZUFycmVzdFVuaXQiOiJzZWMifX0sImtleXR5cGUiOiJQUk9EVUNUSU9OIiwic3Vic2NyaWJlZEFQSXMiOlt7InN1YnNjcmliZXJUZW5hbnREb21haW4iOiJjYXJib24uc3VwZXIiLCJuYW1lIjoiTU9TaXRlU3BlY2lmaWNCbGVuZGVkUHJvYmFiaWxpc3RpY0ZvcmVjYXN0IiwiY29udGV4dCI6IlwvbW8tc2l0ZS1zcGVjaWZpYy1ibGVuZGVkLXByb2JhYmlsaXN0aWMtZm9yZWNhc3RcLzEuMC4wIiwicHVibGlzaGVyIjoiV0RIX0NJIiwidmVyc2lvbiI6IjEuMC4wIiwic3Vic2NyaXB0aW9uVGllciI6IndkaF9icGZfNTUifV0sInRva2VuX3R5cGUiOiJhcGlLZXkiLCJpYXQiOjE3MTU2MDUyMzksImp0aSI6IjIzMGQyZTFmLTdjNWItNDI2Ny04MGEzLWFjNmQxOTdmNDc0YSJ9.TKkt6RxLaPXtwR8gYWG0jJUqdeQnOwq0dvn-qJr5IAlXvvir4aChsV3cpiQMaCTvD3a8D2h8ti7Lr9tMpdBKD11cF1qX-aOVfblrAUFnAY4E4AjQXTc5w2fjYxCrSO-HvTSFuLK7iXkLtFzdXhpPuzn1sfpsGB9E0ZpAEUrvz2nEkrErsqb1RCP0HmtK-5u06LMVd8ijR8SoxCe14IkWHoDSk3BOADOxphrg9iMnsYUtDQ0U9oBOwvXeQZVx6zHiN4VAJIMkhhu2S-zPBZfB0uIRKiu7adthicX2CwWesukKADp8ujSK48h-iWbeJHosrUsRnZko3ZCN3QAAIug3Gg=="
  scan_interval: 3600
  sensor:
    - name: Local Datahub Daily
      value_template: "{{ value_json['features'][0]['properties']['modelRunDate'] }}"
      json_attributes_path: $.features.0.properties
      json_attributes:
        - timeSeries

The API key looks like mine, but mine was the free 360 calls / day Global Spot one:

The 55 calls one is for the Site-Specific Blended Probabilistic Forecast — the API might be different for that.

If that’s not it, possibly too much precision in the lat/long? Here’s mine with the numbers changed, but you can have the fact that I’m west of Greenwich for free:

- resource: https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/hourly?datasource=BD1&includeLocationName=true&latitude=12.3456&longitude=-1.234&excludeParameterMetadata=true
  headers:
    apikey: "eyJ4[ redacted ]Hc-jQCj3SQ=="
  scan_interval: 3600

here’s one of mine:

rest:
- resource: !secret met_office_datahub_1_hourly
  headers:
    apikey: !secret met_office_datahub_apikey
  scan_interval: 3600
  sensor:
    - name: Local Datahub Hourly
      value_template: "{{ value_json['features'][0]['properties']['modelRunDate'] }}"
      json_attributes_path: $.features.0.properties
      json_attributes:
        - timeSeries

and secrets:

met_office_datahub_1_hourly:  https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/hourly?datasource=BD1&includeLocationName=true&latitude=51.xxxxx&longitude=-1.xxxxx&excludeParameterMetadata=true
met_office_datahub_apikey:    "eyJ4...=="

…also west on Greenwich :slight_smile:

Thanks both !

I changed to Global Spot API and reduced the Lat / Long down to x.xx and it works :+1:

I’ll let it run for a while then change the Lat / Long to see if it’s just that the API needs to be Global Spot.

great.

I think you you can do x.xxxxx though (at least it works for me).

One quick one, my Units do not display correctly on the UI, I change the unit vlaue in config.yaml and it changes the calculation but it doesn’t change the label in the UI appropriatley.

Looking out the window I definatley can’t see 22,000 kilometres…

try this sensor:

    - name: Weather visibility
      device_class: distance
      unit_of_measurement: "km"
      state: "{{ state_attr('weather.met_office_datahub','visibility') }}"

edit: also check wind speed…

Morning,

I’ve started to try and get this working but clearly running into an issue somewhere.

If I query the API directly in the Met Office website, I get a good response so I know the API key and Long/Lat data is correct. However in the Data Hub entry I get an Unknown :frowning: My gut feeling is its an issue in the rest config…

My main config.yaml is configured to use these separate yaml files

rest: !include rest.yaml
        
template: !include template.yaml
      
weather: !include weather.yaml

My rest.yaml example (redacted API + different Long/Lat)

- resource: https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/hourly?datasource=BD1&includeLocationName=true&latitude=50.9392&longitude=-2.068&excludeParameterMetadata=true
  headers:
    apikey: "eyJ4NXQiOiJOak16WW REDACTED"
  scan_interval: 3600
  sensor:
    - name: Local Datahub Hourly
      value_template: "{{ value_json['features'][0]['properties']['modelRunDate'] }}"
      json_attributes_path: $.features.0.properties
      json_attributes:
        - timeSeries

My template.yaml (exact)

- sensor:
    - name: Local current weather
      state: >
        {% set tsl = state_attr('sensor.local_datahub_hourly','timeSeries')|map(attribute='time')|map('as_timestamp')|list %}
        {% set ts = tsl|select('>=',(now()-timedelta(minutes=30))|as_timestamp())|first %}
        {{ tsl.index(ts) }}
      attributes:
        status: "{{ state_attr('sensor.local_datahub_hourly','timeSeries')[this.state|int(0)] }}"
      availability: "{{ states('sensor.local_datahub_hourly') not in ('unavailable', 'unknown') }}"

- sensor:
    - name: Local weather condition
      state: >
        {% from 'met_office_codes.jinja' import code2ha %}
        {{ code2ha(state_attr('sensor.local_current_weather','status')['significantWeatherCode']) }}
      attributes:
        timestamp: "{{ state_attr('sensor.local_current_weather','status')['time'] }}"
      availability: "{{ states('sensor.local_current_weather') not in ('unavailable', 'unknown') }}"

- sensor:
    - name: Local wind bearing
      state: >
        {% from 'direction.jinja' import dir %}
        {{ dir(state_attr('sensor.local_current_weather','status')['windDirectionFrom10m']) }}
      attributes:
        timestamp: "{{ state_attr('sensor.local_current_weather','status')['time'] }}"
      availability: "{{ states('sensor.local_current_weather') not in ('unavailable', 'unknown') }}"

Won’t paste the weather.yaml file as too large but its a mirror copy from this thread

I assume you’ve tried restarting? Check your logs for errors?

Mine is still working fine, and your config looks the same other than key and long/lat.

Ah yes logs, this error which i’d imagine is because no data has been extracted from the rest calls

  • TemplateError(‘TypeError: ‘NoneType’ object is not iterable’) while processing template ‘Template<template=({% from ‘met_office_codes.jinja’ import code2ha %} {% set dh = state_attr(‘sensor.local_datahub_daily’,‘timeSeries’) %} {% set ns = namespace(forecast=) %} {% for ts in dh -%} {% if ts[‘time’]|as_datetime > now() %} {% set tsd = { ‘datetime’: (ts[‘time’]|as_datetime).isoformat(), ‘condition’: code2ha(ts.get(‘daySignificantWeatherCode’, ts.get(‘nightSignificantWeatherCode’, ‘n/a’))), ‘humidity’: ts[‘middayRelativeHumidity’], ‘pressure’: ts[‘middayMslp’], ‘temperature’: ts[‘dayMaxScreenTemperature’]|round(0), ‘templow’: ts[‘nightMinScreenTemperature’]|round(0), ‘wind_speed’: ts[‘midday10MWindSpeed’]|round(0) } -%} {% set ns.forecast = ns.forecast + [tsd] -%} {% endif %} {% endfor %} {{ ns.forecast }}) renders=4>’ for attribute ‘_forecast_daily’ in entity ‘weather.met_office_datahub’

Yeah, I’d remove everything downstream of the rest sensor until you get that working.

Restarted HA fixed it, feel rather stupid now :stuck_out_tongue:

although the temperature shows as 0c

Looks like it didn’t create the sensor.outside_temperature sensor for some reason

You missed that bit in my admittedly wordy explanation. Use:

  temperature_template: "{{ state_attr('sensor.local_current_weather','status')['screenTemperature'] }}"

in your weather entity.

Ah yes :slight_smile: , I’ll adjust that with my own sensor

@Troon Thank you for the pointers and all the work you’ve done for this!

1 Like