Net metering (in 15 minutes intervals) with Utility Meters and Nodered (easily done without Nodered)

Hi there,

This topic is about calculating net metering in real-time on HA with a small flow in nodered (that can easily be done only in HA because it’s a render template node)

Having lots of Utility Meters for tracking several energy consumption (grid, solar, returned, used as well as lots of specific devices) I wanted to have a real time value of net metering as calculated in the Portuguese E-Redes (don’t really know if it’s the same in some other countries), that is, 15 minutes net metering: deduce the injected energy in relation to the grid consumption in 15 minutes intervals:
If returned>grid consumption, the rest of the energy is considered “injected” and possibly sold.
If returned<grid consumption, it’s only considered the returned value in the net metering 15 min interval and the rest of the value is paid.

For that i use utility meters:

  • sensor.quarter_hourly_total_energy - grid consumption (shelly em on the main electrical board).
  • sensor.quarter_hourly_return_to_grid - an utility meter with an automatic calculation with the returned energy (when solar is great than the house needs)

Before this net metering calculation I had no “quarter_hourly” sensor, only daily, monthly and yearly but I needed them for this 15 min calculation.

The Utility Meters I use:

# Shelly-EM-01 - Clamp #1
  quarter_hourly_total_energy:
    source: sensor.total_energy
    cycle: quarter-hourly

  quarter_hourly_return_to_grid:
    source: sensor.return_to_grid_calculated_kwh
    cycle: quarter-hourly

The sensors for the second utility meter:

  - platform: template
    sensors: 
      return_to_grid_calculated:
        friendly_name: "Return to Grid - Calculated (power)"
        unit_of_measurement: 'W'
        icon_template: 'mdi:solar-power-variant'
        device_class: energy
        value_template: >-
          {% if states("sensor.total_power")|float(2)  < 0 %}
            {{((states("sensor.total_power")|float(2))|round(2)*(-1))}}
          {% else %}
            {{(states ('0.0')|float(0))|round(2) }}
          {% endif %}

 - platform: integration
    source: sensor.return_to_grid_calculated
    name: return_to_grid_calculated_kwh
    unit_prefix: k
    round: 2
    method: left

Then I need the utility meters for putting the calculated values (my month starts at 12):

# Netmetering (calculated)
  quarter_hourly_netmetering_energy:
    source: sensor.quarter_hourly_netmetering
    cycle: quarter-hourly
  daily_netmetering_energy:
    source: sensor.quarter_hourly_netmetering
    cycle: daily
  monthly_netmetering_energy:
    source: sensor.quarter_hourly_netmetering
    cycle: monthly
    offset:
      days: 12
  yearly_netmetering_energy:
    source: sensor.quarter_hourly_netmetering
    cycle: yearly
  #
  # Injection (calculated)
  quarter_hourly_injection_energy:
    source: sensor.quarter_hourly_injection
    cycle: quarter-hourly
  daily_injection_energy:
    source: sensor.quarter_hourly_injection
    cycle: daily
  monthly_injection_energy:
    source: sensor.quarter_hourly_injection
    cycle: monthly
    offset:
      days: 12
  yearly_injection_energy:
    source: sensor.quarter_hourly_injection
    cycle: yearly
 

