[{"id":"ab2d599a.077738","type":"tibber-query","z":"d48adf45980f6fbf","name":"Get Tibber prices","active":true,"apiEndpointRef":"b70ec5d0.6f8f08","x":330,"y":840,"wires":[["0ef929fde193cf4d","7a48b02ae3e7604f","eafd82a3ca442774"]]},{"id":"4f11b5ae.4cc22c","type":"inject","z":"d48adf45980f6fbf","name":"Refresh","props":[{"p":"payload"}],"repeat":"1800","crontab":"","once":true,"onceDelay":"10","topic":"","payload":"{ viewer { homes { currentSubscription{ priceInfo{ today { total startsAt } tomorrow { total startsAt } } } } } }","payloadType":"str","x":100,"y":840,"wires":[["ab2d599a.077738"]]},{"id":"0ef929fde193cf4d","type":"ps-receive-price","z":"d48adf45980f6fbf","name":"Price Receiver","x":540,"y":840,"wires":[["e9eafec0a9d4b880","071fd378027c2a1c","fb60cc084377388f","b661b0da22d60992","1dc0d6685872c440"]]},{"id":"e9eafec0a9d4b880","type":"ps-strategy-lowest-price","z":"d48adf45980f6fbf","name":"Lowest Price","fromTime":"00","toTime":"00","hoursOn":"6","maxPrice":"","doNotSplit":true,"sendCurrentValueWhenRescheduling":true,"outputValueForOn":"true","outputValueForOff":"false","outputValueForOntype":"bool","outputValueForOfftype":"bool","outputIfNoSchedule":"true","outputOutsidePeriod":"false","contextStorage":"file","x":410,"y":320,"wires":[["09539fe071da3457"],["d50279c870438cba"],[]]},{"id":"09539fe071da3457","type":"api-call-service","z":"d48adf45980f6fbf","name":"","server":"f1f243ad.3283c","version":7,"debugenabled":false,"action":"input_boolean.turn_on","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.cheap_on"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"input_boolean","service":"turn_on","x":720,"y":180,"wires":[[]]},{"id":"d50279c870438cba","type":"api-call-service","z":"d48adf45980f6fbf","name":"","server":"f1f243ad.3283c","version":7,"debugenabled":true,"action":"input_boolean.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.cheap_on"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"results"}],"queue":"none","blockInputOverrides":true,"domain":"input_boolean","service":"turn_off","x":720,"y":240,"wires":[[]]},{"id":"6cf298fc9c936de6","type":"link out","z":"d48adf45980f6fbf","name":"link out 39","mode":"link","links":[],"x":1705,"y":980,"wires":[]},{"id":"938d99b4ad5649ed","type":"inject","z":"d48adf45980f6fbf","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"3600","crontab":"","once":true,"onceDelay":"10","topic":"","payload":"{\"commands\":{\"replan\":true}}","payloadType":"json","x":790,"y":800,"wires":[["071fd378027c2a1c","fb60cc084377388f","b661b0da22d60992"]]},{"id":"133d06a5e6436cd3","type":"debug","z":"d48adf45980f6fbf","name":"debug 123","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1590,"y":740,"wires":[]},{"id":"02dbba196f71baa1","type":"debug","z":"d48adf45980f6fbf","name":"debug 124","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1350,"y":800,"wires":[]},{"id":"071fd378027c2a1c","type":"ps-strategy-heat-capacitor","z":"d48adf45980f6fbf","name":"Heat capacitor","timeHeat1C":"660","timeCool1C":"840","maxTempAdjustment":"0.5","boostTempHeat":"0.5","boostTempCool":"0.3","minSavings":"0.1","setpoint":"19.8","x":1020,"y":800,"wires":[["133d06a5e6436cd3","02dbba196f71baa1","f99b554c2baa813e","da6887b2d319c29a","f01761370b9c6129","5e02ed2781f2c322"],["133d06a5e6436cd3"],["133d06a5e6436cd3","f3dd7385af61511a","d71663889c02ad5c","0390d82868f6f5c7","89fe9f6a64cb7f66","ccc2dce45a2c1ed2"],[]]},{"id":"da6887b2d319c29a","type":"link out","z":"d48adf45980f6fbf","name":"link out 107","mode":"link","links":["26f2608cae29fb7e"],"x":1255,"y":720,"wires":[]},{"id":"f99b554c2baa813e","type":"api-call-service","z":"d48adf45980f6fbf","name":"","server":"f1f243ad.3283c","version":7,"debugenabled":true,"action":"input_number.set_value","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_number.room_setpoint"],"labelId":[],"data":"{\"value\": msg.payload }","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"num"}],"queue":"none","blockInputOverrides":true,"domain":"input_number","service":"set_value","x":1330,"y":660,"wires":[[]]},{"id":"d71735174cc09fbd","type":"api-call-service","z":"d48adf45980f6fbf","name":"","server":"f1f243ad.3283c","version":7,"debugenabled":false,"action":"input_boolean.toggle","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.sterilization_on"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"input_boolean","service":"toggle","x":1920,"y":980,"wires":[[]]},{"id":"4ac4a4131a6a8465","type":"change","z":"d48adf45980f6fbf","name":"Prep for Sterilization planner","rules":[{"t":"set","p":"topic","pt":"msg","to":"Sterilization","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"true","fromt":"bool","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1540,"y":980,"wires":[["6cf298fc9c936de6","d71735174cc09fbd"]]},{"id":"230cbd34c03550c8","type":"ps-schedule-merger","z":"d48adf45980f6fbf","name":"Schedule Merger","outputIfNoSchedule":"false","logicFunction":"AND","schedulingDelay":2000,"sendCurrentValueWhenRescheduling":true,"outputValueForOn":"true","outputValueForOff":"false","outputValueForOntype":"bool","outputValueForOfftype":"bool","x":1250,"y":980,"wires":[["4ac4a4131a6a8465"],[],[]]},{"id":"d5944f731fdfd948","type":"server-state-changed","z":"d48adf45980f6fbf","d":true,"name":"Setpoint","server":"f1f243ad.3283c","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["input_number.setpoint"],"substring":[],"regex":[]},"outputInitially":false,"stateType":"num","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":620,"y":960,"wires":[["95a8139cea7f87a5"]]},{"id":"fb60cc084377388f","type":"ps-strategy-fixed-schedule","z":"d48adf45980f6fbf","name":"Fixed Schedule","periods":[{"start":"12","value":"true"}],"validFrom":"","validTo":"","days":{"Mon":false,"Tue":false,"Wed":false,"Thu":false,"Fri":false,"Sat":false,"Sun":true},"sendCurrentValueWhenRescheduling":true,"outputValueForOn":"true","outputValueForOff":"false","outputValueForOntype":"bool","outputValueForOfftype":"bool","outputIfNoSchedule":"false","contextStorage":"memoryOnly","x":1020,"y":1080,"wires":[[],[],["230cbd34c03550c8"]]},{"id":"b661b0da22d60992","type":"ps-strategy-lowest-price","z":"d48adf45980f6fbf","name":"Lowest Price","fromTime":"00","toTime":"00","hoursOn":"4","maxPrice":"","doNotSplit":true,"sendCurrentValueWhenRescheduling":true,"outputValueForOn":"true","outputValueForOff":"false","outputValueForOntype":"bool","outputValueForOfftype":"bool","outputIfNoSchedule":"false","outputOutsidePeriod":"false","contextStorage":"memoryOnly","x":1030,"y":940,"wires":[[],[],["230cbd34c03550c8"]]},{"id":"95a8139cea7f87a5","type":"change","z":"d48adf45980f6fbf","name":"Prepare for Setpoint","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t \"config\": {\t \"setpoint\":payload\t }\t}","tot":"jsonata"},{"t":"delete","p":"topic","pt":"msg"},{"t":"delete","p":"qos","pt":"msg"},{"t":"delete","p":"retain","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":800,"y":1000,"wires":[["071fd378027c2a1c"]]},{"id":"6311673f32d2c27f","type":"ha-sensor","z":"d48adf45980f6fbf","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":1770,"y":840,"wires":[[]]},{"id":"f3dd7385af61511a","type":"function","z":"d48adf45980f6fbf","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,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1450,"y":840,"wires":[["8d8930839e9ef042","6311673f32d2c27f"]]},{"id":"8d8930839e9ef042","type":"debug","z":"d48adf45980f6fbf","name":"debug 288","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1600,"y":800,"wires":[]},{"id":"a987e0a25d5e712f","type":"change","z":"d48adf45980f6fbf","name":"Prepare for Setpoint","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t \"config\": {\t \"timeHeat1C\":payload\t }\t}","tot":"jsonata"},{"t":"delete","p":"topic","pt":"msg"},{"t":"delete","p":"qos","pt":"msg"},{"t":"delete","p":"retain","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":1200,"wires":[["071fd378027c2a1c"]]},{"id":"4f9e7434d1ca1553","type":"change","z":"d48adf45980f6fbf","name":"Prepare for Setpoint","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t \"config\": {\t \"timeCool1C\":payload\t }\t}","tot":"jsonata"},{"t":"delete","p":"topic","pt":"msg"},{"t":"delete","p":"qos","pt":"msg"},{"t":"delete","p":"retain","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":1240,"wires":[["071fd378027c2a1c"]]},{"id":"e3726c49313fa29f","type":"ui_numeric","z":"d48adf45980f6fbf","name":"Setpoint","label":"Setpoint","tooltip":"","group":"ee32d0cb39eddc0a","order":8,"width":"6","height":1,"wrap":true,"passthru":false,"topic":"Setpoint","topicType":"str","format":"{{value}} C","min":"19","max":"21","step":"0.1","className":"","x":260,"y":1280,"wires":[["2bbf1b8a7e683402"]]},{"id":"2bbf1b8a7e683402","type":"change","z":"d48adf45980f6fbf","name":"Prepare for Setpoint","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t \"config\": {\t \"setpoint\":payload\t }\t}","tot":"jsonata"},{"t":"delete","p":"topic","pt":"msg"},{"t":"delete","p":"qos","pt":"msg"},{"t":"delete","p":"retain","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":1280,"wires":[["071fd378027c2a1c"]]},{"id":"d8063191b0cf9b55","type":"ui_numeric","z":"d48adf45980f6fbf","name":"Max temp adjust","label":"Max temp adjust","tooltip":"","group":"ee32d0cb39eddc0a","order":8,"width":"6","height":1,"wrap":true,"passthru":false,"topic":"Max temp adjust","topicType":"str","format":"{{value}} C","min":"0","max":"2","step":"0.1","className":"","x":280,"y":1320,"wires":[["232b3b31766e33ad"]]},{"id":"232b3b31766e33ad","type":"change","z":"d48adf45980f6fbf","name":"Prepare for Setpoint","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t \"config\": {\t \"maxTempAdjustment\":payload\t }\t}","tot":"jsonata"},{"t":"delete","p":"topic","pt":"msg"},{"t":"delete","p":"qos","pt":"msg"},{"t":"delete","p":"retain","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":1320,"wires":[["071fd378027c2a1c"]]},{"id":"6a6724032cc73cbc","type":"ui_numeric","z":"d48adf45980f6fbf","name":"Minimum savings","label":"Minimum savings","tooltip":"","group":"ee32d0cb39eddc0a","order":8,"width":"6","height":1,"wrap":true,"passthru":false,"topic":"Minimum savings","topicType":"str","format":"{{value}} euro","min":"0","max":"2","step":"0.01","className":"","x":290,"y":1360,"wires":[["32aec5fcbca71765"]]},{"id":"32aec5fcbca71765","type":"change","z":"d48adf45980f6fbf","name":"Prepare for Setpoint","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t \"config\": {\t \"minSavings\":payload\t }\t}","tot":"jsonata"},{"t":"delete","p":"topic","pt":"msg"},{"t":"delete","p":"qos","pt":"msg"},{"t":"delete","p":"retain","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":1360,"wires":[["071fd378027c2a1c"]]},{"id":"3cdcb9f351ee3bf9","type":"ui_numeric","z":"d48adf45980f6fbf","name":"Heating Boost","label":"Heating Boost","tooltip":"","group":"ee32d0cb39eddc0a","order":8,"width":"6","height":1,"wrap":true,"passthru":false,"topic":"Heating Boost","topicType":"str","format":"{{value}} C","min":"0","max":"2","step":"0.1","className":"","x":280,"y":1400,"wires":[["846d96f94def4807"]]},{"id":"846d96f94def4807","type":"change","z":"d48adf45980f6fbf","name":"Prepare for Setpoint","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t \"config\": {\t \"boostTempHeat\":payload\t }\t}","tot":"jsonata"},{"t":"delete","p":"topic","pt":"msg"},{"t":"delete","p":"qos","pt":"msg"},{"t":"delete","p":"retain","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":1400,"wires":[["071fd378027c2a1c"]]},{"id":"5810b97a5be1bd3c","type":"ui_numeric","z":"d48adf45980f6fbf","name":"Cooling Boost","label":"Cooling Boost","tooltip":"","group":"ee32d0cb39eddc0a","order":8,"width":"6","height":1,"wrap":true,"passthru":false,"topic":"Cooling Boost","topicType":"str","format":"{{value}} C","min":"0","max":"2","step":"0.1","className":"","x":280,"y":1440,"wires":[["9378ac801eb6d7d0"]]},{"id":"9378ac801eb6d7d0","type":"change","z":"d48adf45980f6fbf","name":"Prepare for Setpoint","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t \"config\": {\t \"boostTempCool\":payload\t }\t}","tot":"jsonata"},{"t":"delete","p":"topic","pt":"msg"},{"t":"delete","p":"qos","pt":"msg"},{"t":"delete","p":"retain","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":1440,"wires":[["071fd378027c2a1c"]]},{"id":"6f55e893597d309d","type":"ui_text_input","z":"d48adf45980f6fbf","name":"Time_Plus1","label":"Time_Plus1","tooltip":"","group":"ee32d0cb39eddc0a","order":7,"width":0,"height":0,"passthru":true,"mode":"text","delay":"0","topic":"topic","sendOnBlur":true,"className":"","topicType":"msg","x":270,"y":1200,"wires":[["19131db207a5f6db"]]},{"id":"19131db207a5f6db","type":"function","z":"d48adf45980f6fbf","name":"function 99","func":"msg.payload = Number(msg.payload);\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":450,"y":1200,"wires":[["a987e0a25d5e712f"]]},{"id":"9cb5d8da6b4f4c12","type":"ui_text_input","z":"d48adf45980f6fbf","name":"Time_minus1","label":"Time_minus1","tooltip":"","group":"ee32d0cb39eddc0a","order":7,"width":0,"height":0,"passthru":true,"mode":"text","delay":"0","topic":"topic","sendOnBlur":true,"className":"","topicType":"msg","x":280,"y":1240,"wires":[["4de967270fd1ae61"]]},{"id":"4de967270fd1ae61","type":"function","z":"d48adf45980f6fbf","name":"function 100","func":"msg.payload = Number(msg.payload);\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":450,"y":1240,"wires":[["4f9e7434d1ca1553"]]},{"id":"f763e5a5201f7cfb","type":"ui_chart","z":"d48adf45980f6fbf","name":"","group":"31f15ecbe0637ad6","order":7,"width":"0","height":"0","label":"chart","chartType":"line","legend":"true","xformat":"dd HH:mm","interpolate":"step","nodata":"no data has arrived yet.","dot":false,"ymin":"19","ymax":"22","removeOlder":"2","removeOlderPoints":"","removeOlderUnit":"86400","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":1650,"y":1220,"wires":[[]]},{"id":"d71663889c02ad5c","type":"function","z":"d48adf45980f6fbf","name":"help with graph","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\n\n// Prepare chart data for multiple series\nlet setpointData = schedule.map(item => ({ x: item.startAt, y: item.setpoint }));\n\n// Prepare the msg object for the chart\nmsg.payload = [{\n series: [\"Setpoint\"], // Names of the series\n data: [setpointData], // Data for each series\n labels: [\"Setpoint\"], // Labels for the series\n yaxis: [\n { label: \"Setpoint\", min: 18, max: 22 }, // Y-axis for Setpoint\n ]\n}];\n\n// Send the message to the chart node\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1400,"y":1220,"wires":[["f763e5a5201f7cfb"]]},{"id":"1038d5b2669465f8","type":"ui_chart","z":"d48adf45980f6fbf","name":"","group":"31f15ecbe0637ad6","order":7,"width":"0","height":"0","label":"chart","chartType":"line","legend":"true","xformat":"dd HH:mm","interpolate":"step","nodata":"no data has arrived yet.","dot":false,"ymin":"0","ymax":"0.5","removeOlder":"2","removeOlderPoints":"","removeOlderUnit":"86400","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":1650,"y":1260,"wires":[[]]},{"id":"6854bdcb778adc30","type":"delay","z":"d48adf45980f6fbf","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"3","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":1760,"y":260,"wires":[["799b541758fdf8a6","ee9efcc8b293e75d"]]},{"id":"0390d82868f6f5c7","type":"function","z":"d48adf45980f6fbf","name":"help with graph","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\n\n// Prepare chart data for multiple series\n\nlet priceData = schedule.map(item => ({ x: item.startAt, y: item.price }));\n\n// Prepare the msg object for the chart\nmsg.payload = [{\n series: [\"Price\"], // Names of the series\n data: [priceData], // Data for each series\n labels: [ \"Price\"], // Labels for the series\n yaxis: [{ label: \"Price\", min: 0, max: 1.2, opposite: true }], // Y-axis for Price\n \n}];\n\n// Send the message to the chart node\nreturn msg;\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1400,"y":1260,"wires":[["1038d5b2669465f8","458edcc8a14e7be6"]]},{"id":"55ea479141ade4e5","type":"debug","z":"d48adf45980f6fbf","name":"debug 292","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1660,"y":1360,"wires":[]},{"id":"89fe9f6a64cb7f66","type":"debug","z":"d48adf45980f6fbf","name":"debug 293","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1540,"y":1080,"wires":[]},{"id":"ccc2dce45a2c1ed2","type":"function","z":"d48adf45980f6fbf","name":"help with graph","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\n\n// Prepare chart data for multiple series\n\nlet adjustmentData = schedule.map(item => ({ x: item.startAt, y: item.adjustment }));\n\n// Prepare the msg object for the chart\nmsg.payload = [{\n series: [\"Adjustment\"], // Names of the series\n data: [adjustmentData], // Data for each series\n labels: [ \"Adjustment\"], // Labels for the series\n yaxis: [\n \n { label: \"Adjustment\", min: 0, max: 1.2, opposite: true }, // Y-axis for Price\n \n ]\n}];\n\n// Send the message to the chart node\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1400,"y":1300,"wires":[["55ea479141ade4e5","d68fb58432026343"]]},{"id":"458edcc8a14e7be6","type":"debug","z":"d48adf45980f6fbf","name":"debug 294","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1640,"y":1420,"wires":[]},{"id":"d68fb58432026343","type":"ui_chart","z":"d48adf45980f6fbf","name":"","group":"31f15ecbe0637ad6","order":7,"width":"0","height":"0","label":"chart","chartType":"line","legend":"true","xformat":"dd HH:mm","interpolate":"step","nodata":"no data has arrived yet.","dot":false,"ymin":"-2","ymax":"2","removeOlder":"2","removeOlderPoints":"","removeOlderUnit":"86400","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":1650,"y":1300,"wires":[[]]},{"id":"7a48b02ae3e7604f","type":"debug","z":"d48adf45980f6fbf","d":true,"name":"debug 295","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":440,"y":700,"wires":[]},{"id":"eafd82a3ca442774","type":"function","z":"d48adf45980f6fbf","d":true,"name":"function 101","func":"// Extract price data for today and tomorrow\nconst todayPrices = msg.payload.viewer.homes[0].currentSubscription.priceInfo.today;\nconst tomorrowPrices = msg.payload.viewer.homes[0].currentSubscription.priceInfo.tomorrow;\n\n// Initialize an array to hold the chart data\nlet chartData = [];\n\n// Combine today's and tomorrow's prices into one array\nlet allPrices = todayPrices.concat(tomorrowPrices);\n\n// Loop through each price object and format it for the chart\nallPrices.forEach(item => {\n // Convert the 'startsAt' field to epoch time (milliseconds)\n const timestamp = new Date(item.startsAt).getTime(); // Get epoch time\n \n // Prepare the data in the format {x: timestamp, y: price}\n chartData.push({\n x: timestamp, // x-axis is the epoch time\n y: item.total // y-axis is the price\n });\n});\n\n// Debugging: Log the formatted data to the debug window\nnode.warn(chartData); // Inspect the output in the Node-RED debug window\n\n// Check if chartData is in the expected format\nif (!Array.isArray(chartData)) {\n node.error(\"chartData is not an array\", msg);\n} else if (chartData.length === 0) {\n node.error(\"chartData is empty\", msg);\n} else {\n // Set the msg.payload to the chart data\n \n}\nvar chart = chartData;\n msg.payload = chart;\n// Return the message with the formatted chart data\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":610,"y":640,"wires":[["9dec7328265c4ae1","940a1cae1f5d1bb3"]]},{"id":"9dec7328265c4ae1","type":"ui_chart","z":"d48adf45980f6fbf","d":true,"name":"","group":"31f15ecbe0637ad6","order":3,"width":"12","height":"5","label":"chart","chartType":"line","legend":"true","xformat":"auto","interpolate":"step","nodata":"No data arrived.","dot":false,"ymin":"0","ymax":"2","removeOlder":"2","removeOlderPoints":"","removeOlderUnit":"86400","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":830,"y":640,"wires":[[]]},{"id":"940a1cae1f5d1bb3","type":"debug","z":"d48adf45980f6fbf","d":true,"name":"debug 296","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":750,"y":580,"wires":[]},{"id":"65d8cd058cc446d4","type":"change","z":"d48adf45980f6fbf","name":"Prepare for Setpoint","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t \"config\": {\t \"timeHeat1C\":660, \"timeCool1C\":880, \"setpoint\":19.7,\"maxTempAdjustment\":0.3, \"minSavings\":0.03,\"boostTempHeat\":0.2,\"boostTempCool\":0}\t}","tot":"jsonata"},{"t":"delete","p":"topic","pt":"msg"},{"t":"delete","p":"qos","pt":"msg"},{"t":"delete","p":"retain","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":760,"y":740,"wires":[["071fd378027c2a1c"]]},{"id":"f01761370b9c6129","type":"rbe","z":"d48adf45980f6fbf","name":"","func":"deadbandEq","gap":"2%","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":1200,"y":580,"wires":[["490c1f7836e2742a","77d58a65c42f2c2e"]]},{"id":"490c1f7836e2742a","type":"change","z":"d48adf45980f6fbf","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":1380,"y":580,"wires":[["a1143e9222931211","44cd762bac55d9ae"]]},{"id":"a1143e9222931211","type":"link out","z":"d48adf45980f6fbf","name":"link out 108","mode":"link","links":["270130abee727eee"],"x":1545,"y":580,"wires":[]},{"id":"1dc0d6685872c440","type":"ps-strategy-lowest-price","z":"d48adf45980f6fbf","name":"Lowest Price","fromTime":"10","toTime":"04","hoursOn":"4","maxPrice":"","doNotSplit":true,"sendCurrentValueWhenRescheduling":true,"outputValueForOn":"true","outputValueForOff":"false","outputValueForOntype":"bool","outputValueForOfftype":"bool","outputIfNoSchedule":"false","outputOutsidePeriod":"false","contextStorage":"memoryOnly","x":1150,"y":480,"wires":[["f62afeab5d7f3d27"],[],["72a994701f07b202"]]},{"id":"f62afeab5d7f3d27","type":"debug","z":"d48adf45980f6fbf","name":"debug 297","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1320,"y":400,"wires":[]},{"id":"72a994701f07b202","type":"debug","z":"d48adf45980f6fbf","name":"debug 298","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1340,"y":460,"wires":[]},{"id":"44cd762bac55d9ae","type":"debug","z":"d48adf45980f6fbf","name":"debug 299","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1520,"y":520,"wires":[]},{"id":"77d58a65c42f2c2e","type":"debug","z":"d48adf45980f6fbf","name":"debug 300","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1320,"y":520,"wires":[]},{"id":"5e02ed2781f2c322","type":"ui_switch","z":"d48adf45980f6fbf","name":"","label":"switch","tooltip":"","group":"ee32d0cb39eddc0a","order":7,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"true","onvalueType":"str","onicon":"","oncolor":"","offvalue":"false","offvalueType":"str","officon":"","offcolor":"","animate":false,"className":"","x":1030,"y":280,"wires":[["27c31cadbd0e4110"]]},{"id":"27c31cadbd0e4110","type":"function","z":"d48adf45980f6fbf","name":"function 102","func":"if (msg.topic == 'true') \n{\n return msg.payload\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1210,"y":280,"wires":[[]]},{"id":"799b541758fdf8a6","type":"change","z":"d48adf45980f6fbf","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1760,"y":180,"wires":[["35776b7039ade2de"]]},{"id":"35776b7039ade2de","type":"delay","z":"d48adf45980f6fbf","name":"","pauseType":"delay","timeout":"3","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1940,"y":180,"wires":[["5728a9d0f1b482b0","7ff323e246c67308"]]},{"id":"61f903e8357a8c1f","type":"delay","z":"d48adf45980f6fbf","name":"","pauseType":"delay","timeout":"7","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1940,"y":340,"wires":[["5728a9d0f1b482b0","796c7ee09a7cc2a4"]]},{"id":"8d0e9699c999c263","type":"debug","z":"d48adf45980f6fbf","name":"debug 304","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1760,"y":520,"wires":[]},{"id":"5728a9d0f1b482b0","type":"link out","z":"d48adf45980f6fbf","name":"link out 109","mode":"link","links":["3aa06511dcdbce13"],"x":2055,"y":260,"wires":[]},{"id":"ee9efcc8b293e75d","type":"change","z":"d48adf45980f6fbf","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1760,"y":340,"wires":[["61f903e8357a8c1f"]]},{"id":"f1e2bea469e8d427","type":"link in","z":"d48adf45980f6fbf","name":"link in 80","links":["e89a93fc4792f763"],"x":1645,"y":260,"wires":[["6854bdcb778adc30"]]},{"id":"7ff323e246c67308","type":"debug","z":"d48adf45980f6fbf","name":"debug 305","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2130,"y":180,"wires":[]},{"id":"796c7ee09a7cc2a4","type":"debug","z":"d48adf45980f6fbf","name":"debug 306","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2130,"y":340,"wires":[]},{"id":"b70ec5d0.6f8f08","type":"tibber-api-endpoint","queryUrl":"https://api.tibber.com/v1-beta/gql","feedConnectionTimeout":"30","feedTimeout":"15","queryRequestTimeout":"30","name":"Tibber API"},{"id":"f1f243ad.3283c","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":"f1f243ad.3283c","deviceConfig":"","name":"toApexChart","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"toApexChart"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false},{"id":"ee32d0cb39eddc0a","type":"ui_group","name":"Group 1","tab":"a875413b759e29c4","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"31f15ecbe0637ad6","type":"ui_group","name":"Group 2","tab":"a875413b759e29c4","order":2,"disp":true,"width":"16","collapse":false,"className":""},{"id":"a875413b759e29c4","type":"ui_tab","name":"Cost Optimization","icon":"dashboard","order":16,"disabled":false,"hidden":false}]