European power grid frequency sensor

Hi everyone, I hope you have a great day!

As a small Sunday project, I decided to display the European power grid frequency in HA. I don’t have any hardware to measure it at home, so I searched for a way to utilize free online services. And why not share my solutions with the rest of you :). Ideas and recommendations are very welcome, I’m also fairly new to HA.

Why


I am living in Germany and I saw a video about the power grid and how the frequency reflects the health of the grid. This made me curious and wanted to dive deeper into the topic. I also wanted to represent the data in my HA dashboard.

Providers


At the time of writing this post, I found three data providers.

  • netzfrequenz.info
    • Pros
      • Simple
      • High update rate
    • Cons
      • Data provided by a private project
  • gridradar.net
    • Pros
      • API
      • Professional service
    • Cons
      • Low update rate (1m)
  • netzfrequenzmessung.de
    • Pros
      • Also provides phase-angle relative to 50Hz
      • High update rate (up to 1s)
    • Cons
      • Limited queries per day
      • No official API

Solutions


Solution 1: netzfrequenz.info

BEST

My most recent and also preferred solution. The endpoint returns just the current frequency without any bells and whistles. An update frequency of 1-second is also nice to have. The project is run by a single person and the provider states that the data should not be used for any controlling tasks.

Update Nov. 2024: I have updated the sensor configuration based on an improvement provided by @haip :pray:

Add the sensor config to your: /homeassistant/configuration.yaml

sensor:
  - platform: rest
    name: Netzfrequenz
    resource: "https://www.netzfrequenz.info/json/act.json"
    force_update: true
    scan_interval: 3
    value_template: >
        {% set netzfrequenz = float(value_json) | round(2) %}
        {% if netzfrequenz == 49 %}
          {{ states('sensor.netzfrequenz') }}
        {% else %}
          {{ netzfrequenz }}
        {% endif %}
    icon: mdi:sine-wave
    unit_of_measurement: Hz
    state_class: measurement


Solution 2: gridradar.net

Good

Gridradar sells its grid data for monitoring and analysis and provides an API. A better and more solid solution. A gridradar.net user account is required for this sensor. It works well so far, is simple, and provides API documentation. The update rate seems to be limited to around one query every ~60 seconds. The API returns a list of data points since the last query with the provided key, my implementation just picks the last one from the list and discards the rest.

Note: You have to set your API token in your secrets.yaml.
Example: gridradar_net_token: Bearer your-token

  - platform: rest
    # Grid Frequency (gridradar.net)
    name: gridradar_net_frequency_ucte
    resource: "https://api.gridradar.net/query"
    headers:
      Authorization: !secret gridradar_net_token # bearer token
      Content-Type: application/json
    payload: '{"metric":"frequency-ucte-median-1s", "aggr":"1m"}'
    force_update: true
    scan_interval: 60 # the request interval for the free plan is limited, updating every 1 minute has proven to be the best
    value_template: "{{ float(value_json[0].datapoints[-1:][0][0]) | round(2) }}"
    unit_of_measurement: "Hz"
    state_class: "measurement"

Solution 3: netzfrequenzmessung.de

Limited

Probably the last solution you should use. The biggest downside of this provider is the statement that the display of the network frequency is limited per day. From my understanding, they say “display” because pulling the XML is not officially documented. Another limitation is the XML content type used by the website. It is defined as plain text, which makes HA not being able to convert it from XML to JSON automatically. If there is an option to force XML as the resource content type, please let me know :). Right now I’m going through a public XML to JSON converter. Ugly & fragile.

  - platform: rest
    # Powergrid Frequency (netzfrequenzmessung.de)
    # Limitiations:
    #  - Requires "xml-to-json" proxy
    #  - Limited requests per day: https://www.netzfrequenzmessung.de/konzept.htm
    name: netzfrequenzmessung_de_dataprovider
    resource: "https://api.factmaven.com/xml-to-json/?xml=https://netzfrequenzmessung.de:9080/frequenz02.xml" # proxy is required because endpoint returns plain text content type
    force_update: true
    scan_interval: 10
    json_attributes_path: "$.r"
    state_class: "measurement"
    value_template: "OK"
    json_attributes:
      - f2
      - p
   - platform: template
    sensors:
      netzfrequenzmessung_de_frequency_ucte:
        unit_of_measurement: "Hz"
        value_template: "{{ float(state_attr('sensor.netzfrequenzmessung_de_dataprovider', 'f2')) | round(2) }}"
      netzfrequenzmessung_de_phase_angle_ucte:
        unit_of_measurement: "°"
        value_template: "{{ float(state_attr('sensor.netzfrequenzmessung_de_dataprovider', 'p')) }}"

Extra: I also found an iframe on netzfrequenz.info that can be implemented as a panel webpage. The labels are in German though. https://www.netzfrequenz.info/charts/gauge_full.php


