It seems that I’ve managed to find a very simple solution - only “downside” is that it works with Node Red.
Basically Node Red calculates the kWh by looking at the duration of the last state (on or off) of a switch, sends this value via MQTT to a sensor in Home Assistant, which gets read by the Utility Meter and added up.
First you need to make a flow like that :
Here’s the flow to copy:
[
{
"id": "82770b78.c61bb8",
"type": "server-state-changed",
"z": "3c74f436.9e051c",
"name": "XYZ ON",
"server": "938bbb0d.d39038",
"version": 1,
"exposeToHomeAssistant": false,
"haConfig": [
{
"property": "name",
"value": ""
},
{
"property": "icon",
"value": ""
}
],
"entityidfilter": "switch.xyz",
"entityidfiltertype": "exact",
"outputinitially": false,
"state_type": "str",
"haltifstate": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"outputs": 1,
"output_only_on_state_change": true,
"x": 100,
"y": 1500,
"wires": [
[
"451731a4.89d82"
]
]
},
{
"id": "2ef96586.96ce9a",
"type": "interval-length",
"z": "3c74f436.9e051c",
"format": "mills",
"bytopic": false,
"minimum": "",
"maximum": "",
"window": "",
"timeout": false,
"msgTimeout": "",
"minimumunit": "msecs",
"maximumunit": "msecs",
"windowunit": "msecs",
"msgTimeoutUnit": "msecs",
"reset": false,
"startup": false,
"msgField": "payload",
"timestampField": "timestamp",
"repeatTimeout": false,
"name": "",
"x": 480,
"y": 1500,
"wires": [
[
"3b31d530.44a95a",
"afc1d974.630488"
],
[]
]
},
{
"id": "3b31d530.44a95a",
"type": "api-current-state",
"z": "3c74f436.9e051c",
"name": "OFF",
"server": "938bbb0d.d39038",
"version": 1,
"outputs": 2,
"halt_if": "off",
"halt_if_type": "str",
"halt_if_compare": "is",
"override_topic": false,
"entity_id": "switch.xyz",
"state_type": "str",
"state_location": "",
"override_payload": "none",
"entity_location": "",
"override_data": "none",
"blockInputOverrides": false,
"x": 670,
"y": 1440,
"wires": [
[
"caf894a8.d73fb8",
"bbd5ce79.d3dfe"
],
[]
]
},
{
"id": "afc1d974.630488",
"type": "api-current-state",
"z": "3c74f436.9e051c",
"name": "ON",
"server": "938bbb0d.d39038",
"version": 1,
"outputs": 2,
"halt_if": "on",
"halt_if_type": "str",
"halt_if_compare": "is",
"override_topic": false,
"entity_id": "switch.xyz",
"state_type": "str",
"state_location": "",
"override_payload": "none",
"entity_location": "",
"override_data": "none",
"blockInputOverrides": false,
"x": 670,
"y": 1560,
"wires": [
[
"ce50d3fe.fae1c",
"579fecb9.d13fd4"
],
[]
]
},
{
"id": "451731a4.89d82",
"type": "switch",
"z": "3c74f436.9e051c",
"name": "",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "on",
"vt": "str"
},
{
"t": "eq",
"v": "off",
"vt": "str"
},
{
"t": "eq",
"v": "unavailable",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 3,
"x": 310,
"y": 1500,
"wires": [
[
"2ef96586.96ce9a"
],
[
"2ef96586.96ce9a"
],
[]
]
},
{
"id": "d3430473.57f0a8",
"type": "function",
"z": "3c74f436.9e051c",
"name": "kWh",
"func": "\nmsg.payload = (msg.payload[1] * 0.00000027777778) * (msg.payload[0] / 1000);\n\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 1430,
"y": 1440,
"wires": [
[
"683e95e4.7b8e0c",
"fe4ed983.e12f48"
]
]
},
{
"id": "4deac3b8.2f902c",
"type": "join",
"z": "3c74f436.9e051c",
"name": "",
"mode": "custom",
"build": "array",
"property": "payload",
"propertyType": "msg",
"key": "topic",
"joiner": "\\n",
"joinerType": "str",
"accumulate": false,
"timeout": "",
"count": "2",
"reduceRight": false,
"reduceExp": "",
"reduceInit": "",
"reduceInitType": "",
"reduceFixup": "",
"x": 1270,
"y": 1440,
"wires": [
[
"d3430473.57f0a8"
]
]
},
{
"id": "caf894a8.d73fb8",
"type": "api-current-state",
"z": "3c74f436.9e051c",
"name": "XYZ Watt",
"server": "938bbb0d.d39038",
"version": 1,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"override_topic": false,
"entity_id": "input_number.xyz_watt",
"state_type": "num",
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"blockInputOverrides": false,
"x": 1060,
"y": 1400,
"wires": [
[
"4deac3b8.2f902c"
]
]
},
{
"id": "bbd5ce79.d3dfe",
"type": "delay",
"z": "3c74f436.9e051c",
"name": "",
"pauseType": "delay",
"timeout": "5",
"timeoutUnits": "milliseconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"x": 1050,
"y": 1460,
"wires": [
[
"4deac3b8.2f902c"
]
]
},
{
"id": "f46f070a.905bb8",
"type": "function",
"z": "3c74f436.9e051c",
"name": "kWh",
"func": "\nmsg.payload = (msg.payload[1] * 0.00000027777778) * (msg.payload[0] / 1000);\n\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 1430,
"y": 1560,
"wires": [
[
"683e95e4.7b8e0c",
"82898f2c.ddf6d"
]
]
},
{
"id": "c55b5515.fe1ba8",
"type": "join",
"z": "3c74f436.9e051c",
"name": "",
"mode": "custom",
"build": "array",
"property": "payload",
"propertyType": "msg",
"key": "topic",
"joiner": "\\n",
"joinerType": "str",
"accumulate": false,
"timeout": "",
"count": "2",
"reduceRight": false,
"reduceExp": "",
"reduceInit": "",
"reduceInitType": "",
"reduceFixup": "",
"x": 1270,
"y": 1560,
"wires": [
[
"f46f070a.905bb8"
]
]
},
{
"id": "ce50d3fe.fae1c",
"type": "api-current-state",
"z": "3c74f436.9e051c",
"name": "Switch standby Watt",
"server": "938bbb0d.d39038",
"version": 1,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"override_topic": false,
"entity_id": "input_number.switch_standby_watt",
"state_type": "num",
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"blockInputOverrides": false,
"x": 1040,
"y": 1540,
"wires": [
[
"c55b5515.fe1ba8"
]
]
},
{
"id": "579fecb9.d13fd4",
"type": "delay",
"z": "3c74f436.9e051c",
"name": "",
"pauseType": "delay",
"timeout": "5",
"timeoutUnits": "milliseconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"x": 1050,
"y": 1600,
"wires": [
[
"c55b5515.fe1ba8"
]
]
},
{
"id": "683e95e4.7b8e0c",
"type": "mqtt out",
"z": "3c74f436.9e051c",
"name": "",
"topic": "power_consumption/kWh/xyz",
"qos": "0",
"retain": "true",
"broker": "bda1e0cb.3e2458",
"x": 1840,
"y": 1500,
"wires": []
},
{
"id": "fe4ed983.e12f48",
"type": "delay",
"z": "3c74f436.9e051c",
"name": "",
"pauseType": "delay",
"timeout": "50",
"timeoutUnits": "milliseconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"x": 1590,
"y": 1400,
"wires": [
[
"eaf71cf9.23401"
]
]
},
{
"id": "eaf71cf9.23401",
"type": "change",
"z": "3c74f436.9e051c",
"name": "back to zero",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "0",
"tot": "num"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1790,
"y": 1400,
"wires": [
[
"683e95e4.7b8e0c"
]
]
},
{
"id": "82898f2c.ddf6d",
"type": "delay",
"z": "3c74f436.9e051c",
"name": "",
"pauseType": "delay",
"timeout": "50",
"timeoutUnits": "milliseconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"x": 1590,
"y": 1600,
"wires": [
[
"fb35bf26.9afc1"
]
]
},
{
"id": "fb35bf26.9afc1",
"type": "change",
"z": "3c74f436.9e051c",
"name": "back to zero",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "0",
"tot": "num"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1790,
"y": 1600,
"wires": [
[
"683e95e4.7b8e0c"
]
]
},
{
"id": "d70f995d.901bc8",
"type": "comment",
"z": "3c74f436.9e051c",
"name": "Theoretical Power Consumption",
"info": "",
"x": 170,
"y": 1340,
"wires": []
},
{
"id": "938bbb0d.d39038",
"type": "server",
"z": "",
"name": "Home Assistant"
},
{
"id": "bda1e0cb.3e2458",
"type": "mqtt-broker",
"z": "",
"name": "[email protected]",
"broker": "10.127.20.238",
"port": "1883",
"clientid": "",
"usetls": false,
"compatmode": true,
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"willTopic": "",
"willQos": "0",
"willPayload": ""
}
]
EDIT: You have to insert an rbe node between the switch and the interval length, so if the switch goes to unavailable and back to the previous state, it is not taken into the calculation
The reason I set the kWh back to zero imediately in the flow is because the utility meter only adds positive changes from a sensor. So only this way the difference from the last value to the current is the actual consumption
Now you need a MQTT sensor to recieve the values:
- platform: mqtt
name: "XYZ Power Consumption"
state_topic: "power_consumption/kWh/xyz"
unit_of_measurement: 'kWh'
icon: 'mdi:flash'
and of course a Utility Meter:
xyz_month_energy_draw:
source: sensor.xyz_power_consumption
cycle: monthly
The input numbers are not a necessity - I just like to be able to quickly change them (don’t even know why, because they shouldn’t ever change anyway)
One for the standby consumption of the switch
switch_standby_watt:
name: 'Switch Standby Watt'
min: 0
max: 10
step: 0.01
mode: box
and one for the wattage of the device
xyz_watt:
name: 'XYZ Watt'
min: 0
max: 3600
step: 1
mode: box
If you don’t use the input numbers, you can simply remove the part of the flow between OFF/ON and the functions, and in the function instead of
msg.payload = (msg.payload[1] * 0.00000027777778) * (msg.payload[0] / 1000);
you use:
msg.payload = (msg.payload * 0.00000027777778) * ( [INSERT POWER CONSUMPTION IN WATT] * 1000);
The biggest issue right now it, that “unavailable” status is being ignored - this doen’t matter as long as they are powered and just lose connection for a moment - just think about it, if you want to unplug them for some time the easiest workaround would be to treat OFF an UNAVAILABLE the same way and assume both are 0 consumption … I don’t like this solution so I’ll look into it furthrer
Hope some of you like this