Net metering with node-RED

Hello everyone, this is the first project I share with the community, as I am pretty much a noob in home assistant.

The challenge was to create a bunch of sensors providing sufficient information, to simulate Portuguese energy billing with photovoltaic injection, a 15 minute integration net-metering.

For this project I used a Shelly EM with a 50A clamp to measure power supplied and returned to the grid and communicate via MQTT.
For a sensor with net energy, I used the Riemann integration on the active power measurement ( "shellies/shellyem-xxxxxx/emeter/0/power"):

- platform: integration
  source: sensor.site_power
  name: riemann_net_energy
  method: trapezoidal
  unit_prefix: k
  round: 3

And used the utility meter integration to be able to reset the sensor every 15 minutes:

utility_meter:
  riemann_quarter_hourly_energy:
    source: sensor.riemann_net_energy
    cycle: quarter-hourly
    net_consumption: true

With these foundations, I jumped on Node-RED. After much research and testing, this is what I came up with:

I’ll try to explain the basics of this flow.

Every 15 minutes I send the value of the previous 15 minute energy, with the last_period attribute of the riemann_quarter_hourly_energy, to totaliser nodes (node-red-contrib-totaliser). Those nodes accumulate the input values and send the sum to entity nodes (custom integration of Node-RED required).

There are four entities for each tariff. Each one of those entities is reset at a given time - start of the day / month / year.

To solve the volatility of the totaliser nodes, I store the last value of each entity with persist nodes (node-red-contrib-persist) to load them in the totaliser at startup.

I then created a bunch of template sensors with energy costs:

- platform: template
 sensors:
       energy_cost_total_v:
     friendly_name: "Total Energy Cost V"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.net_metering_total_v') | float * 0.110577) | round(2) }}"
   energy_cost_year_v:
     friendly_name: "Year Energy Cost V"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.net_metering_yearly_v') | float * 0.110577) | round(2) }}"
   energy_cost_month_v:
     friendly_name: "Month Energy Cost V"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.net_metering_monthly_v') | float * 0.110577) | round(2) }}"
   energy_cost_day_v:
     friendly_name: "Day Energy Cost V"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.net_metering_daily_v') | float * 0.110577) | round(2) }}"
   energy_cost_total_fv:
     friendly_name: "Total Energy Cost FV"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.net_metering_total_fv') | float * 0.237636) | round(2) }}"
   energy_cost_year_fv:
     friendly_name: "Year Energy Cost FV"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.net_metering_yearly_fv') | float * 0.237636) | round(2) }}"
   energy_cost_month_fv:
     friendly_name: "Month Energy Cost FV"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.net_metering_monthly_fv') | float * 0.237636) | round(2) }}"
   energy_cost_day_fv:
     friendly_name: "Day Energy Cost"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.net_metering_daily_fv') | float * 0.237636) | round(2) }}"
   energy_cost_total:
     friendly_name: "Total Energy Cost"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.energy_cost_total_v') | float + states('sensor.energy_cost_total_fv') | float ) | round(2) }}"
   energy_cost_year:
     friendly_name: "Year Energy Cost"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.energy_cost_year_v') | float + states('sensor.energy_cost_year_fv') | float) | round(2) }}"
   energy_cost_month:
     friendly_name: "Month Energy Cost"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.energy_cost_month_v') | float + states('sensor.energy_cost_month_fv') | float) | round(2) }}"
   energy_cost_day:
     friendly_name: "Day Energy Cost"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.energy_cost_day_v') | float + states('sensor.energy_cost_day_fv') | float) | round(2) }}"

At the moment, it seems to be working fine.

One final note:

  • I tried using utility meter with the shelly’s energy sensor, but each time I reset HA, the first value is the total energy, instead of the difference from the last 15 minutes. There is a marginal error between the two approaches, but I think it’s not significant.

Future improvements / additions

  • A daily / monthly / yearly self consumption % of the solar energy;
  • Some financial indicators - ROI, estimate payback period;
  • Adding sensors for different tariff and time cycles as a way to determine the most economic solution;
  • A pretty dashboard.

I hope this post gave you some ideas or helped in any way and I’m aware my method is not the best nor the most optimized for this purpose and I appreciate your feedback, ideas and suggestions.

I leave you with the JSON for the Node-RED flow in the comments.

Here is the JSON (1/2):