For calculating values to put on those Utility Meters i used a Nodered flow that calculates the net value and puts it on the sensor feed in the utility meters: sensor.quarter_hourly_netmetering and sensor.quarter_hourly_injection

 [{"id":"4a650827d449fd3b","type":"cronplus","z":"7d27859395acc0bf","name":"quarter-hourly","outputField":"payload","timeZone":"","persistDynamic":false,"commandResponseMsgOutput":"output1","outputs":1,"options":[{"name":"schedule1","topic":"quarter-hourly","payloadType":"num","payload":"0","expressionType":"cron","expression":"58 14,29,44,59 * * * * *","location":"","offset":"0","solarType":"all","solarEvents":"sunrise,sunset"}],"x":110,"y":115,"wires":[["9b76811efbdbfc58","ec45781ca3ab7e3b"]]},{"id":"a2a6d28d63fead7e","type":"comment","z":"7d27859395acc0bf","name":"Netmetering","info":"","x":325,"y":80,"wires":[]},{"id":"9b76811efbdbfc58","type":"api-render-template","z":"7d27859395acc0bf","name":"netmetering calculation","server":"14d98a81.8cd3d5","version":0,"template":"{% set  totalnow = states('sensor.quarter_hourly_total_energy')|float(0)|round(2)  %}\n{% set  returnnow = states('sensor.quarter_hourly_return_to_grid')|float(0)|round(2)  %}\n\n{% set netmetering_temp = (totalnow|float(0)|round(2))-(returnnow|float(0)|round(2)) %}\n\n{%- if (netmetering_temp|float(0)|round(2))  > 0 %}\n{%-  set netmetering = returnnow %}\n{%- else %}\n{%-  set netmetering = totalnow %}\n{%- endif %}\n\n{%  set  before = states('sensor.quarter_hourly_netmetering')|float(0)|round(2) %}\n{%  set  netmetering = before + netmetering  %}\n\n{{ netmetering|float(0)|round(2) }}","resultsLocation":"payload","resultsLocationType":"msg","templateLocation":"","templateLocationType":"none","x":365,"y":115,"wires":[["496e8b1fb765dac0","af67ee410ff2e88b"]]},{"id":"496e8b1fb765dac0","type":"debug","z":"7d27859395acc0bf","name":"netmetering","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"netmetering","statusType":"auto","x":600,"y":165,"wires":[]},{"id":"af67ee410ff2e88b","type":"ha-sensor","z":"7d27859395acc0bf","name":"Quarter Hourly Netmetering","entityConfig":"75fd01f783ef7adb","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":650,"y":115,"wires":[[]]},{"id":"b79940d566dfca8c","type":"comment","z":"7d27859395acc0bf","name":"Injection","info":"","x":315,"y":170,"wires":[]},{"id":"ec45781ca3ab7e3b","type":"api-render-template","z":"7d27859395acc0bf","name":"injection calculation","server":"14d98a81.8cd3d5","version":0,"template":"{% set  totalnow = states('sensor.quarter_hourly_total_energy')|float(0)|round(2)  %}\n{% set  returnnow = states('sensor.quarter_hourly_return_to_grid')|float(0)|round(2)  %}\n\n{% set netmetering_temp = (totalnow|float(0)|round(2))-(returnnow|float(0)|round(2)) %}\n\n{%- if (netmetering_temp|float(0)|round(2))  > 0 %}\n{%-  set netmetering = returnnow %}\n{%- else %}\n{%-  set netmetering = totalnow %}\n{%- endif %}\n\n{% if (netmetering < returnnow) %}\n{%  set injection = (returnnow - netmetering) %}\n{% else %}\n{%  set injection = 0 %}\n{% endif %}\n\n{%  set before = states('sensor.quarter_hourly_injection')|float(0)|round(2) %}\n{%  set injection = before + injection  %}\n\n{{ injection|float(0)|round(2) }}","resultsLocation":"payload","resultsLocationType":"msg","templateLocation":"","templateLocationType":"none","x":355,"y":205,"wires":[["da145458a516b149","998feb1daf452241"]]},{"id":"da145458a516b149","type":"debug","z":"7d27859395acc0bf","name":"injection","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"netmetering","statusType":"auto","x":590,"y":255,"wires":[]},{"id":"998feb1daf452241","type":"ha-sensor","z":"7d27859395acc0bf","name":"Quarter Hourly Injection","entityConfig":"da3d1f926b845a0f","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":640,"y":205,"wires":[[]]},{"id":"14d98a81.8cd3d5","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true},{"id":"75fd01f783ef7adb","type":"ha-entity-config","server":"14d98a81.8cd3d5","deviceConfig":"","name":"Quarter Hourly Netmetering","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Quarter Hourly Netmetering"},{"property":"icon","value":"mdi:gauge"},{"property":"entity_category","value":""},{"property":"device_class","value":"energy"},{"property":"unit_of_measurement","value":"kWh"},{"property":"state_class","value":"total_increasing"}],"resend":true,"debugEnabled":false},{"id":"da3d1f926b845a0f","type":"ha-entity-config","server":"14d98a81.8cd3d5","deviceConfig":"","name":"Quarter Hourly Injection","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Quarter Hourly Injection"},{"property":"icon","value":"mdi:gauge"},{"property":"entity_category","value":""},{"property":"device_class","value":"energy"},{"property":"unit_of_measurement","value":"kWh"},{"property":"state_class","value":"total_increasing"}],"resend":true,"debugEnabled":false}]

I run those “render template nodes” at 2 second before the quarter hour ends with the cronplus node.

The nodered flow code is above but here it is the Netmetering Calculation node, if you want it to put it on HA only:

{% set  totalnow = states('sensor.quarter_hourly_total_energy')|float(0)|round(2)  %}
{% set  returnnow = states('sensor.quarter_hourly_return_to_grid')|float(0)|round(2)  %}

{% set netmetering_temp = (totalnow|float(0)|round(2))-(returnnow|float(0)|round(2)) %}

{%- if (netmetering_temp|float(0)|round(2))  > 0 %}
{%-  set netmetering = returnnow %}
{%- else %}
{%-  set netmetering = totalnow %}
{%- endif %}

{%  set  before = states('sensor.quarter_hourly_netmetering')|float(0)|round(2) %}
{%  set  netmetering = before + netmetering  %}

{{ netmetering|float(0)|round(2) }}

The Injection Calculation node:

{% set  totalnow = states('sensor.quarter_hourly_total_energy')|float(0)|round(2)  %}
{% set  returnnow = states('sensor.quarter_hourly_return_to_grid')|float(0)|round(2)  %}

{% set netmetering_temp = (totalnow|float(0)|round(2))-(returnnow|float(0)|round(2)) %}

{%- if (netmetering_temp|float(0)|round(2))  > 0 %}
{%-  set netmetering = returnnow %}
{%- else %}
{%-  set netmetering = totalnow %}
{%- endif %}

{% if (netmetering < returnnow) %}
{%  set injection = (returnnow - netmetering) %}
{% else %}
{%  set injection = 0 %}
{% endif %}

{%  set before = states('sensor.quarter_hourly_injection')|float(0)|round(2) %}
{%  set injection = before + injection  %}

{{ injection|float(0)|round(2) }}

After having the data on the Utility Meters, you can have some nice graphs (i use appexcharts):

This first image is a hourly graph like the one on the energy dashboard of HA but with netmetering and injection separated with colors (green and purple).


Code:

type: custom:apexcharts-card
graph_span: 24h
span:
  end: day
  offset: +0h
header:
  show: true
  title: Today Hourly kWh
yaxis:
  - decimals: 2
    apex_config:
      tickAmount: 30
    max: 2
    min: -1
apex_config:
  dataLabels:
    enabled: true
    formatter: |
      EVAL:function(value) {
        if (value <= 0.2){
          value = ''
        }
        return value;
      }
  annotations:
    position: front
    yaxis:
      - 'y': 0
        strokeDashArray: 0
        borderColor: '#FFFFFF'
        borderWidth: 1.5
  chart:
    height: 600
  stroke:
    width: 1.5
  grid:
    show: true
    borderColor: '#666666'
    strokeDashArray: 1
  xaxis:
    lines:
      show: true
    axisBorder:
      show: false
    labels:
      style:
        fontSize: 12px
  yaxis:
    axisBorder:
      show: false
  legend:
    show: true
    fontSize: 8px
    labels:
      style:
        fontSize: 5px
all_series_config:
  extend_to: now
  float_precision: 2
stacked: true
now:
  show: true
  color: '#FFFFFF'
  label: Now
series:
  - entity: sensor.daily_solar_used
    type: column
    name: Solar
    color: '#F4D03F'
    show:
      datalabels: false
      extremas: true
    group_by:
      func: delta
      duration: 1h
  - entity: sensor.daily_total_energy
    type: column
    name: Grid
    color: '#E74C3C'
    show:
      datalabels: false
      extremas: true
    group_by:
      func: delta
      duration: 1h
  - entity: sensor.quarter_hourly_netmetering_energy
    type: column
    name: Netmetering
    color: '#99FF00'
    show:
      datalabels: false
      extremas: true
    invert: true
    group_by:
      func: sum
      duration: 1h
  - entity: sensor.quarter_hourly_injection_energy
    type: column
    name: Injection
    color: '#E1BEE7'
    show:
      datalabels: false
      extremas: true
    invert: true
    group_by:
      func: sum
      duration: 1h

The second one is a quarter-hourly graph of mainly the netmetering and injection but with shaded solar used and grid used.


Code:

type: custom:apexcharts-card
graph_span: 12h
span:
  start: day
  offset: +7h
header:
  show: true
  title: Netmetering/Injection Quarterly-Hour kWh
yaxis:
  - decimals: 2
    apex_config:
      tickAmount: 5
    max: 0.5
    min: 0
apex_config:
  dataLabels:
    enabled: true
    formatter: |
      EVAL:function(value) {
        if (value <= 0.2){
          value = ''
        }
        return value;
      }
  annotations:
    position: front
    yaxis:
      - 'y': 0
        strokeDashArray: 0
        borderColor: '#FFFFFF'
        borderWidth: 1.5
  chart:
    height: 300
  stroke:
    width: 0
  grid:
    show: true
    borderColor: '#666666'
    strokeDashArray: 1
  xaxis:
    lines:
      show: true
    axisBorder:
      show: false
    labels:
      style:
        fontSize: 12px
  yaxis:
    axisBorder:
      show: false
  legend:
    show: true
    fontSize: 8px
    labels:
      style:
        fontSize: 5px
all_series_config:
  extend_to: now
  float_precision: 2
stacked: true
now:
  show: true
  color: '#FFFFFF'
  label: Now
series:
  - entity: sensor.quarter_hourly_netmetering_energy
    type: column
    name: Netmetering 15min
    color: '#99FF00'
    show:
      datalabels: false
      extremas: false
    group_by:
      func: max
      duration: 15min
  - entity: sensor.quarter_hourly_injection_energy
    type: column
    name: Injection 15min
    color: '#E1BEE7'
    show:
      datalabels: false
      extremas: false
    group_by:
      func: max
      duration: 15min
  - entity: sensor.quarter_hourly_solar_used_energy
    type: column
    name: Solar Used 15 min
    color: '#F4D03F'
    opacity: 0.2
    group_by:
      func: max
      duration: 15min
  - entity: sensor.quarter_hourly_total_energy
    type: column
    name: Grid 15 min
    color: '#E74C3C'
    opacity: 0.2
    group_by:
      func: max
      duration: 15min

Enjoy.
Pedro.

2 Likes

Hi, can you please explain where do I choice the shelly em sensor ?

The shelly sensor for Grid power is “sensor.total_power”. When positive you’re getting power froom the grid, when negative you’re sending to the grid and that’s what we need to know to get the netmetering.
(if it’s sending to the grid it should come from some source of power, usually solar).

Pedro.

1 Like

Thanks Pedro :slight_smile:

And what about sensor.total_energy its total energy measured by shelly ?

I used them both:

  • sensor.total_power - is the current power being used from the grid or sent to the grid at that moment. It is measured in watts.
  • sensor.total_energy is the energy consumed, normally in kilowatts per hour (kWh). For instance a heater can use 2000 W but if it is only used for 5 minutes it will only use 0.1667 kWh because kWh = (watts × hrs) ÷ 1,000. That is what Utitlity Meters use in HA.

Pedro.

Hi, I understand that this is an example, but for graphs i’m getting missng of sensors
daily_solar_used
daily_total_energy
quarter_hourly_injection_energy

can you help me how to create those sensors ?

Hi,

Thanks for your example. Very good.

Can you post the rest of it? What i mean is to use:
daily_solar_used
daily_total_energy
quarter_hourly_injection_energy

It would be very useful.

Thank you!

Did you get it to work? And if so, how?

Exactly what I’ve been looking for - will give this a try over the weekend. Obrigado!