With this example, you can visualize the setpoint from the Heat capacitor node and the electricity price in Home Assistant
You need the following installed before you can use this example:
In Node-RED, select Import from the menu, and paste the code for nodes below. Connect them like this:
[{"id":"dc013b2573667c36","type":"tab","label":"Heatcapacitor to apexchart","disabled":false,"info":"","env":[]},{"id":"9b380d597ac405bd","type":"api-current-state","z":"dc013b2573667c36","name":"Read Nord Pool","server":"ade5bab2.7713c8","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.nordpool_kwh_bergen_nok_3_095_025","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","x":320,"y":100,"wires":[["5a8fb67d458b8f29"]]},{"id":"2adcd75f2e479ad1","type":"inject","z":"dc013b2573667c36","name":"","props":[{"p":"payload"}],"repeat":"3600","crontab":"","once":false,"onceDelay":"10","topic":"","payload":"","payloadType":"date","x":130,"y":100,"wires":[["9b380d597ac405bd"]]},{"id":"5a8fb67d458b8f29","type":"ps-receive-price","z":"dc013b2573667c36","name":"Price Receiver","x":500,"y":100,"wires":[["2859147afa632a19"]]},{"id":"2859147afa632a19","type":"ps-strategy-heat-capacitor","z":"dc013b2573667c36","name":"Heat capacitor","timeHeat1C":50,"timeCool1C":50,"maxTempAdjustment":0.5,"boostTempHeat":0,"boostTempCool":0,"minSavings":0.08,"setpoint":23,"x":700,"y":100,"wires":[[],[],["ca913fa97c0bcd50"]]},{"id":"432829171bc7b018","type":"ha-sensor","z":"dc013b2573667c36","name":"Info from PS to HA","entityConfig":"e4921b906cc4eb37","version":0,"state":"payload","stateType":"str","attributes":[{"property":"Schedule","value":"payload.schedule","valueType":"msg"}],"inputOverride":"allow","outputProperties":[],"x":1170,"y":120,"wires":[[]]},{"id":"ca913fa97c0bcd50","type":"function","z":"dc013b2573667c36","name":"calculate schedule","func":"function addMinutes(date, minutes) {\n return new Date(date.getTime() + minutes * 60 * 1000);\n}\n\n// Get variables from the payload\nconst scheduleStartTime = new Date(msg.payload.startAt);\nconst prices = msg.payload.priceData;\nconst fixed_setpoint = msg.payload.config.setpoint;\nconst adjustments = msg.payload.temperatures;\n\n\nlet schedule = []\nlet last_sp = null;\nlet last_price = null;\n\nfor (let i = 0; i < adjustments.length; i++) {\n let startAt = addMinutes(scheduleStartTime, i);\n let hourDiff = Math.floor((startAt.getTime() - scheduleStartTime.getTime()) / (60 * 60 * 1000))\n\n // Skip this adjustment if nothing changed\n if ((prices[hourDiff].value == last_price) && (adjustments[i] == last_sp)) {\n continue;\n }\n last_sp = adjustments[i];\n last_price = prices[hourDiff].value;\n\n // Add data to schedule\n schedule.push({\n \"startAt\": startAt,\n \"price\": prices[hourDiff].value,\n \"adjustment\": adjustments[i],\n \"setpoint\": fixed_setpoint + adjustments[i]\n });\n}\n\n// What is the setpoint now?\nconst now = new Date();\nconst minutes_since_start = Math.floor((now.getTime() - scheduleStartTime.getTime()) / (60 * 1000));\nconst setpoint_now = fixed_setpoint + adjustments[minutes_since_start];\n\nmsg = {\n \"payload\": {\n \"setpoint_now\": setpoint_now,\n \"schedule\": schedule\n }\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":950,"y":120,"wires":[["432829171bc7b018"]]},{"id":"ade5bab2.7713c8","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":"e4921b906cc4eb37","type":"ha-entity-config","server":"ade5bab2.7713c8","deviceConfig":"","name":"toApexChart","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"toApexChart"},{"property":"icon","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false}]
Add a custom card with the apex chart with the following code:
type: custom:apexcharts-card
header:
show: true
title: HeatCapacitor visualization
now:
show: true
label: Nå
graph_span: 2d
span:
start: day
apex_config:
stroke:
width: 2
dataLabels:
enabled: true
fill:
type: gradient
gradient:
shadeIntensity: 1
inverseColors: false
opacityFrom: 0.45
opacityTo: 0.05
stops:
- 10
- 50
- 75
- 1000
legend:
show: false
yaxis:
- id: setpoint
decimalsInFloat: 1
min: auto
forceNiceScale: true
- id: price
show: true
decimalsInFloat: 1
extend_to: end
opposite: true
series:
- entity: sensor.toapexchart
name: Setpunkt
yaxis_id: setpoint
type: area
curve: stepline
stroke_width: 2
opacity: 0
color: '#FF0000'
extend_to: false
data_generator: |
return entity.attributes.schedule.map((entry) => {
return [new Date(entry.startAt), entry.setpoint];
});
- entity: sensor.toapexchart
name: Price
yaxis_id: price
type: area
curve: stepline
stroke_width: 2
opacity: 0
color: '#00FF00'
extend_to: false
show:
in_header: false
legend_value: false
float_precision: 4
data_generator: |
return entity.attributes.schedule.map((entry) => {
return [new Date(entry.startAt), entry.price];
});