ottopaulsen / node-red-contrib-power-saver

A Node-RED node to saver money by turning off when the power is most expensive
Other
71 stars 17 forks source link

Problem when lowest price node sends off, when outside period. #92

Open KasperEdw opened 1 year ago

KasperEdw commented 1 year ago

I really like these nodes - a super addition in these times where the Putin is messing with our energy prices. Thanks for making them available. I have set up a flow that uses the PS lowest price for two different time periods 1) 0-12 and 2) 12-24. This to ensure that the battery is charged at the lowest price points in the night, morning, afternoon, and evening. All looks good but the PS lowest price node lets out a signal outside its time range. Consequently a charge on is quickly followed by a charge off by the node outside time range:

image

The on/off's are just switches to turn the flows on/off

The PS low price 0-12 node is configured like this:

image

This seem to be a consequence of the node sending a signal when there is "No Schedule" - if so, I think this is wrong behavior, or at least for me it gives problems. When given a time bracket the node should only send Schedule info. Another solution: "If no schedule" and "Outside period" could have a "Do nothing" option. Maby this would be the best and most flexible solution.

ottopaulsen commented 1 year ago

Hi, thanks for the input. I am currently working on a new node that will merge multiple schedules. I think it will solve this issue. It will take a few weeks until it is done.

marhoy commented 1 year ago

@KasperEdw I'm doing it like this:

image

In the On/Off function-node, I'm sending "On" if at least one of the schedulers wants "On":

if (!flow.get("auto_control")) {
    // We're in manual control, bail out
    return [null, null];
}

if (msg.payload.some((element) => element)) {
    // At least one of the schedulers wants "On"
    msg.payload = "On";
    return [msg, null];
} else {
    // All schedulers agrees to "Off"
    msg.payload = "Off";
    return [null, msg];
}
mvjt commented 1 year ago

Greetings from Sverige. +1 to both the kudos and the issue with sending on/off when out of schedule. It messes up my setup as well where I have one "run" during night and one during the day when the electricity is cheap....

marhoy commented 1 year ago

@mvjt I don't have an issue with "out of schedule"?

I set my schedulers to send "Off" both if there's "no schedule" and "outside schedule". Since I join all the scheduler outputs (it waits until all schedulers have sent their output), I can simultaneously consider all of them and turn "On" only if one or more of them wants "On" (otherwise "Off")

mvjt commented 1 year ago

@mvjt I don't have an issue with "out of schedule"?

I set my schedulers to send "Off" both if there's "no schedule" and "outside schedule". Since I join all the scheduler outputs (it waits until all schedulers have sent their output), I can simultaneously consider all of them and turn "On" only if one or more of them wants "On" (otherwise "Off")

Hi @marhoy I´ve not looked into your flow yet as there was no flow json provided. I still think the improvement would be useful as most of us are not as experienced with NR as you might be. My problem is the 12+ hours when there is no schedule until Nordpool publishes them.

marhoy commented 1 year ago

@mvjt Flow? Here you go:

