The way I did it:
Set up a helper of type input_text with whatever name you want and maximum length of 255 and prepopulates with 47 values that represent half hour power consumption average just to kick it off. These numbers will be replace in 24 hours with real numbers.
e.g. 375,401,452,419,356,388,379,344,341,298,291,291,294,291,297,291,295,291,295,293,292,282,330,301,276,898,413,474,1128,1947,1185,921,635,520,355,395,465,436,387,353,391,420,386,424,660,631,509
I’ve done it in Watts without spaces and due to low consumption in my household this allows me to fit that string into 255 characters max length of an input_text entity. If your half hour average consumption plus a comer times 47 it greater than 255 you may have to use kW.
I use Node-Red so I set up a flow that runs every 60 seconds and adds the current power consumption to the last 60 second window’s average for 29 cycles and on the 30th divides the total by 30 to get the average power consumption for the last 30 minutes.
This number is then passed to a function that deletes the first number from the lefthand end of the input_text string and adds the newly calculated number as a string to the end of the input_text.
Then in the MPC POST I’ve added this to the jinja template:
"load_power_forecast": {{
([states('sensor.house_power_consumption_less_deferrables')|int(0)] +
states('input_text.fifo_buffer').split(',') | map('int') | list)
}},
This adds the actual power consumption at the time of the POST to the front of the input_text string which is the whole purpose of the exercise (to get EMHASS to react to turning the coffee machine on in the morning or follow the sun when charging the car).
The half hour average function looks like this.
var sumHousePower = parseInt(flow.get("flowSumHousePower")) || 0;
var iterationCount = flow.get("flowIterationCount") || 0;
var avgHousePower = flow.get("flowAvgHousePower") || 0;
var msg1 = { payload: 0 };
iterationCount = iterationCount + 1;
sumHousePower += parseInt(msg.payload);
flow.set("flowSumHousePower", sumHousePower);
flow.set("flowIterationCount", iterationCount);
// Determine exit path based on iteration count
if (iterationCount < 30) {
var msg2 = { payload: [iterationCount,sumHousePower] };
return [null, msg2];
}
else {
// Calculate average power consumption
var averagePower = sumHousePower / 30;
// Reset flow variables for the next 30 iterations
sumHousePower = 0;
iterationCount = 0;
flow.set("flowSumHousePower", sumHousePower);
flow.set("flowIterationCount", iterationCount);
// Send the average power out the first exit
msg1.payload = parseInt(averagePower);
flow.set("flowAvgHousePower", msg1.payload.toString());
return [msg1, null];
}
The FIFO the input text function looks like this:
msg.payload.lastIndexOf(',')));
let fiFoBuffer = msg.payload.substring(msg.payload.indexOf(',') + 1);
let avgHousePower = flow.get("flowAvgHousePower");
let comerString = ","
// Combine values, and comer and create a string
let newValue = fiFoBuffer.concat(comerString.concat(avgHousePower));
// Set the payload to the new value
msg.payload = newValue;
return msg;
Entire flow:
[
{
"id": "e6833878191a3648",
"type": "api-current-state",
"z": "65840aa926d9c567",
"name": "",
"server": "afc27684.cf6ed8",
"version": 3,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "input_text.fifo_buffer",
"state_type": "str",
"blockInputOverrides": false,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "entity"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 220,
"y": 3260,
"wires": [
[
"c2b7aef7a74ada86"
]
]
},
{
"id": "c2b7aef7a74ada86",
"type": "function",
"z": "65840aa926d9c567",
"name": "FIFO the input text",
"func": "// Get the values from the previous nodes\n//let fiFoBuffer = msg.payload.slice(msg.payload.lastIndexOf(',') + 1);\n//let fiFoBuffer = msg.payload.substring(0, Math.max(0, msg.payload.lastIndexOf(',')));\nlet fiFoBuffer = msg.payload.substring(msg.payload.indexOf(',') + 1);\nlet avgHousePower = flow.get(\"flowAvgHousePower\");\nlet comerString = \",\"\n\n// Combine values, and comer and create a string\n// let newValue = avgHousePower.concat(comerString.concat(fiFoBuffer));\nlet newValue = fiFoBuffer.concat(comerString.concat(avgHousePower));\n\n// Set the payload to the new value\nmsg.payload = newValue;\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 550,
"y": 3240,
"wires": [
[
"3e2215cdd1b5a4ac",
"8d497005f65ec71b"
]
]
},
{
"id": "3e2215cdd1b5a4ac",
"type": "api-call-service",
"z": "65840aa926d9c567",
"name": "",
"server": "afc27684.cf6ed8",
"version": 5,
"debugenabled": true,
"domain": "input_text",
"service": "set_value",
"areaId": [],
"deviceId": [],
"entityId": [
"input_text.fifo_buffer"
],
"data": "{\"value\":\"{{payload}}\"}",
"dataType": "json",
"mergeContext": "",
"mustacheAltTags": false,
"outputProperties": [],
"queue": "none",
"x": 820,
"y": 3240,
"wires": [
[]
]
},
{
"id": "8d497005f65ec71b",
"type": "debug",
"z": "65840aa926d9c567",
"name": "debug 6",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 840,
"y": 3320,
"wires": []
},
{
"id": "ab9db8fade2bc42c",
"type": "function",
"z": "65840aa926d9c567",
"name": "half hour average",
"func": "var sumHousePower = parseInt(flow.get(\"flowSumHousePower\")) || 0;\nvar iterationCount = flow.get(\"flowIterationCount\") || 0;\nvar avgHousePower = flow.get(\"flowAvgHousePower\") || 0;\nvar msg1 = { payload: 0 };\n\n\niterationCount = iterationCount + 1;\nsumHousePower += parseInt(msg.payload);\n\nflow.set(\"flowSumHousePower\", sumHousePower);\nflow.set(\"flowIterationCount\", iterationCount);\n\n// Determine exit path based on iteration count\nif (iterationCount < 30) {\n var msg2 = { payload: [iterationCount,sumHousePower] };\n return [null, msg2];\n } \nelse {\n // Calculate average power consumption\n var averagePower = sumHousePower / 30;\n\n // Reset flow variables for the next 30 iterations\n sumHousePower = 0;\n iterationCount = 0;\n flow.set(\"flowSumHousePower\", sumHousePower);\n flow.set(\"flowIterationCount\", iterationCount);\n\n // Send the average power out the first exit\n msg1.payload = parseInt(averagePower);\n flow.set(\"flowAvgHousePower\", msg1.payload.toString());\n\n return [msg1, null];\n }\n",
"outputs": 2,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 650,
"y": 3380,
"wires": [
[
"69a86b65b394b08c",
"e6833878191a3648"
],
[
"71f6efbade6f62f7"
]
]
},
{
"id": "ab70d43f5a804319",
"type": "api-current-state",
"z": "65840aa926d9c567",
"name": "Current State house power less def",
"server": "afc27684.cf6ed8",
"version": 3,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "sensor.house_power_consumption_less_deferrables",
"state_type": "str",
"blockInputOverrides": false,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "entity"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 400,
"y": 3380,
"wires": [
[
"ab9db8fade2bc42c"
]
]
},
{
"id": "32ba8859361ba366",
"type": "inject",
"z": "65840aa926d9c567",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "60",
"crontab": "",
"once": false,
"onceDelay": "60",
"topic": "",
"payload": "",
"payloadType": "date",
"x": 170,
"y": 3380,
"wires": [
[
"ab70d43f5a804319"
]
]
},
{
"id": "69a86b65b394b08c",
"type": "debug",
"z": "65840aa926d9c567",
"name": "debug 7",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 840,
"y": 3380,
"wires": []
},
{
"id": "71f6efbade6f62f7",
"type": "debug",
"z": "65840aa926d9c567",
"name": "debug 8",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 840,
"y": 3440,
"wires": []
},
{
"id": "48a7dfb873c1e2b8",
"type": "comment",
"z": "65840aa926d9c567",
"name": "FIFO Buffer for load_power_forecast ",
"info": "",
"x": 220,
"y": 3220,
"wires": []
},
{
"id": "afc27684.cf6ed8",
"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
}
]
Mark uses an automation that he explains here