[
    {
        "id": "7086a8e4.16a8f8",
        "type": "tab",
        "label": "Net Metering",
        "disabled": false,
        "info": ""
    },
    {
        "id": "62300ae1.7645c4",
        "type": "api-current-state",
        "z": "7086a8e4.16a8f8",
        "name": "15 min energy",
        "server": "56157d96.6354e4",
        "version": 2,
        "outputs": 1,
        "halt_if": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "entity_id": "sensor.riemann_quarter_hourly_energy",
        "state_type": "num",
        "blockInputOverrides": false,
        "outputProperties": [
            {
                "property": "payload",
                "propertyType": "msg",
                "value": "",
                "valueType": "entityState"
            },
            {
                "property": "data",
                "propertyType": "msg",
                "value": "",
                "valueType": "entity"
            }
        ],
        "override_topic": false,
        "state_location": "payload",
        "override_payload": "msg",
        "entity_location": "data",
        "override_data": "msg",
        "x": 420,
        "y": 380,
        "wires": [
            [
                "1ad7436e.f23b2d"
            ]
        ]
    },
    {
        "id": "5df63e1.a6459c",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "Convert to number",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "$number(payload)",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 390,
        "y": 460,
        "wires": [
            [
                "e2ba84de.a91c98"
            ]
        ]
    },
    {
        "id": "1ad7436e.f23b2d",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "last_period",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "data.attributes.last_period",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 190,
        "y": 460,
        "wires": [
            [
                "5df63e1.a6459c"
            ]
        ]
    },
    {
        "id": "175967a4.618ae8",
        "type": "totaliser",
        "z": "7086a8e4.16a8f8",
        "output": "total",
        "interval": "5",
        "intervalUnits": "seconds",
        "name": "Year Energy V",
        "x": 840,
        "y": 140,
        "wires": [
            [
                "ee9ae37c.c7051"
            ]
        ]
    },
    {
        "id": "edbc7e86.9fe3e",
        "type": "totaliser",
        "z": "7086a8e4.16a8f8",
        "output": "total",
        "interval": "5",
        "intervalUnits": "seconds",
        "name": "Month Energy V",
        "x": 840,
        "y": 200,
        "wires": [
            [
                "f774b664.860ef8"
            ]
        ]
    },
    {
        "id": "f8bedfbd.a3da3",
        "type": "totaliser",
        "z": "7086a8e4.16a8f8",
        "output": "total",
        "interval": "5",
        "intervalUnits": "seconds",
        "name": "Day Energy V",
        "x": 840,
        "y": 260,
        "wires": [
            [
                "a29e83f5.9d7bd"
            ]
        ]
    },
    {
        "id": "9a7e3b76.2fc538",
        "type": "cronplus",
        "z": "7086a8e4.16a8f8",
        "name": "Every 15min",
        "outputField": "payload",
        "timeZone": "",
        "persistDynamic": false,
        "commandResponseMsgOutput": "output1",
        "outputs": 1,
        "options": [
            {
                "name": "schedule1",
                "topic": "schedule1",
                "payloadType": "default",
                "payload": "",
                "expressionType": "cron",
                "expression": "1 15,30,45 * * * *",
                "location": "",
                "offset": "0",
                "solarType": "all",
                "solarEvents": "sunrise,sunset"
            }
        ],
        "x": 110,
        "y": 380,
        "wires": [
            [
                "2ca4b0ce.c26c6",
                "29eadcff.d72894"
            ]
        ]
    },
    {
        "id": "ee9ae37c.c7051",
        "type": "ha-entity",
        "z": "7086a8e4.16a8f8",
        "name": "Net_Metering_Yearly_V",
        "server": "56157d96.6354e4",
        "version": 1,
        "debugenabled": false,
        "outputs": 1,
        "entityType": "sensor",
        "config": [
            {
                "property": "name",
                "value": "net_metering_yearly_v"
            },
            {
                "property": "device_class",
                "value": "energy"
            },
            {
                "property": "icon",
                "value": ""
            },
            {
                "property": "unit_of_measurement",
                "value": "kWh"
            }
        ],
        "state": "payload.total",
        "stateType": "msg",
        "attributes": [],
        "resend": true,
        "outputLocation": "",
        "outputLocationType": "none",
        "inputOverride": "allow",
        "outputOnStateChange": false,
        "outputPayload": "$entity().state ? \"on\": \"off\"",
        "outputPayloadType": "jsonata",
        "x": 1070,
        "y": 140,
        "wires": [
            [
                "7c1ec256.b96f8c"
            ]
        ]
    },
    {
        "id": "f774b664.860ef8",
        "type": "ha-entity",
        "z": "7086a8e4.16a8f8",
        "name": "Net_Metering_Monthly_V",
        "server": "56157d96.6354e4",
        "version": 1,
        "debugenabled": false,
        "outputs": 1,
        "entityType": "sensor",
        "config": [
            {
                "property": "name",
                "value": "net_metering_monthly_v"
            },
            {
                "property": "device_class",
                "value": "energy"
            },
            {
                "property": "icon",
                "value": ""
            },
            {
                "property": "unit_of_measurement",
                "value": "kWh"
            }
        ],
        "state": "payload.total",
        "stateType": "msg",
        "attributes": [],
        "resend": true,
        "outputLocation": "",
        "outputLocationType": "none",
        "inputOverride": "allow",
        "outputOnStateChange": false,
        "outputPayload": "$entity().state ? \"on\": \"off\"",
        "outputPayloadType": "jsonata",
        "x": 1070,
        "y": 200,
        "wires": [
            [
                "4fd3648.16da69c"
            ]
        ]
    },
    {
        "id": "a29e83f5.9d7bd",
        "type": "ha-entity",
        "z": "7086a8e4.16a8f8",
        "name": "Net_Metering_Daily_V",
        "server": "56157d96.6354e4",
        "version": 1,
        "debugenabled": false,
        "outputs": 1,
        "entityType": "sensor",
        "config": [
            {
                "property": "name",
                "value": "net_metering_daily_v"
            },
            {
                "property": "device_class",
                "value": "energy"
            },
            {
                "property": "icon",
                "value": ""
            },
            {
                "property": "unit_of_measurement",
                "value": "kWh"
            }
        ],
        "state": "payload.total",
        "stateType": "msg",
        "attributes": [],
        "resend": true,
        "outputLocation": "",
        "outputLocationType": "none",
        "inputOverride": "allow",
        "outputOnStateChange": false,
        "outputPayload": "$entity().state ? \"on\": \"off\"",
        "outputPayloadType": "jsonata",
        "x": 1060,
        "y": 260,
        "wires": [
            [
                "6ce61a28.9ebe74"
            ]
        ]
    },
    {
        "id": "1f8d67ce.e2f8a8",
        "type": "totaliser",
        "z": "7086a8e4.16a8f8",
        "output": "total",
        "interval": "5",
        "intervalUnits": "seconds",
        "name": "Total Energy V",
        "x": 840,
        "y": 80,
        "wires": [
            [
                "e7b0a850.097a58"
            ]
        ]
    },
    {
        "id": "e7b0a850.097a58",
        "type": "ha-entity",
        "z": "7086a8e4.16a8f8",
        "name": "Net_Metering_Total_V",
        "server": "56157d96.6354e4",
        "version": 1,
        "debugenabled": false,
        "outputs": 1,
        "entityType": "sensor",
        "config": [
            {
                "property": "name",
                "value": "net_metering_total_v"
            },
            {
                "property": "device_class",
                "value": "energy"
            },
            {
                "property": "icon",
                "value": ""
            },
            {
                "property": "unit_of_measurement",
                "value": "kWh"
            }
        ],
        "state": "payload.total",
        "stateType": "msg",
        "attributes": [],
        "resend": true,
        "outputLocation": "",
        "outputLocationType": "none",
        "inputOverride": "allow",
        "outputOnStateChange": false,
        "outputPayload": "$entity().state ? \"on\": \"off\"",
        "outputPayloadType": "jsonata",
        "x": 1060,
        "y": 80,
        "wires": [
            [
                "ac43efcf.7fb26"
            ]
        ]
    },
    {
        "id": "b5211534.fdecb8",
        "type": "cronplus",
        "z": "7086a8e4.16a8f8",
        "name": "At 00:00",
        "outputField": "payload",
        "timeZone": "",
        "persistDynamic": false,
        "commandResponseMsgOutput": "output1",
        "outputs": 1,
        "options": [
            {
                "name": "schedule1",
                "topic": "schedule1",
                "payloadType": "default",
                "payload": "",
                "expressionType": "cron",
                "expression": "0 0 0 * * *",
                "location": "",
                "offset": "0",
                "solarType": "all",
                "solarEvents": "sunrise,sunset"
            }
        ],
        "x": 100,
        "y": 660,
        "wires": [
            [
                "13241d1a.784e23"
            ]
        ]
    },
    {
        "id": "13241d1a.784e23",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "Reset",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "reset",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 410,
        "y": 660,
        "wires": [
            [
                "f8bedfbd.a3da3",
                "57bae8a0.722ce8"
            ]
        ]
    },

Here is the JSON (2/2):

    {
        "id": "4cc3b69b.7526f8",
        "type": "cronplus",
        "z": "7086a8e4.16a8f8",
        "name": "Fist day of the month",
        "outputField": "payload",
        "timeZone": "",
        "persistDynamic": false,
        "commandResponseMsgOutput": "output1",
        "outputs": 1,
        "options": [
            {
                "name": "schedule1",
                "topic": "schedule1",
                "payloadType": "default",
                "payload": "",
                "expressionType": "cron",
                "expression": "0 0 0 1 * *",
                "location": "",
                "offset": "0",
                "solarType": "all",
                "solarEvents": "sunrise,sunset"
            }
        ],
        "x": 140,
        "y": 600,
        "wires": [
            [
                "216f5425.723abc"
            ]
        ]
    },
    {
        "id": "216f5425.723abc",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "Reset",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "reset",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 410,
        "y": 600,
        "wires": [
            [
                "edbc7e86.9fe3e",
                "9e7424d.ade43d8"
            ]
        ]
    },
    {
        "id": "4208a0b4.cc73c",
        "type": "cronplus",
        "z": "7086a8e4.16a8f8",
        "name": "First day of the year",
        "outputField": "payload",
        "timeZone": "",
        "persistDynamic": false,
        "commandResponseMsgOutput": "output1",
        "outputs": 1,
        "options": [
            {
                "name": "schedule1",
                "topic": "schedule1",
                "payloadType": "default",
                "payload": "",
                "expressionType": "cron",
                "expression": "0 0 0 1 1 *",
                "location": "",
                "offset": "0",
                "solarType": "all",
                "solarEvents": "sunrise,sunset"
            }
        ],
        "x": 140,
        "y": 540,
        "wires": [
            [
                "f7354d18.c2f7f"
            ]
        ]
    },
    {
        "id": "f7354d18.c2f7f",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "Reset",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "reset",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 410,
        "y": 540,
        "wires": [
            [
                "175967a4.618ae8",
                "11b95515.178cdb"
            ]
        ]
    },
    {
        "id": "b6fcc1f6.f2961",
        "type": "persist in",
        "z": "7086a8e4.16a8f8",
        "name": "Total Energy V",
        "storageNode": "f69cfabd.8fa068",
        "x": 1420,
        "y": 80,
        "wires": []
    },
    {
        "id": "fc91bc80.4e93e",
        "type": "persist in",
        "z": "7086a8e4.16a8f8",
        "name": "Year Energy V",
        "storageNode": "f69cfabd.8fa068",
        "x": 1420,
        "y": 140,
        "wires": []
    },
    {
        "id": "b540468f.dac058",
        "type": "persist in",
        "z": "7086a8e4.16a8f8",
        "name": "Month Energy V",
        "storageNode": "f69cfabd.8fa068",
        "x": 1420,
        "y": 200,
        "wires": []
    },
    {
        "id": "4a807607.58d118",
        "type": "persist in",
        "z": "7086a8e4.16a8f8",
        "name": "Day Energy V",
        "storageNode": "f69cfabd.8fa068",
        "x": 1420,
        "y": 260,
        "wires": []
    },
    {
        "id": "6b150f6e.0fe9",
        "type": "inject",
        "z": "7086a8e4.16a8f8",
        "name": "At startup",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": "5",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 90,
        "y": 160,
        "wires": [
            [
                "e040e281.37a63",
                "e9eefd7b.fc2cb",
                "3719be70.5fee12",
                "87047654.a617f8"
            ]
        ]
    },
    {
        "id": "e040e281.37a63",
        "type": "persist out",
        "z": "7086a8e4.16a8f8",
        "name": "Total Energy V",
        "storageNode": "f69cfabd.8fa068",
        "x": 500,
        "y": 80,
        "wires": [
            [
                "1f8d67ce.e2f8a8"
            ]
        ]
    },
    {
        "id": "e9eefd7b.fc2cb",
        "type": "persist out",
        "z": "7086a8e4.16a8f8",
        "name": "Year Energy V",
        "storageNode": "f69cfabd.8fa068",
        "x": 500,
        "y": 140,
        "wires": [
            [
                "175967a4.618ae8"
            ]
        ]
    },
    {
        "id": "3719be70.5fee12",
        "type": "persist out",
        "z": "7086a8e4.16a8f8",
        "name": "Month Energy V",
        "storageNode": "f69cfabd.8fa068",
        "x": 500,
        "y": 200,
        "wires": [
            [
                "edbc7e86.9fe3e"
            ]
        ]
    },
    {
        "id": "87047654.a617f8",
        "type": "persist out",
        "z": "7086a8e4.16a8f8",
        "name": "Day Energy V",
        "storageNode": "f69cfabd.8fa068",
        "x": 500,
        "y": 260,
        "wires": [
            [
                "f8bedfbd.a3da3"
            ]
        ]
    },
    {
        "id": "ac43efcf.7fb26",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "total",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.total",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1250,
        "y": 80,
        "wires": [
            [
                "b6fcc1f6.f2961"
            ]
        ]
    },
    {
        "id": "7c1ec256.b96f8c",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "total",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.total",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1250,
        "y": 140,
        "wires": [
            [
                "fc91bc80.4e93e"
            ]
        ]
    },
    {
        "id": "4fd3648.16da69c",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "total",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.total",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1250,
        "y": 200,
        "wires": [
            [
                "b540468f.dac058"
            ]
        ]
    },
    {
        "id": "6ce61a28.9ebe74",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "total",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.total",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1250,
        "y": 260,
        "wires": [
            [
                "4a807607.58d118"
            ]
        ]
    },
    {
        "id": "11b95515.178cdb",
        "type": "totaliser",
        "z": "7086a8e4.16a8f8",
        "output": "total",
        "interval": "5",
        "intervalUnits": "seconds",
        "name": "Year Energy FV",
        "x": 820,
        "y": 820,
        "wires": [
            [
                "3dd634c7.d98c1c"
            ]
        ]
    },
    {
        "id": "9e7424d.ade43d8",
        "type": "totaliser",
        "z": "7086a8e4.16a8f8",
        "output": "total",
        "interval": "5",
        "intervalUnits": "seconds",
        "name": "Month Energy FV",
        "x": 830,
        "y": 880,
        "wires": [
            [
                "cfb6754b.e581d8"
            ]
        ]
    },
    {
        "id": "57bae8a0.722ce8",
        "type": "totaliser",
        "z": "7086a8e4.16a8f8",
        "output": "total",
        "interval": "5",
        "intervalUnits": "seconds",
        "name": "Day Energy FV",
        "x": 820,
        "y": 940,
        "wires": [
            [
                "899578f8.60eb78"
            ]
        ]
    },
    {
        "id": "3dd634c7.d98c1c",
        "type": "ha-entity",
        "z": "7086a8e4.16a8f8",
        "name": "Net_Metering_Yearly_FV",
        "server": "56157d96.6354e4",
        "version": 1,
        "debugenabled": false,
        "outputs": 1,
        "entityType": "sensor",
        "config": [
            {
                "property": "name",
                "value": "net_metering_yearly_fv"
            },
            {
                "property": "device_class",
                "value": "energy"
            },
            {
                "property": "icon",
                "value": ""
            },
            {
                "property": "unit_of_measurement",
                "value": "kWh"
            }
        ],
        "state": "payload.total",
        "stateType": "msg",
        "attributes": [],
        "resend": true,
        "outputLocation": "",
        "outputLocationType": "none",
        "inputOverride": "allow",
        "outputOnStateChange": false,
        "outputPayload": "$entity().state ? \"on\": \"off\"",
        "outputPayloadType": "jsonata",
        "x": 1050,
        "y": 820,
        "wires": [
            [
                "e70c873c.784458"
            ]
        ]
    },
    {
        "id": "cfb6754b.e581d8",
        "type": "ha-entity",
        "z": "7086a8e4.16a8f8",
        "name": "Net_Metering_Monthly_FV",
        "server": "56157d96.6354e4",
        "version": 1,
        "debugenabled": false,
        "outputs": 1,
        "entityType": "sensor",
        "config": [
            {
                "property": "name",
                "value": "net_metering_monthly_fv"
            },
            {
                "property": "device_class",
                "value": "energy"
            },
            {
                "property": "icon",
                "value": ""
            },
            {
                "property": "unit_of_measurement",
                "value": "kWh"
            }
        ],
        "state": "payload.total",
        "stateType": "msg",
        "attributes": [],
        "resend": true,
        "outputLocation": "",
        "outputLocationType": "none",
        "inputOverride": "allow",
        "outputOnStateChange": false,
        "outputPayload": "$entity().state ? \"on\": \"off\"",
        "outputPayloadType": "jsonata",
        "x": 1060,
        "y": 880,
        "wires": [
            [
                "a0705be0.0496f8"
            ]
        ]
    },
    {
        "id": "899578f8.60eb78",
        "type": "ha-entity",
        "z": "7086a8e4.16a8f8",
        "name": "Net_Metering_Daily_FV",
        "server": "56157d96.6354e4",
        "version": 1,
        "debugenabled": false,
        "outputs": 1,
        "entityType": "sensor",
        "config": [
            {
                "property": "name",
                "value": "net_metering_daily_fv"
            },
            {
                "property": "device_class",
                "value": "energy"
            },
            {
                "property": "icon",
                "value": ""
            },
            {
                "property": "unit_of_measurement",
                "value": "kWh"
            }
        ],
        "state": "payload.total",
        "stateType": "msg",
        "attributes": [],
        "resend": true,
        "outputLocation": "",
        "outputLocationType": "none",
        "inputOverride": "allow",
        "outputOnStateChange": false,
        "outputPayload": "$entity().state ? \"on\": \"off\"",
        "outputPayloadType": "jsonata",
        "x": 1050,
        "y": 940,
        "wires": [
            [
                "1fc542b2.53b63d"
            ]
        ]
    },
    {
        "id": "5256be29.98ca8",
        "type": "totaliser",
        "z": "7086a8e4.16a8f8",
        "output": "total",
        "interval": "5",
        "intervalUnits": "seconds",
        "name": "Total Energy FV",
        "x": 820,
        "y": 760,
        "wires": [
            [
                "8ba166a1.39f9a8"
            ]
        ]
    },
    {
        "id": "8ba166a1.39f9a8",
        "type": "ha-entity",
        "z": "7086a8e4.16a8f8",
        "name": "Net_Metering_Total_FV",
        "server": "56157d96.6354e4",
        "version": 1,
        "debugenabled": false,
        "outputs": 1,
        "entityType": "sensor",
        "config": [
            {
                "property": "name",
                "value": "net_metering_total_fv"
            },
            {
                "property": "device_class",
                "value": "energy"
            },
            {
                "property": "icon",
                "value": ""
            },
            {
                "property": "unit_of_measurement",
                "value": "kWh"
            }
        ],
        "state": "payload.total",
        "stateType": "msg",
        "attributes": [],
        "resend": true,
        "outputLocation": "",
        "outputLocationType": "none",
        "inputOverride": "allow",
        "outputOnStateChange": false,
        "outputPayload": "$entity().state ? \"on\": \"off\"",
        "outputPayloadType": "jsonata",
        "x": 1050,
        "y": 760,
        "wires": [
            [
                "a8ae78a2.cc9eb8"
            ]
        ]
    },
    {
        "id": "2e0d91f0.c63dde",
        "type": "persist in",
        "z": "7086a8e4.16a8f8",
        "name": "Total Energy FV",
        "storageNode": "f69cfabd.8fa068",
        "x": 1440,
        "y": 760,
        "wires": []
    },
    {
        "id": "341a1b24.396344",
        "type": "persist in",
        "z": "7086a8e4.16a8f8",
        "name": "Year Energy FV",
        "storageNode": "f69cfabd.8fa068",
        "x": 1440,
        "y": 820,
        "wires": []
    },
    {
        "id": "81372d36.c01ec",
        "type": "persist in",
        "z": "7086a8e4.16a8f8",
        "name": "Month Energy FV",
        "storageNode": "f69cfabd.8fa068",
        "x": 1450,
        "y": 880,
        "wires": []
    },
    {
        "id": "6b9cf5bd.f7953c",
        "type": "persist in",
        "z": "7086a8e4.16a8f8",
        "name": "Day Energy FV",
        "storageNode": "f69cfabd.8fa068",
        "x": 1440,
        "y": 940,
        "wires": []
    },
    {
        "id": "2057b196.d7e0ee",
        "type": "inject",
        "z": "7086a8e4.16a8f8",
        "name": "At startup",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": "5",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 90,
        "y": 840,
        "wires": [
            [
                "b064e3f7.ae9f8",
                "537fcd87.462fd4",
                "2819c996.dd1326",
                "d18d6b56.051888"
            ]
        ]
    },
    {
        "id": "b064e3f7.ae9f8",
        "type": "persist out",
        "z": "7086a8e4.16a8f8",
        "name": "Total Energy FV",
        "storageNode": "f69cfabd.8fa068",
        "x": 320,
        "y": 760,
        "wires": [
            [
                "5256be29.98ca8"
            ]
        ]
    },
    {
        "id": "537fcd87.462fd4",
        "type": "persist out",
        "z": "7086a8e4.16a8f8",
        "name": "Year Energy FV",
        "storageNode": "f69cfabd.8fa068",
        "x": 320,
        "y": 820,
        "wires": [
            [
                "11b95515.178cdb"
            ]
        ]
    },
    {
        "id": "2819c996.dd1326",
        "type": "persist out",
        "z": "7086a8e4.16a8f8",
        "name": "Month Energy FV",
        "storageNode": "f69cfabd.8fa068",
        "x": 330,
        "y": 880,
        "wires": [
            [
                "9e7424d.ade43d8"
            ]
        ]
    },
    {
        "id": "d18d6b56.051888",
        "type": "persist out",
        "z": "7086a8e4.16a8f8",
        "name": "Day Energy FV",
        "storageNode": "f69cfabd.8fa068",
        "x": 320,
        "y": 940,
        "wires": [
            [
                "57bae8a0.722ce8"
            ]
        ]
    },
    {
        "id": "a8ae78a2.cc9eb8",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "total",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.total",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1270,
        "y": 760,
        "wires": [
            [
                "2e0d91f0.c63dde"
            ]
        ]
    },
    {
        "id": "e70c873c.784458",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "total",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.total",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1270,
        "y": 820,
        "wires": [
            [
                "341a1b24.396344"
            ]
        ]
    },
    {
        "id": "a0705be0.0496f8",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "total",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.total",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1270,
        "y": 880,
        "wires": [
            [
                "81372d36.c01ec"
            ]
        ]
    },
    {
        "id": "1fc542b2.53b63d",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "total",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.total",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1270,
        "y": 940,
        "wires": [
            [
                "6b9cf5bd.f7953c"
            ]
        ]
    },
    {
        "id": "e2ba84de.a91c98",
        "type": "switch",
        "z": "7086a8e4.16a8f8",
        "name": "Vazio",
        "property": "vazio",
        "propertyType": "flow",
        "rules": [
            {
                "t": "eq",
                "v": "on",
                "vt": "str"
            },
            {
                "t": "neq",
                "v": "on",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 570,
        "y": 460,
        "wires": [
            [
                "1f8d67ce.e2f8a8",
                "175967a4.618ae8",
                "edbc7e86.9fe3e",
                "f8bedfbd.a3da3"
            ],
            [
                "5256be29.98ca8",
                "11b95515.178cdb",
                "9e7424d.ade43d8",
                "57bae8a0.722ce8"
            ]
        ]
    },
    {
        "id": "2ca4b0ce.c26c6",
        "type": "api-current-state",
        "z": "7086a8e4.16a8f8",
        "name": "Vazio",
        "server": "56157d96.6354e4",
        "version": 2,
        "outputs": 1,
        "halt_if": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "entity_id": "input_boolean.horario_vazio",
        "state_type": "str",
        "blockInputOverrides": false,
        "outputProperties": [
            {
                "property": "payload",
                "propertyType": "msg",
                "value": "",
                "valueType": "entityState"
            },
            {
                "property": "data",
                "propertyType": "msg",
                "value": "",
                "valueType": "entity"
            }
        ],
        "override_topic": false,
        "state_location": "payload",
        "override_payload": "msg",
        "entity_location": "data",
        "override_data": "msg",
        "x": 270,
        "y": 320,
        "wires": [
            [
                "3f8dcb46.1da394"
            ]
        ]
    },
    {
        "id": "29eadcff.d72894",
        "type": "delay",
        "z": "7086a8e4.16a8f8",
        "name": "100ms",
        "pauseType": "delay",
        "timeout": "100",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "x": 270,
        "y": 380,
        "wires": [
            [
                "62300ae1.7645c4"
            ]
        ]
    },
    {
        "id": "3f8dcb46.1da394",
        "type": "change",
        "z": "7086a8e4.16a8f8",
        "name": "flow.vazio",
        "rules": [
            {
                "t": "set",
                "p": "vazio",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 420,
        "y": 320,
        "wires": [
            []
        ]
    },
    {
        "id": "56157d96.6354e4",
        "type": "server",
        "name": "Home Assistant",
        "version": 1,
        "legacy": false,
        "addon": true,
        "rejectUnauthorizedCerts": true,
        "ha_boolean": "y|yes|true|on|home|open",
        "connectionDelay": true,
        "cacheJson": true
    },
    {
        "id": "f69cfabd.8fa068",
        "type": "persist-store",
        "filename": "persistence.json",
        "interval": "900"
    }
]

You need to add availability templates to your template sensors that you use in the utility meters.

When HA starts the sensors in the templates may be ‘unavailable’ which is converted to 0 by the |float filters. The utility meter then sees the jump from 0 to the value, doubling its total.

e.g.

sensors:
   energy_cost_total_v:
     friendly_name: "Total Energy Cost V"
     unit_of_measurement: '€'
     value_template: "{{ (states('sensor.net_metering_total_v') | float * 0.110577) | round(2) }}"
     availability_template: "{{ not is_state('sensor.net_metering_total_v', 'unavailable') }}"

Thanks for the input @tom_l .
In fact, I noticed the doubling of values, but I don’t think it’s related to the utility meter, as I am using power sensors.
My workaround was blocking the startup inject node when the value of the totalizer differs from zero.


image

The issue with the utility meters using energy sensors is that the first value after HA reset is the total energy since the beggining. It’s computing the difference between the accumulated energy and zero.

Anyway, I guess it should work with the utility meter. Something like this?

sensor:
  - platform: template
    sensors:
      net_energy:
        friendly_name: "Net Energy"
        unit_of_measurement: 'kWh'
        value_template: "{{ ( states('sensor.site_total_energy') |float - states('sensor.site_total_returned_energy') | float ) | round(3) }}"
        availability_template: "{{ not is_state('sensor.site_total_energy', 'unavailable') or not is_state('sensor.site_total_returned_energy', 'unavailable') }}"

utility_meter:
  daily_energy:
    source: sensor.net_energy
    cycle: daily
    tariffs:
      - peak
      - offpeak
    net_consumption: true

Hi,
Thanks for this i’m trying to do the same with my new Shelly EM i have utility meter sensors set up with my peak and off peak and the automation to change them.
So now i’m wanting to calculate the costs like you have but in GBP and have the following but i dont think i have the price set right could you confirm for me
My elec rates are
23.25p per kWh peak
12.78p per kWh offpeak
and i have a daily standing charge of 24.47p but i’m not actually sure how you enter that into the code as the prices seem odd like the total for the week is showing “36,386.13 £”
Thanks for any help

- platform: template
  sensors:
    daily_energy:
      unique_id: daily_energy
      friendly_name: Daily Energy
      unit_of_measurement: kWh
      value_template: "{{ states('sensor.daily_energy')|float }}"
    weekly_energy:
      unique_id: weekly_energy
      friendly_name: Weekly Energy
      unit_of_measurement: kWh
      value_template: "{{ states('sensor.weekly_energy')|float }}"
    monthly_energy:
      unique_id: monthly_energy
      friendly_name: Monthly Energy
      unit_of_measurement: kWh
      value_template: "{{ states('sensor.montly_energy')|float }}"
    energy_cost_today:
      unique_id: energy_cost_today
      friendly_name: "Energy Cost Today"
      unit_of_measurement: '£'
      icon_template: mdi:currency-gbp
      value_template: "{{ (((states('sensor.daily_energy_peak') | float * 0.2325) + (states('sensor.daily_energy_offpeak') | float) * 0.1278 ) + 0.2447) | round(2) }}"
    energy_cost_this_week:
      unique_id: energy_cost_this_week
      friendly_name: "Energy Cost This Week"
      unit_of_measurement: '£'
      icon_template: mdi:currency-gbp
      value_template: "{{ ((states('sensor.weekly_energy_peak') | float * 0.2325) + (states('sensor.weekly_energy_offpeak') | float) * 0.1278 ) | round(2) }}" 
    energy_cost_this_month:
      unique_id: energy_cost_this_month
      friendly_name: "Energy Cost This Month"
      unit_of_measurement: '£'
      icon_template: mdi:currency-gbp
      value_template: "{{ ((states('sensor.monthly_energy_peak') | float * 0.2325) + (states('sensor.monthly_energy_offpeak') | float) * 0.1278 ) | round(2) }}"

Hi Steve, glad to help.

Are you using power or energy data from shelly? They have energy counter in Watt-minute, if you want to use the energy counter you have to correct the value:

value_template: "{{ (value|float / 60000) | round(3)}}"

I’m actually using a riemann integrator with the power readings, as I find it more stable.

For the daily standing charge, you can create an input number with a stepping of 0.2447 and increment it daily with an automation, for example. I have yet to include the daily rate in my costs, let me know if you found a better solution.

Thanks,
yeah I’ve been using the energy readings as everywhere I read says to use them over power but I’ll set up the power alongside it to compare.
Where are you adding that value template ? In with one of your sensors or on its own.
I’ve been reading about the riemann integrator as well but couldn’t fully understand it lol.
With my standing charge I’ve just added the amounts to each sensor with the monthly average for my charges and that amount gets added on when the sensor rolls over.
With the £ symbol I’ve now removed the unit of measurement line and just put a £ symbol at the start.
Do you ever have any issues with the Shelly Em ? I’ve had the web UI fail to load a few times and mqtt stops reporting, but oddly the device is still online and the integration sensors keep updating.

value_template: "£{{ ((states('sensor.monthly_energy_peak') | float * 0.2325) + (states('sensor.monthly_energy_offpeak') | float) * 0.1278 ) + 7.072) | round(2) }}"

yeah I’ve been using the energy readings as everywhere I read says to use them over power but I’ll set up the power alongside it to compare.

I’ve had some issues with the energy sensor, and for my case, the error of the Integrator is negligable. I encorage you to compare them both and see if it works for you.

Where are you adding that value template ? In with one of your sensors or on its own.

I put the value template in the MQTT entity:

- platform: mqtt                                                                #Water_Heater_Energy
  name: "Water_Heater_Energy"
  device_class: energy
  state_topic: "shellies/shelly1pm-E8DB8----/relay/0/energy"                 #energy counter in Watt-minute
  value_template: "{{ (value|float / 60000) | round(3)}}"                                   #Fix for Watt-minutes
  qos: 1
  unit_of_measurement: "kWh"

In this topic, tom_l gives some nice info on the matter:

Configuring Shelly EM Energy Meter

I’ve been reading about the riemann integrator as well but couldn’t fully understand it lol.

Riemann integrator takes values from a sensor and gives you an approximation of the integral of that data.
For instance, for each value from the sensor, the trapezoidal method takes averages with the previous value, multiplies the result by the delay between and adds the result to itself.
The other methods simply takes the first (left) or last (right) value of the two instead of the average.

The following code takes a power sensor in Watt (site_power) and returns a energy sensor in kWh:

- platform: integration                                                         #riemann_net_energy
  source: sensor.site_power
  name: riemann_net_energy
  method: trapezoidal
  unit_prefix: k
  round: 3

With my standing charge I’ve just added the amounts to each sensor with the monthly average for my charges and that amount gets added on when the sensor rolls over.

So it only updates monthly?

With the £ symbol I’ve now removed the unit of measurement line and just put a £ symbol at the start.

It’s a workaround, but if you want to do some work with grafana, you’d benefit from the unit of measurement. I have no issue using ‘€’

Do you ever have any issues with the Shelly Em ? I’ve had the web UI fail to load a few times and mqtt stops reporting, but oddly the device is still online and the integration sensors keep updating.

Never had any issue with shelly. Since I’m using it without the cloud, the shelly app takes some time to retrieve values, but in home assistant it’s quite robust.

Thanks for the explanation of the Riemann integrator, I’ve now created the sensor for it should i now use that sensor with Utility meter instead of the Shelly power sensor ?

No sorry only that monthly sensor updates monthly.
I’ve now set up 3 sensors that add up my peak & off peak per day/week/month

     energy_cost_today:
        unique_id: energy_cost_today
        friendly_name: "Energy Cost Today"
        unit_of_measurement: '£'
        icon_template: mdi:currency-gbp
        value_template: "{{ (((states('sensor.daily_energy_peak') | float * 0.2214) + (states('sensor.daily_energy_offpeak') | float) * 0.1217 ) + 0.2330) | round(2) }}"
      energy_cost_this_week:
        unique_id: energy_cost_this_week
        friendly_name: "Energy Cost This Week"
        unit_of_measurement: '£'
        icon_template: mdi:currency-gbp
        value_template: "{{ (((states('sensor.weekly_energy_peak') | float * 0.2214) + (states('sensor.weekly_energy_offpeak') | float) * 0.1217 ) + 1.63) | round(2) }}" 
      energy_cost_this_month:
        unique_id: energy_cost_this_month
        friendly_name: "Energy Cost This Month"
        unit_of_measurement: '£'
        icon_template: mdi:currency-gbp
        value_template: "{{ (((states('sensor.monthly_energy_peak') | float * 0.2214) + (states('sensor.monthly_energy_offpeak') | float) * 0.1217 ) + 7.087) | round(2) }}"

I tried again creating the MQTT sensor above but it’s just so unreliable for me with this Shelly Em, the sensor created fine and was reporting but then stopped reporting for nearly 5 minutes even in MQTT explorer and then would come back online, I’m thinking its a WiFi issue as the Shelly is right on the edge of my WiFi range but I’m buying some better quality access points soon so hopefully that will sort that.

I use the Shelly Power into Riemann into Utility meter.
As I said, never had a communication issue with Shelly EM, I find MQTT faster and more reliable than the integration, but have a strong wifi signal. If you fix that it should work fine.

You could improve the weekly sensor by including the weekday in the calculation. {{ now().weekday() }} gives a value for each day of the week (Sunday = 0). You could tweek that to your needs.

Also, {{ now().day }} for the montly sensor.

Something like:

value_template: "{{ (((states('sensor.monthly_energy_peak') | float * 0.2214) + states('sensor.monthly_energy_offpeak') | float) * 0.1217 ) + 0.2330 * now().day) | round(2) }}"

Also would create input numbers for the prices, so when they change you only have to change in one place.

1 Like

Thanks I’ll try them out.

David,

Great work. I’ve done something like you did but probably in a simpler way. If you want you can take a look here.

I don’t know if you still use the same flow but you can probably make it a little simpler, if you only want to calculate the netmetering value.

Pedro.