[{"id":"6a6f662ebd0290ef","type":"api-call-service","z":"4052caf3dd00c142","name":"Varmtvann På","server":"a9970d75.fc3e1","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.kjeller_varmtvann"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"last","x":1120,"y":140,"wires":[[]]},{"id":"1c71e029af2360c2","type":"api-call-service","z":"4052caf3dd00c142","name":"Varmtvann Av","server":"a9970d75.fc3e1","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.kjeller_varmtvann"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"last","x":1120,"y":200,"wires":[[]]},{"id":"09fae2e61a94c229","type":"trigger-state","z":"4052caf3dd00c142","name":"Varmtvann styring","server":"a9970d75.fc3e1","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityid":"input_select.varmtvann_styring","entityidfiltertype":"exact","debugenabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is_not","comparatorValueDatatype":"str","comparatorValue":"Auto"}],"inputs":0,"outputs":2,"customoutputs":[],"outputinitially":true,"state_type":"str","enableInput":false,"x":130,"y":100,"wires":[["2676dd5683d597e6"],["a619526a0c951619"]]},{"id":"78894ff4a8c06ef8","type":"link in","z":"4052caf3dd00c142","name":"Priser: Tibber + Elvia","links":["17e948d9b2028b3f"],"x":290,"y":300,"wires":[["6eeacdec9dbd2f3a"]],"l":true},{"id":"14ea8c500c7aa2cd","type":"switch","z":"4052caf3dd00c142","name":"On or Off","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"On","vt":"str"},{"t":"eq","v":"Off","vt":"str"}],"checkall":"false","repair":false,"outputs":2,"x":520,"y":80,"wires":[["6a6f662ebd0290ef"],["1c71e029af2360c2"]]},{"id":"a619526a0c951619","type":"change","z":"4052caf3dd00c142","name":"Auto control","rules":[{"t":"set","p":"auto_control","pt":"flow","to":"true","tot":"bool"},{"t":"set","p":"payload","pt":"msg","to":"{\"commands\":{\"sendOutput\":true}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":120,"wires":[["6eeacdec9dbd2f3a"]]},{"id":"2676dd5683d597e6","type":"change","z":"4052caf3dd00c142","name":"Manual control","rules":[{"t":"set","p":"auto_control","pt":"flow","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":80,"wires":[["14ea8c500c7aa2cd"]]},{"id":"e27b07b7eb751099","type":"ps-strategy-lowest-price","z":"4052caf3dd00c142","name":"Lowest Price 20-08","fromTime":"20","toTime":"08","hoursOn":"3","maxPrice":"","doNotSplit":false,"sendCurrentValueWhenRescheduling":true,"outputIfNoSchedule":"false","outputOutsidePeriod":"false","contextStorage":"memory","x":550,"y":260,"wires":[["7c8db7b406232476"],["7c8db7b406232476"],["48cbcd19deb54068"]]},{"id":"b835b6e1555c8ff2","type":"ps-strategy-lowest-price","z":"4052caf3dd00c142","name":"Lowest Price 08-20","fromTime":"08","toTime":"20","hoursOn":"3","maxPrice":"","doNotSplit":false,"sendCurrentValueWhenRescheduling":true,"outputIfNoSchedule":"false","outputOutsidePeriod":"false","contextStorage":"memory","x":550,"y":340,"wires":[["b57f10158dcc79f0"],["b57f10158dcc79f0"],["48cbcd19deb54068"]]},{"id":"512054092ba0afc8","type":"join","z":"4052caf3dd00c142","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"5","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":750,"y":280,"wires":[["8592457f56627fd6"]]},{"id":"8592457f56627fd6","type":"function","z":"4052caf3dd00c142","name":"On / Off","func":"if (!flow.get(\"auto_control\")) {\n    // We're in manual control, bail out\n    return [null, null];\n}\n\nif (msg.payload.some((element) => element)) {\n    // At least one of the schedulers wants \"On\"\n    msg.payload = \"On\";\n    return [msg, null];\n} else {\n    // All schedulers agrees to \"Off\"\n    msg.payload = \"Off\";\n    return [null, msg];\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":880,"y":280,"wires":[["6a6f662ebd0290ef"],["1c71e029af2360c2"]],"outputLabels":["On","Off"]},{"id":"48cbcd19deb54068","type":"join","z":"4052caf3dd00c142","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"5","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":750,"y":320,"wires":[["097efcfedfbe075c"]]},{"id":"097efcfedfbe075c","type":"function","z":"4052caf3dd00c142","name":"Merge schedules","func":"const hours1 = msg.payload[0].hours\nconst hours2 = msg.payload[1].hours\n\nlet hours = []\nfor (let i = 0; i < hours1.length; i++) {\n    hours[i] = {\n        \"start\": hours1[i].start,\n        \"price\": hours1[i].price,\n        \"onOff\": (hours1[i].onOff | hours2[i].onOff)\n    };\n}\n\nmsg.payload = {\n    \"hours\": hours,\n    \"configs\": [msg.payload[0].config, msg.payload[1].config]\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":910,"y":320,"wires":[["9d9e12bd81ccf952"]]},{"id":"9d9e12bd81ccf952","type":"ha-sensor","z":"4052caf3dd00c142","name":"Info fra PS til HA","entityConfig":"eab799518168f5a3","version":0,"state":"payload","stateType":"str","attributes":[{"property":"hours","value":"payload.hours","valueType":"msg"},{"property":"configs","value":"payload.configs","valueType":"msg"}],"inputOverride":"allow","outputProperties":[],"x":1100,"y":320,"wires":[[]]},{"id":"6eeacdec9dbd2f3a","type":"junction","z":"4052caf3dd00c142","x":420,"y":300,"wires":[["e27b07b7eb751099","b835b6e1555c8ff2"]]},{"id":"7c8db7b406232476","type":"junction","z":"4052caf3dd00c142","x":660,"y":260,"wires":[["512054092ba0afc8"]]},{"id":"b57f10158dcc79f0","type":"junction","z":"4052caf3dd00c142","x":660,"y":320,"wires":[["512054092ba0afc8"]]},{"id":"a9970d75.fc3e1","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":true,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"id","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true},{"id":"eab799518168f5a3","type":"ha-entity-config","server":"a9970d75.fc3e1","deviceConfig":"","name":"Powersaver_VVB","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Powersaver_VVB"},{"property":"icon","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":true}]
mvjt commented 1 year ago

Takk @marhoy !

marhoy commented 1 year ago

@mvjt Your problem is that you have two schedulers running in parallel, writing their output to the same switch. But the two schedulers might (almost always) disagree on whether the switch should be "On" or "Off". And then you need to figure out how you want to handle that.

In my (and I believe your) case, it makes sense to switch "On" if one or more of the schedulers wants "On" (otherwise turn "Off").

mvjt commented 1 year ago

@mvjt Your problem is that you have two schedulers running in parallel, writing their output to the same switch. But the two schedulers might (almost always) disagree on whether the switch should be "On" or "Off". And then you need to figure out how you want to handle that.

In my (and I believe your) case, it makes sense to switch "On" if one or more of the schedulers wants "On" (otherwise turn "Off").

The two schedulers run at different times. So they do not conflict. It works pretty well but the only issue is that when the night shift ends, there is no schedule for the next day as it has not been released by Nordpool. So first it sends OFF because out of schedule (or end of ON period), then it sends off because there is no new schedule (until 13.00). So it turns off twice at the same time.

marhoy commented 1 year ago

@mvjt It sounds like you misunderstand how this works: It doesn't matter whether the two schedulers overlap wrt. the time-periods they consider. The scheduler is always running and will always (at any point in time) have an opinion on whether things should be On or Off.

Thus, when you combine multiple schedulers, you need to figure out how you will handle disagreement between the schedulers.

mvjt commented 1 year ago

@mvjt It sounds like you misunderstand how this works: It doesn't matter whether the two schedulers overlap wrt. the time-periods they consider. The scheduler is always running and will always (at any point in time) have an opinion on whether things should be On or Off.

Thus, when you combine multiple schedulers, you need to figure out how you will handle disagreement between the schedulers.

I agree, I´ve struggled to understand how things work behind the scenes. Simple things as when rescheduling actually takes place. I understand your point and nice flow but "NR rookies" including myself would not have figured your flow out without your help.

mvjt commented 1 year ago

On top of that my case is slightly more complicated. Im switching my HP between eco, normal and comfort WW modes. And during daytime, I have additional requirement that VV temperature needs to be below a certain temperatur to change from eco to normal/comfort.

KasperEdw commented 1 year ago

My solution is a bit more clumsy, but works. I prefer only to start the flow once at the start of every hour. This is to stay in sync with the hourly rate changes and to make sure it does start again if I have manually turned it off. The get time - switch nodes ensures that signals outside the time bracket of the flow are ignored. Data for the graph is not merged but handled as two separate entities shown in the same graph.

image
mvjt commented 1 year ago

Unfortunately the flow above dit not work well for me. I've been received on/off notifications all night. I'm assuming it is because the rescheduel/out of schedule notifications. Still trying to set off->off and I don´t want these extra writes and notifications. This turned into being more complicated than I thought.

KasperEdw commented 1 year ago

The code from my flow:

[{"id":"6b4758c88b69ce4d","type":"debug","z":"a8b6b9981e2da679","name":"4","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1310,"y":1380,"wires":[]},{"id":"fe800241f1368faa","type":"comment","z":"a8b6b9981e2da679","name":"This last line generate data for visualisation of turn on","info":"","x":1000,"y":1340,"wires":[]},{"id":"48bcdcca.fe42a4","type":"api-current-state","z":"a8b6b9981e2da679","name":"Read Nord Pool","server":"785585e5.21e4ec","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.nordpool_kwh_dk2_dkk_2_10_025","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","x":180,"y":1180,"wires":[["428d7c7ca88db95f"]]},{"id":"428d7c7ca88db95f","type":"ps-receive-price","z":"a8b6b9981e2da679","name":"Price Receiver","x":380,"y":1180,"wires":[["969895a069839adc","818d67dc8015e3ea"]]},{"id":"722542b2abf6fb9e","type":"debug","z":"a8b6b9981e2da679","name":"turn off","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1640,"y":1180,"wires":[]},{"id":"0d5e938e9d84dff9","type":"debug","z":"a8b6b9981e2da679","name":"turn on","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1640,"y":1120,"wires":[]},{"id":"969895a069839adc","type":"ps-strategy-lowest-price","z":"a8b6b9981e2da679","name":"Lowest Price 0-12","fromTime":"00","toTime":"12","hoursOn":"2","maxPrice":"","doNotSplit":true,"sendCurrentValueWhenRescheduling":false,"outputIfNoSchedule":"false","outputOutsidePeriod":"false","contextStorage":"memory","x":610,"y":1180,"wires":[["c8b058f3d2073bd3"],["a0e23e5a07717f69"],["cad33a63f66ef72e"]]},{"id":"9816ad8749248794","type":"api-call-service","z":"a8b6b9981e2da679","name":"","server":"785585e5.21e4ec","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.charge_battery"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1380,"y":1120,"wires":[["0d5e938e9d84dff9"]]},{"id":"cca4366011efc9e4","type":"api-call-service","z":"a8b6b9981e2da679","name":"","server":"785585e5.21e4ec","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.charge_battery"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1420,"y":1180,"wires":[["722542b2abf6fb9e"]]},{"id":"eab799518168f5a3","type":"ha-entity","z":"a8b6b9981e2da679","name":"Info fra PS til HA","server":"785585e5.21e4ec","version":2,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"Powersaver"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""},{"property":"last_reset","value":""}],"state":"payload","stateType":"str","attributes":[{"property":"Schedule","value":"payload.schedule","valueType":"msg"},{"property":"Hours","value":"payload.hours","valueType":"msg"},{"property":"Control","value":"payload.hours[0].onOff","valueType":"str"},{"property":"Current","value":"payload.current","valueType":"str"}],"resend":true,"outputLocation":"payload","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1140,"y":1380,"wires":[["6b4758c88b69ce4d"]]},{"id":"cad33a63f66ef72e","type":"function","z":"a8b6b9981e2da679","name":"Convert true/false to 1/0","func":"msg.payload.hours.forEach(h => h.onOff = h.onOff ? \"1\" : \"0\")\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":910,"y":1380,"wires":[["eab799518168f5a3"]]},{"id":"7e4cd656.31676","type":"inject","z":"a8b6b9981e2da679","name":"Every minute","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"60","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":1080,"wires":[["d9dc9f17.84ef68"]]},{"id":"8183d8ac.01c1e","type":"rbe","z":"a8b6b9981e2da679","name":"Block unless change","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":580,"y":1080,"wires":[["48bcdcca.fe42a4"]]},{"id":"d9dc9f17.84ef68","type":"function","z":"a8b6b9981e2da679","name":"Get Hour","func":"function gethour() {\n var date = new Date();\n var hour = (\"0\"+date.getHours()).substr(-2);\n return hour;\n}\n\nvar hour = gethour();\n\nreturn { payload : hour };","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":1080,"wires":[["8183d8ac.01c1e"]]},{"id":"129548855d36c381","type":"comment","z":"a8b6b9981e2da679","name":"First line ensures a signal is sent at start of every hour, so it matches the change i prices","info":"","x":340,"y":1040,"wires":[]},{"id":"c3d4464a463d4a38","type":"inject","z":"a8b6b9981e2da679","name":"Manual inject","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":110,"y":1260,"wires":[["48bcdcca.fe42a4"]]},{"id":"818d67dc8015e3ea","type":"ps-strategy-lowest-price","z":"a8b6b9981e2da679","name":"Lowest Price 12-24","fromTime":"12","toTime":"00","hoursOn":"2","maxPrice":"","doNotSplit":true,"sendCurrentValueWhenRescheduling":false,"outputIfNoSchedule":"false","outputOutsidePeriod":"false","contextStorage":"memory","x":610,"y":1260,"wires":[["2b77cdeae58bcd95"],["2ee437cede0ffb48"],["6ba57daa4fef5a0d"]]},{"id":"dfd0c49e4ecd2263","type":"debug","z":"a8b6b9981e2da679","name":"4","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1310,"y":1440,"wires":[]},{"id":"48f064bb8601525b","type":"ha-entity","z":"a8b6b9981e2da679","name":"Info fra PS til HA","server":"785585e5.21e4ec","version":2,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"Powersaver2"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""},{"property":"last_reset","value":""}],"state":"payload","stateType":"str","attributes":[{"property":"Schedule","value":"payload.schedule","valueType":"msg"},{"property":"Hours","value":"payload.hours","valueType":"msg"},{"property":"Control","value":"payload.hours[0].onOff","valueType":"str"},{"property":"Current","value":"payload.current","valueType":"str"}],"resend":true,"outputLocation":"payload","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1140,"y":1440,"wires":[["dfd0c49e4ecd2263"]]},{"id":"6ba57daa4fef5a0d","type":"function","z":"a8b6b9981e2da679","name":"Convert true/false to 1/0","func":"msg.payload.hours.forEach(h => h.onOff = h.onOff ? \"1\" : \"0\")\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":910,"y":1440,"wires":[["48f064bb8601525b"]]},{"id":"e21779a957f5c077","type":"ha-entity","z":"a8b6b9981e2da679","name":"AutoCharge00-12","server":"785585e5.21e4ec","version":2,"debugenabled":false,"outputs":2,"entityType":"switch","config":[{"property":"name","value":"AutoCharge00-12"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""},{"property":"last_reset","value":""}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"payload","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":true,"outputPayload":"","outputPayloadType":"str","x":370,"y":1340,"wires":[[],[]]},{"id":"c198e9b0627a758e","type":"ha-entity","z":"a8b6b9981e2da679","name":"AutoCharge12-24","server":"785585e5.21e4ec","version":2,"debugenabled":false,"outputs":2,"entityType":"switch","config":[{"property":"name","value":"AutoCharge12-24"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""},{"property":"last_reset","value":""}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"payload","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":true,"outputPayload":"","outputPayloadType":"str","x":370,"y":1400,"wires":[[],[]]},{"id":"c8b058f3d2073bd3","type":"api-current-state","z":"a8b6b9981e2da679","name":"on/off","server":"785585e5.21e4ec","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"switch.autocharge00_12","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":810,"y":1160,"wires":[["6a9f1c5da17f41cc"]]},{"id":"a0e23e5a07717f69","type":"api-current-state","z":"a8b6b9981e2da679","name":"on/off","server":"785585e5.21e4ec","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"switch.autocharge00_12","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":930,"y":1180,"wires":[["f3ea963531d9983e"]]},{"id":"2ee437cede0ffb48","type":"api-current-state","z":"a8b6b9981e2da679","name":"on/off","server":"785585e5.21e4ec","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"switch.autocharge12_24","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":930,"y":1260,"wires":[["5d668d251b4fc437"]]},{"id":"2b77cdeae58bcd95","type":"api-current-state","z":"a8b6b9981e2da679","name":"on/off","server":"785585e5.21e4ec","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"switch.autocharge12_24","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":810,"y":1240,"wires":[["ef3055cbb07ac178"]]},{"id":"502eebb230a0f69c","type":"comment","z":"a8b6b9981e2da679","name":"Switches for control","info":"","x":370,"y":1300,"wires":[]},{"id":"c1bb3f7538018540","type":"comment","z":"a8b6b9981e2da679","name":"Charge automation: Finds the 2 lowest price points every day and starts charger. ","info":"","x":280,"y":980,"wires":[]},{"id":"1b584d7c58eafc5a","type":"debug","z":"a8b6b9981e2da679","name":"turn off","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1640,"y":1300,"wires":[]},{"id":"04d23292b29195c9","type":"debug","z":"a8b6b9981e2da679","name":"turn on","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1640,"y":1240,"wires":[]},{"id":"027aff94b1a46541","type":"api-call-service","z":"a8b6b9981e2da679","name":"","server":"785585e5.21e4ec","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.charge_battery"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1420,"y":1240,"wires":[["04d23292b29195c9"]]},{"id":"9c03f8ee490a8598","type":"api-call-service","z":"a8b6b9981e2da679","name":"","server":"785585e5.21e4ec","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.charge_battery"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1420,"y":1300,"wires":[["1b584d7c58eafc5a"]]},{"id":"017694f45ea7638c","type":"comment","z":"a8b6b9981e2da679","name":"Update to vinter time","info":"","x":800,"y":1080,"wires":[]},{"id":"6a9f1c5da17f41cc","type":"function","z":"a8b6b9981e2da679","name":"Get time","func":"var now = new Date();\n\nmsg.date =  now.getDate() + \"/\" + ('0' + (now.getMonth()+1)).slice(-2) + \"/\" + now.getFullYear() ;\nmsg.time =  ('0' + (now.getHours())).slice(-2) + \":\" + ('0' + (now.getMinutes())).slice(-2);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1000,"y":1140,"wires":[["36e2c43c48078bbf"]]},{"id":"36e2c43c48078bbf","type":"switch","z":"a8b6b9981e2da679","name":"","property":"time","propertyType":"msg","rules":[{"t":"btwn","v":"00:00","vt":"str","v2":"11:59","v2t":"str"},{"t":"btwn","v":"12:00","vt":"str","v2":"23:59","v2t":"str"}],"checkall":"false","repair":false,"outputs":2,"x":1150,"y":1140,"wires":[["9816ad8749248794"],[]]},{"id":"f3ea963531d9983e","type":"function","z":"a8b6b9981e2da679","name":"Get time","func":"var now = new Date();\n\nmsg.date =  now.getDate() + \"/\" + ('0' + (now.getMonth()+1)).slice(-2) + \"/\" + now.getFullYear() ;\nmsg.time =  ('0' + (now.getHours())).slice(-2) + \":\" + ('0' + (now.getMinutes())).slice(-2);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1100,"y":1180,"wires":[["7e04193f9dfee82f"]]},{"id":"7e04193f9dfee82f","type":"switch","z":"a8b6b9981e2da679","name":"","property":"time","propertyType":"msg","rules":[{"t":"btwn","v":"00:00","vt":"str","v2":"11:59","v2t":"str"},{"t":"btwn","v":"12:00","vt":"str","v2":"23:59","v2t":"str"}],"checkall":"false","repair":false,"outputs":2,"x":1230,"y":1180,"wires":[["cca4366011efc9e4"],[]]},{"id":"5d668d251b4fc437","type":"function","z":"a8b6b9981e2da679","name":"Get time","func":"var now = new Date();\n\nmsg.date =  now.getDate() + \"/\" + ('0' + (now.getMonth()+1)).slice(-2) + \"/\" + now.getFullYear() ;\nmsg.time =  ('0' + (now.getHours())).slice(-2) + \":\" + ('0' + (now.getMinutes())).slice(-2);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1120,"y":1280,"wires":[["d44c1b96bf3d9d54"]]},{"id":"d44c1b96bf3d9d54","type":"switch","z":"a8b6b9981e2da679","name":"","property":"time","propertyType":"msg","rules":[{"t":"btwn","v":"00:00","vt":"str","v2":"11:59","v2t":"str"},{"t":"btwn","v":"12:00","vt":"str","v2":"23:59","v2t":"str"}],"checkall":"false","repair":false,"outputs":2,"x":1250,"y":1280,"wires":[[],["9c03f8ee490a8598"]]},{"id":"ef3055cbb07ac178","type":"function","z":"a8b6b9981e2da679","name":"Get time","func":"var now = new Date();\n\nmsg.date =  now.getDate() + \"/\" + ('0' + (now.getMonth()+1)).slice(-2) + \"/\" + now.getFullYear() ;\nmsg.time =  ('0' + (now.getHours())).slice(-2) + \":\" + ('0' + (now.getMinutes())).slice(-2);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1100,"y":1240,"wires":[["c441bcd8b62d2a1f"]]},{"id":"c441bcd8b62d2a1f","type":"switch","z":"a8b6b9981e2da679","name":"","property":"time","propertyType":"msg","rules":[{"t":"btwn","v":"00:00","vt":"str","v2":"11:59","v2t":"str"},{"t":"btwn","v":"12:00","vt":"str","v2":"23:59","v2t":"str"}],"checkall":"false","repair":false,"outputs":2,"x":1230,"y":1240,"wires":[[],["027aff94b1a46541"]]},{"id":"785585e5.21e4ec","type":"server","name":"Home Assistant","version":1,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":false,"cacheJson":true}]
mvjt commented 1 year ago

@marhoy right now I'm using your flow, which I by the way learned a lot from, thank you. I have two schedules, one between 12-20 and one from 00-07. This means that the "join" node gets 4 messages at every reschedule. 2 x Out Of Schedule and 2 x Rescheduling which means 2 notifications (which is how I'm testing this without invoking writes to the HP). Your example does not have any period without a schedule.

I´ll try disabling send on reschedule to see if it helps...

It would make sense to use best saver but I need to avoid many writes to the heat pump flash memory (tear)

marhoy commented 1 year ago

@mvjt The lowest-price nodes sends output whenever:

  1. It receives (valid) input
  2. The schedule changes (going from on->off or opposite)

If you think you're get too frequent outputs, you need to have a look at the input you're sending to the node.

stenkarlsen commented 1 year ago

@mvjt Flow? Here you go:

[{"id":"6a6f662ebd0290ef","type":"api-call-service","z":"4052caf3dd00c142","name":"Varmtvann På","server":"a9970d75.fc3e1","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.kjeller_varmtvann"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"last","x":1120,"y":140,"wires":[[]]},{"id":"1c71e029af2360c2","type":"api-call-service","z":"4052caf3dd00c142","name":"Varmtvann Av","server":"a9970d75.fc3e1","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.kjeller_varmtvann"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"last","x":1120,"y":200,"wires":[[]]},{"id":"09fae2e61a94c229","type":"trigger-state","z":"4052caf3dd00c142","name":"Varmtvann styring","server":"a9970d75.fc3e1","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityid":"input_select.varmtvann_styring","entityidfiltertype":"exact","debugenabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is_not","comparatorValueDatatype":"str","comparatorValue":"Auto"}],"inputs":0,"outputs":2,"customoutputs":[],"outputinitially":true,"state_type":"str","enableInput":false,"x":130,"y":100,"wires":[["2676dd5683d597e6"],["a619526a0c951619"]]},{"id":"78894ff4a8c06ef8","type":"link in","z":"4052caf3dd00c142","name":"Priser: Tibber + Elvia","links":["17e948d9b2028b3f"],"x":290,"y":300,"wires":[["6eeacdec9dbd2f3a"]],"l":true},{"id":"14ea8c500c7aa2cd","type":"switch","z":"4052caf3dd00c142","name":"On or Off","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"On","vt":"str"},{"t":"eq","v":"Off","vt":"str"}],"checkall":"false","repair":false,"outputs":2,"x":520,"y":80,"wires":[["6a6f662ebd0290ef"],["1c71e029af2360c2"]]},{"id":"a619526a0c951619","type":"change","z":"4052caf3dd00c142","name":"Auto control","rules":[{"t":"set","p":"auto_control","pt":"flow","to":"true","tot":"bool"},{"t":"set","p":"payload","pt":"msg","to":"{\"commands\":{\"sendOutput\":true}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":120,"wires":[["6eeacdec9dbd2f3a"]]},{"id":"2676dd5683d597e6","type":"change","z":"4052caf3dd00c142","name":"Manual control","rules":[{"t":"set","p":"auto_control","pt":"flow","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":80,"wires":[["14ea8c500c7aa2cd"]]},{"id":"e27b07b7eb751099","type":"ps-strategy-lowest-price","z":"4052caf3dd00c142","name":"Lowest Price 20-08","fromTime":"20","toTime":"08","hoursOn":"3","maxPrice":"","doNotSplit":false,"sendCurrentValueWhenRescheduling":true,"outputIfNoSchedule":"false","outputOutsidePeriod":"false","contextStorage":"memory","x":550,"y":260,"wires":[["7c8db7b406232476"],["7c8db7b406232476"],["48cbcd19deb54068"]]},{"id":"b835b6e1555c8ff2","type":"ps-strategy-lowest-price","z":"4052caf3dd00c142","name":"Lowest Price 08-20","fromTime":"08","toTime":"20","hoursOn":"3","maxPrice":"","doNotSplit":false,"sendCurrentValueWhenRescheduling":true,"outputIfNoSchedule":"false","outputOutsidePeriod":"false","contextStorage":"memory","x":550,"y":340,"wires":[["b57f10158dcc79f0"],["b57f10158dcc79f0"],["48cbcd19deb54068"]]},{"id":"512054092ba0afc8","type":"join","z":"4052caf3dd00c142","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"5","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":750,"y":280,"wires":[["8592457f56627fd6"]]},{"id":"8592457f56627fd6","type":"function","z":"4052caf3dd00c142","name":"On / Off","func":"if (!flow.get(\"auto_control\")) {\n    // We're in manual control, bail out\n    return [null, null];\n}\n\nif (msg.payload.some((element) => element)) {\n    // At least one of the schedulers wants \"On\"\n    msg.payload = \"On\";\n    return [msg, null];\n} else {\n    // All schedulers agrees to \"Off\"\n    msg.payload = \"Off\";\n    return [null, msg];\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":880,"y":280,"wires":[["6a6f662ebd0290ef"],["1c71e029af2360c2"]],"outputLabels":["On","Off"]},{"id":"48cbcd19deb54068","type":"join","z":"4052caf3dd00c142","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"5","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":750,"y":320,"wires":[["097efcfedfbe075c"]]},{"id":"097efcfedfbe075c","type":"function","z":"4052caf3dd00c142","name":"Merge schedules","func":"const hours1 = msg.payload[0].hours\nconst hours2 = msg.payload[1].hours\n\nlet hours = []\nfor (let i = 0; i < hours1.length; i++) {\n    hours[i] = {\n        \"start\": hours1[i].start,\n        \"price\": hours1[i].price,\n        \"onOff\": (hours1[i].onOff | hours2[i].onOff)\n    };\n}\n\nmsg.payload = {\n    \"hours\": hours,\n    \"configs\": [msg.payload[0].config, msg.payload[1].config]\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":910,"y":320,"wires":[["9d9e12bd81ccf952"]]},{"id":"9d9e12bd81ccf952","type":"ha-sensor","z":"4052caf3dd00c142","name":"Info fra PS til HA","entityConfig":"eab799518168f5a3","version":0,"state":"payload","stateType":"str","attributes":[{"property":"hours","value":"payload.hours","valueType":"msg"},{"property":"configs","value":"payload.configs","valueType":"msg"}],"inputOverride":"allow","outputProperties":[],"x":1100,"y":320,"wires":[[]]},{"id":"6eeacdec9dbd2f3a","type":"junction","z":"4052caf3dd00c142","x":420,"y":300,"wires":[["e27b07b7eb751099","b835b6e1555c8ff2"]]},{"id":"7c8db7b406232476","type":"junction","z":"4052caf3dd00c142","x":660,"y":260,"wires":[["512054092ba0afc8"]]},{"id":"b57f10158dcc79f0","type":"junction","z":"4052caf3dd00c142","x":660,"y":320,"wires":[["512054092ba0afc8"]]},{"id":"a9970d75.fc3e1","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":true,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"id","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true},{"id":"eab799518168f5a3","type":"ha-entity-config","server":"a9970d75.fc3e1","deviceConfig":"","name":"Powersaver_VVB","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Powersaver_VVB"},{"property":"icon","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":true}]

When I import this flow I get an error on one of the nodes. I've googled and looked around, but cant seem to find any node called "ha-sensor". I have the node-red-contrib-home-assistant-websocket installed, but the "ha-sensor" is not part of it..

image

Where do I find this node?

marhoy commented 1 year ago

@stenkarlsen:

It's included in the standard HA package, and allows you to create entities in HA from Node-RED. https://github.com/zachowj/node-red-contrib-home-assistant-websocket

You also need this HA-integration, in order for the ha-sensor node to work: https://github.com/zachowj/hass-node-red

image

stenkarlsen commented 1 year ago

@stenkarlsen:

It's included in the standard HA package, and allows you to create entities in HA from Node-RED. https://github.com/zachowj/node-red-contrib-home-assistant-websocket

You also need this HA-integration, in order for the ha-sensor node to work: https://github.com/zachowj/hass-node-red

image

Hmmm.. I've got both, and use both, but there is no ha-sensor. What is the difference between the sensor and the entity node?

marhoy commented 1 year ago

@stenkarlsen: Maybe you have an old version? The latest version of node-red-contrib-home-assistant-websocket is 0.45.9:

https://github.com/zachowj/node-red-contrib-home-assistant-websocket/tags

stenkarlsen commented 1 year ago

@stenkarlsen: Maybe you have an old version? The latest version of node-red-contrib-home-assistant-websocket is 0.45.9:

https://github.com/zachowj/node-red-contrib-home-assistant-websocket/tags

omg, thank you....:)

mvjt commented 1 year ago

@mvjt The lowest-price nodes sends output whenever:

  1. It receives (valid) input
  2. The schedule changes (going from on->off or opposite)

If you think you're get too frequent outputs, you need to have a look at the input you're sending to the node.

Thanks. I had a bugs on both input and output sides unfortunately (took me a while, was a can't see the forrest because of the trees scenario). But I would still like to be able to NOT output when it receives valid input. But it can of course be workaround with output logic/filtering. Thanks a lot for both this component also your example flow.

ottopaulsen commented 1 year ago

There are some changes related to this in version 4. For example the nodes will try not to send output if the output is not changed, unless send output on rescheduling is checked. Also, it shall send the correct value when there is no more schedule.

Please try, and if there is nothing else, close this issue.