How can I configure the energy dashboard to understand my net electric meter?

  • I have rtlamr2mqtt set up, receiving data from my electic meter over rtlamr and publishing the data to home assistant with mqtt
  • I have solaredge reporting my solar production

Everything with the energy dashboard seems to mostly work:



image

The main sticking point is that my energy meter is a net meter, and home assistant doesn’t seem to know how to handle that. Doing the math, my solar system produced 11.92 kWh and my net meter reports -2.85 kWh, so my home’s usage is 9.07 kWh and I returned 2.85 kWh to the grid. Instead, home assistant thinks my home’s usage is 11.92 kWh, my grid usage is -2.85 kWh (it doesn’t seem to handle negative numbers here very well), and I returned 0 kWh to the grid.

Similar problems were solved on other threads by creating a template sensor to split a single state_class: measurement sensor into two separate sensors, one for consumption and one for return based on the sign of the sensor’s state (positive values were usage, negative values were return, or vice-versa). But my sensor is total, so its value will generally always have the same sign, and that solution won’t really work here.

  1. Looking at the documentation for long-term statistics, the example table has separate sum_increase and sum_decrease columns. I think this is exactly what I want. sum_increase would be grid consumption and sum_decrease would be grid return. I couldn’t figure out how to access those separate columns from a template sensor though. Playing around in the developer tools playground page, I can see that states.statistics is an object, but iterating over it doesn’t reveal any entities. Are those real columns that are being calculated or was it just for the sake of the example? If they’re real, how can I access them in a template sensor?
  2. Is there some way to craft a template sensor that converts a total sensor into a measurement sensor? Once I have a measurement sensor, I can probably apply the same solution from the other posts to get separate sensors for grid consumption and grid return.
  3. Is my current set up something that home assistant should support straight out of the box? All the data is there, it just seems to be doing math a little wrong
  1. It appears that sum_increase and sum_decrease are indeed just for the sake of the example. The long-term statistics are stored in a SQL database, and the schema can be found here (it only has the overall sum column`).
  2. I played around with a couple different ways of doing this:
  • I tried derivative and integration sensors. I was taking a derivative of my net meter sensor.electric_meter, filtering that derivative into two separate template sensors based on the sign of the derivative, then taking the Riemann sum of those two template sensors. The numbers ended up being pretty far off, so I abandoned this idea (and I don’t have the config I tried to show here).
  • I tried a couple plain template sensors that did basically what the derivative and integral sensors would do. There were a couple problems if the underlying sensor.electric_meter state was ever unavailable or unknown. And the numbers ended up being a little inaccurate here at times, maybe due to floating point errors

template.yaml:

- trigger:
  - platform: state
    entity_id: sensor.electric_meter
    to: # Skip attribute-only changes.
  sensor:
    - name: "Electric Meter Consumption"
      unique_id: "electric_meter_consumption"
      device_class: "energy"
      unit_of_measurement: "kWh"
      state_class: "total_increasing"
      state: >
        {% set last = states("sensor.electric_meter_consumption") | float(0) %}
        {% if trigger.from_state.state == 'unknown' 
                or trigger.from_state.state == 'unavailable'
                or trigger.to_state.state == 'unknown'
                or trigger.to_state.state == 'unavailable' %}
          {{ last }}
        {% else %}
          {% set from = trigger.from_state.state | float %}
          {% set to = trigger.to_state.state | float %}
          {% if to - from > 0 %}
            {{ last + to - from }}
          {% else %}
            {{ last }}
          {% endif %}
        {% endif %}
    - name: "Electric Meter Return"
      unique_id: "electric_meter_return"
      device_class: "energy"
      unit_of_measurement: "kWh"
      state_class: "total_increasing"
      state: >
        {% set last = states("sensor.electric_meter_return") | float(0) %}
        {% if trigger.from_state.state == 'unknown' 
                or trigger.from_state.state == 'unavailable'
                or trigger.to_state.state == 'unknown'
                or trigger.to_state.state == 'unavailable' %}
          {{ last }}
        {% else %}
          {% set from = trigger.from_state.state | float %}
          {% set to = trigger.to_state.state | float %}
          {% if to - from > 0 %}
            {{ last }}
          {% else %}
            {{ last + from - to }}
          {% endif %}
        {% endif %}
  • Lastly, I tried an SQL sensor. This is maybe a little more resource intensive, but AFAICT there aren’t any inaccuracies in the numbers it reports.

sensor.yaml:

- platform: sql
  scan_interval: 300
  queries:
    - name: electric_meter_consumption_sql
      query: >
        SELECT SUM(delta) AS state, 'energy' AS device_class, 'total_increasing' AS state_class
        FROM (
          SELECT state - LAG(state) OVER (ORDER BY state_id) AS delta
          FROM states
          WHERE entity_id = 'sensor.electric_meter'
            AND state != 'unknown'
            AND state != 'unavailable')
        WHERE delta > 0;
      column: state
      unit_of_measurement: kWh
      
    - name: electric_meter_return_sql
      query: >
        SELECT -SUM(delta) AS state, 'energy' AS device_class, 'total_increasing' AS state_class
        FROM (
          SELECT state - LAG(state) OVER (ORDER BY state_id) AS delta
          FROM states
          WHERE entity_id = 'sensor.electric_meter'
            AND state != 'unknown'
            AND state != 'unavailable')
        WHERE delta < 0;
      column: state
      unit_of_measurement: kWh
  1. This still feels like quite a few hoops to jump through to get this work. I might look into seeing if the energy dashboard can support this a little more natively

Hello! I have the same issue with a energy meter which can run both ways to indicate cons/prod. Very smart solution with the sql sensor which seem very accurate, but how do we avoid the issue with HA database autopurge every night 04:12 which messes up the energy tab staples?

Is that sensor one that’s being generated from the sql, or something else?

I think I ran into a similar problem where HomeAssistant eventually purges history from the electric_meter, and I ended up tweaking the sql even more, making it even more complicated and nigh unreadable >.< Instead of just summing the deltas from from the electric_meter, the sql sensor now keeps track of which electric_meter states it has already processed in an attribute, and adds only new electric_meter states to its current value. So for electric_meter_consumption_sql I have

    SELECT
      MAX(sql_sensor.state)  -- All sql_sensor.state values should be the same.
        + SUM(  -- SUM the deltas we haven't seen yet.
            CASE
              source_sensor.state_id > sql_sensor.last_processed_state_id
              AND source_sensor.delta > 0
            WHEN TRUE THEN source_sensor.delta
            ELSE 0
            END) AS state,
      MAX(source_sensor.state_id) AS last_processed_state_id,
      'energy' AS device_class,
      'total_increasing' AS state_class
    FROM (
      SELECT
        state_id,
        state,
        IFNULL(json_extract(attributes, '$.last_processed_state_id'), 0) AS last_processed_state_id
      FROM states
      WHERE entity_id = 'sensor.electric_meter_consumption_sql'
      UNION ALL
      SELECT
        0 AS state_id,
        0 AS state,
        0 AS last_processed_state_id
      ORDER BY state_id DESC
      LIMIT 1
    ) AS sql_sensor LEFT JOIN (
      SELECT
        state_id,
        state - LAG(state) OVER (ORDER BY state_id) AS delta
      FROM states
      WHERE TRUE
        AND entity_id = 'sensor.electric_meter'
        AND state != 'unknown'
        AND state != 'unavailable'
    ) AS source_sensor ON (TRUE);

And then electric_meter_return_sql is basically the same with just a few small changes

    SELECT
      ...
        + SUM(  -- SUM the deltas we haven't seen yet.
            CASE
              source_sensor.state_id > sql_sensor.last_processed_state_id
              AND source_sensor.delta < 0
            WHEN TRUE THEN -source_sensor.delta
            ELSE 0
            END) AS state,
      ...
    FROM (
      ...
      WHERE entity_id = 'sensor.electric_meter_return_sql'
      ...
    ) AS sql_sensor LEFT JOIN (
      ... );

My screendump was one of the SQL-sensors history… Good work with your tweaking there, that’s beyond my coding skills, thanks for your example :slight_smile:

My workaround which seem to work was to add both SQL-sensors as utility meters which never goes negative and use them in energy module instead.

Hi Jeffrey and Danne, I am in the same boat with rtlamr2mqtt. I was wondering if you had considered trying to address this by creating two MQTT sensors derived from the same MQTT topic: one sensor for positive values and one sensor for negative values?

My meter gives me a reading of the net electric consumption since it was first installed. Currently, the number’s around 1400 kWh. You would still need something keeping track of the deltas between the current reading and the previous reading so you’d know which topic to publish the sum to.

I guess you could do this with a separate MQTT topic, but I’m not sure if it’s any less complex than a template or sql sensor

I see what you mean about keeping track of the previous values - it’s just as messy. It would be handy if the Energy Panel could be programmed to treat a negative consumption delta as “return to grid.”

As i wrote in my last reply it’s (ugly) solved by adding the sql-sensor as utility meter…

I’m glad the sql-sensor is working out, and I may try that. Before I do, in an effort to make something easier for me to understand, I wonder if this approach has any potential (not working- pseudocode):

automations.yaml:

- id: 1
  alias: demux energy
  trigger:
  - platform: mqtt
    topic: readings/XXXXXXXX/meter_reading
  action:
  - service: mqtt.publish
    data_template:
       payload: {{ trigger.from_state.state - trigger.to_state.state | abs }}
       topic: >
        {% if ( trigger.from_state.state < trigger.to_state.state ) %}
            readings/energy-consumed
        {% elif ( trigger.from_state.state > trigger.to_state.state ) %}
            readings/energy-returned
        {% endif %}

Then I can make two energy sensors of ‘state_class: measurement’ based on these two topics. If you think it has potential, could either of you help make the syntax correct?

The MQTT trigger doesn’t give you from_state and to_state templating variables: Automation Trigger Variables - Home Assistant

The state trigger gives you those variables: Automation Trigger Variables - Home Assistant

I had played around with using a state trigger with a template sensor to do this above, but the problem I was running into was unknown and unavailable sensor states messing up the calculations. Basically my electric meter would go from a reading of 5, become unavailable, then have a reading of 11. The state trigger would report two separate events from_state: 5, to_state: unavailable and from_state: unavailable, to_state: 11, and the template sensor would miss the 11 - 5 delta

I’m very interested in this thread. I find it hard to believe that the Energy Panel does not easily support net meters which where I live in California is what we all currently have. I too have a device (emporia vue) that is just giving me net readings. I’ve been working on this for a few days and just stumbled this thread. I’m first going to try the triggers you shared above to understand the issues you were talking about. If the results are only off a little for the day then I like that approach because it is easy for old guys like me to understand–and it is conceptually simpler. While I used SQL in a past job i really hate it and find it had to understand.

Anyway I’m going to follow this discussion. Thanks.

1 Like

So being a newbie am having trouble getting the config file set up. I’ll figure it out but in looking at the script you did above did you consider adding a condition to the trigger so that it only happens if the ‘to state’ is valid?

update: I did get my config files straightened out. :slight_smile: I also added some rounding of the numbers when setting the state. Being a newbie I tried to see how to add a condition on the triggers with the new style template sensor but never figured it out.

I’ll give it a day now to see how close it is to actuals.

If Im happy fine, if not I’ll go down the SQL path.

In the template sensor above I had

        {% if trigger.from_state.state == 'unknown' 
                or trigger.from_state.state == 'unavailable'
                or trigger.to_state.state == 'unknown'
                or trigger.to_state.state == 'unavailable' %}
          {{ last }}
        {% else %}

which was effectively a condition skipping when either state was unknown/unavailable. It probably would have been better off as a condition. Although I’m not sure if template sensors accept conditions like automations do. Looking at the documentation doesn’t show me how to add a condition to a template sensor other than how I did it :confused:

So far, the template sensor approach with added rounding seems to be working OK for me. I submitted a Feature Request about this topic, hoping that the Energy Dashboard devs will consider it.

2 Likes

Not sure how well this would work but maybe you could use a trend binary sensor an automation, and a utility meter with two tariffs (export and import).

This binary sensor should be on when you are importing energy, and off when you are exporting energy:

binary_sensor:
  - platform: trend
    sensors:
      energy_export:
        entity_id: sensor.your_net_energy_sensor
        max_samples: 2
        min_gradient: 0

This utility meter will create two energy sensors, one for import and one for export:

utility_meter:
  grid_energy:
    source: sensor.your_net_energy_sensor
    cycle: daily
    net_consumption: true
    tariffs:
      - import
      - export

This automation switches the utility meter tariffs:

trigger:
  - platform: state
    entity_id: binary_sensor.energy_export
    to:
      - 'on'
      - 'off'
action:
  - service: utility_meter.select_tariff
    data:
      entity_id: utility_meter.grid_energy
      tariff: "{{ 'export' if trigger.to_state.state == 'on' else 'import' }}"

My attempt to solve this problem used the statistics sensor to difference the samples.

sensor:
  - platform: statistics    #seems to miss some points so systematically underreports
    name: "Electricity net meter delta"
    entity_id: sensor.electricity_net_meter
    state_characteristic: change
    sampling_size: 2

and I have a binary sensor to detect when it goes negative

binary_sensor:
  - platform: threshold
    entity_id: sensor.electricity_net_meter_delta
    lower: 0
    name: electricity grid return

I split the signal from the rtlamr2mqtt like this:

  - sensor:
      - name: "electricity grid delta consumption"
        unit_of_measurement: "kWh"
        state: >
          {{ [states('sensor.electricity_net_meter_delta')|float, 0 ]|max }}
        device_class: energy
        state_class: total     
  - sensor:
      - name: "electricity grid delta return"
        unit_of_measurement: "kWh"
        state: >
          {{ [states('sensor.electricity_net_meter_delta')|float, 0 ]|min |abs }}
        device_class: energy
        state_class: total

I define the utility meters like this, using the delta values option

utility_meter:
   daily_electricity_grid_consumption:   
        source: sensor.electricity_grid_delta_consumption
        cycle: daily
        net_consumption: false
        delta_values: true
    daily_electricity_grid_return:   
        source: sensor.electricity_grid_delta_return
        cycle: daily
        net_consumption: false
        delta_values: true

This seemed to work ok, but on closer inspection, the statistics sensor seems to lose samples, and so slowly diverges from the underlying signal. You can see that it misses the increase at 10:42 and 10:57 etc.

I would appreciate any insights on why some of the data points may be missed.

Thanks

I have the same setup as you, i am having exactly the same issue.
It skips those times because the value does not change.
Because of this the utility meter thinks it is an unchanged value and does not update.
Have you solved this issue?

I have this same issue as well. Wondering if others have gleaned a solution?

I wanted to use @JeffreyFalgout’s SQL sensor solution, but I use PostgreSQL as my recorder database instead of MySQL or SQLite, so I had to do some tweaking to the queries. Below are my revised queries for PostgreSQL, plus the updated SQL sensor entities in YAML. I’m pleased to report that everything is working perfectly, and I’m grateful that most of the work was already done by @JeffreyFalgout!

Note that the SQL sensor syntax seems to have changed a bit in the interim.

Production Sensor Query

SELECT
    MAX(sql_sensor.state)
    + SUM(
        CASE
        source_sensor.state_id > sql_sensor.last_processed_state_id
        AND source_sensor.delta > 0
            WHEN TRUE THEN source_sensor.delta
            ELSE 0
        END
    ) AS state,
    MAX(source_sensor.state_id) AS last_processed_state_id,
    'energy' AS device_class,
    'total_increasing' AS state_class
FROM (
    SELECT
        states.state_id AS state_id,
        CASE
            WHEN states.state = 'unknown'
                THEN 0
            ELSE states.state::numeric
        END AS state,
        COALESCE(
            (state_attributes.shared_attrs::json ->> 'last_processed_state_id'
            )::numeric, 0
        ) AS last_processed_state_id
    FROM states
    LEFT JOIN
        state_attributes
        ON states.attributes_id = state_attributes.attributes_id
    WHERE states.entity_id = 'sensor.electric_meter_consumption_sql'
    UNION ALL
    SELECT
        0 AS state_id,
        0 AS state,
        0 AS last_processed_state_id
    ORDER BY state_id DESC
    LIMIT 1
) AS sql_sensor LEFT JOIN (
    SELECT
        state_id,
        state::numeric - LAG(state::numeric) OVER (ORDER BY state_id)
        AS delta
    FROM states
    WHERE
        TRUE
        AND entity_id = 'sensor.meter_electric_new'
        AND state != 'unknown'
        AND state != 'unavailable'
) AS source_sensor ON (TRUE);

Production Sensor YAML

- name: Electric Meter Consumption SQL
  db_url: !secret postgres_db_url
  query: >
    [insert query above]
  column: state
  unit_of_measurement: kWh

Return Sensor Query

SELECT
    MAX(sql_sensor.state)
    + SUM(
        CASE
        source_sensor.state_id > sql_sensor.last_processed_state_id
        AND source_sensor.delta < 0
            WHEN TRUE THEN -source_sensor.delta
            ELSE 0
        END
    ) AS state,
    MAX(source_sensor.state_id) AS last_processed_state_id,
    'energy' AS device_class,
    'total_increasing' AS state_class
FROM (
    SELECT
        states.state_id AS state_id,
        CASE
            WHEN states.state = 'unknown'
                THEN 0
            ELSE states.state::numeric
        END
        AS state,
        COALESCE(
            (
                state_attributes.shared_attrs::json
                ->> 'last_processed_state_id'
            )::numeric,
            0
        ) AS last_processed_state_id
    FROM states
    LEFT JOIN
        state_attributes
        ON states.attributes_id = state_attributes.attributes_id
    WHERE states.entity_id = 'sensor.electric_meter_return_sql'
    UNION ALL
    SELECT
        0 AS state_id,
        0 AS state,
        0 AS last_processed_state_id
    ORDER BY state_id DESC
    LIMIT 1
) AS sql_sensor LEFT JOIN (
    SELECT
        state_id,
        state::numeric - LAG(state::numeric) OVER (ORDER BY state_id)
        AS delta
    FROM states
    WHERE
        TRUE
        AND entity_id = 'sensor.meter_electric_new'
        AND state != 'unknown'
        AND state != 'unavailable'
) AS source_sensor ON (TRUE);

Return Sensor YAML

- name: Electric Meter Return SQL
  db_url: !secret postgres_db_url
  query: >
    [insert query above]
  column: state
  unit_of_measurement: kWh