Wish you all the best, Tilman :v:

8 Likes

Hello,

I tried the netzfrequenz.info API but sometimes there are values of exactly 49 Hz spikes. Is this intentionally by the API provider or some sort of a bug?

Does anyone have the same behavior?

Regards
Marc

I also use netzfrequenz.info and have sometimes values of exact 49 Hz.

I put an automation between the raw value netzfrequenz_info_frequency_ucte and my new variable and update my variable on change of netzfrequenz_info_frequency_ucte but only if the value is not 49.0:

alias: update netzfreq cleaned
trigger:
  - platform: state
    entity_id: sensor.netzfrequenz_info_frequency_ucte
condition:
  - condition: not
    conditions:
      - condition: state
        entity_id: sensor.netzfrequenz_info_frequency_ucte
        state: "49.0"
action:
  - service: input_number.set_value
    entity_id: input_number.grid_frequency
    data:
      value: "{{ states.sensor.netzfrequenz_info_frequency_ucte.state }}"

The downside is, that my logs are flooded with change messages, this will probably lead to a databasea problem in the future, but the result is exactly what I want.

Maybe a pro could give an advice on how to realize that in a “good” manner.

@vbherodes you could also use a template sensor for your rest sensor instead of an automation to filter out all “49” values.

rest sensor

- platform: rest
  name: Netzfrequenz Info
  resource: "https://www.netzfrequenz.info/json/act.json"
  force_update: true
  scan_interval: 5
  value_template: "{{ float(value_json) | round(2) }}"
  icon: mdi:sine-wave
  unit_of_measurement: Hz
  state_class: measurement

template sensor

- name: "Netzfrequenz"     
  unit_of_measurement: "Hz"
  state_class: measurement
  icon: hass:sine-wave
  state: >                                                                           
    {% set netzfrequenz = states('sensor.netzfrequenz_info') | float %}
    {% if netzfrequenz == 49 %}
      {{ states('sensor.netzfrequenz') }}
    {% else %}
      {{ netzfrequenz }}
    {% endif %}
1 Like

I’ll try that. Thanks!

Kann mir jemand helfen ich hab da immer so Ausschläge nach unten zu 49hz

Can someone help me, I always have rashes down to 49hz

sensor:

  • platform: rest
    name: Netzfrequenz Info Frequency
    resource: “https://www.netzfrequenz.info/json/act.json
    force_update: true
    scan_interval: 3
    value_template: “{{ float(value_json) }}”
    icon: mdi:sine-wave
    unit_of_measurement: Hz
    state_class: measurement

template:

  • sensor:
    • name: “Netzfrequenz”
      unit_of_measurement: “HZ”
      state_class: measurement
      state: >
      {% if states(‘sensor.netzfrequenz_info_frequency’) == 49 %}
      {{ states(‘sensor.netzfrequenz’) }}
      {% else %}
      {{ states(‘sensor.netzfrequenz_info_frequency’) | float }}
      {% endif %}

In one Sensor (Template Sensor not neeeded):

- platform: rest
  name: Netzfrequenz
  resource: "https://www.netzfrequenz.info/json/act.json"
  force_update: true
  scan_interval: 3
  value_template: >
    {% set netzfrequenz = float(value_json) | round(2) %}
    {% if netzfrequenz == 49 %}
      {{ states('sensor.netzfrequenz') }}
    {% else %}
      {{ netzfrequenz }}
    {% endif %}
  icon: mdi:sine-wave
  unit_of_measurement: Hz
  state_class: measurement

Bildschirm­foto 2023-01-29 um 20.18.07

@cool338 In my first example with the template sensor, you have to use the template sensor as your main entity. But with my last rest sensor, everything is handled in this sensor (delete the template sensor). No outliers of 49 anymore.

2 Likes

Hi Tilman,

I am also new to Home Assistant … Where to insert the script, I have not found anything suitable.

Greetings
Markus

found it ?

#2 works for me with a short modification:

sensor:
#
## Grid-Radar
#
  - platform: rest
    # Grid Frequency (gridradar.net)
    name: gridradar_net_frequency_ucte
    resource: "https://api.gridradar.net/query?token=_MEIN-TOKEN_&metric=frequency-ucte-median-1s"
    payload: '{"metric":"frequency-ucte-median-1s", "aggr":"1m"}'
    force_update: true
    scan_interval: 60
    value_template: "{{ float(value_json[0].datapoints[-1:][0][0]) | round(2) }}"
    icon: mdi:sine-wave
    unit_of_measurement: "Hz"
    state_class: "measurement"
#

Thx!

I’m sorry for the late reply, I have updated my initial post with a bit more info. Generally, sensor configurations go in your /homeassistant/configuration.yaml :v: