NodeRed, ESP32, Tasmota, photovoltaic installation and control 3 heaters

Hello

Using ESP32 with Tasmota and NodeRed software, I want to control 3 heaters (2kV - domestic hot water, 4.2kV and 6kV - heating the house in winter). I also have a photovoltaic installation with a Fronius inverter. Device via API I can ask about current production. I live in Poland and the price of electricity depends on the time of day. I check the cheap price using my JS function available on NPM cheap-electricity-tariff - npm
I would like to maximize the production of electricity from the PV installation and turn on one or several heaters depending on the amount of production.
Scenarios:

  • small production and expensive electricity tariff - heaters turned off
  • PV production 8kVh - 6kV or 2kV and 4.2kV heater on
  • PV production 3kV - 2kV heater turned on
  • small production and cheap tariff, all heaters on (in winter, 2kV only in summer)
  • e.t.c ...

I know how to download data from PV and cheap electricity tariff separately, but how to connect it? PV data is asynhronic. Electricity price data calculated locally on Synology NAS running NodeRed

[
    {
        "id": "db1af05c8e10d423",
        "type": "inject",
        "z": "620fe87e26a122f7",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "Cheap tariff winter",
        "payloadType": "str",
        "x": 160,
        "y": 660,
        "wires": [
            [
                "3258b0e2cda8ba1e"
            ]
        ]
    },
    {
        "id": "d89fab58f598f5e0",
        "type": "debug",
        "z": "620fe87e26a122f7",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 590,
        "y": 740,
        "wires": []
    },
    {
        "id": "efff1d3429cdfc91",
        "type": "http request",
        "z": "620fe87e26a122f7",
        "name": "",
        "method": "GET",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "https://fotowoltaikaapi.wachcio.pl/inverter-realtime-data/common-inverter-data",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "senderr": true,
        "x": 290,
        "y": 500,
        "wires": [
            [
                "f3f9ad6281a8ef15"
            ]
        ]
    },
    {
        "id": "ca76e1ffeec4db82",
        "type": "debug",
        "z": "620fe87e26a122f7",
        "name": "Fronius",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 840,
        "y": 500,
        "wires": []
    },
    {
        "id": "f9c179e6c0dcc08c",
        "type": "inject",
        "z": "620fe87e26a122f7",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 120,
        "y": 500,
        "wires": [
            [
                "efff1d3429cdfc91"
            ]
        ]
    },
    {
        "id": "f3f9ad6281a8ef15",
        "type": "json",
        "z": "620fe87e26a122f7",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 450,
        "y": 500,
        "wires": [
            [
                "4fe070fd15b99bf1"
            ]
        ]
    },
    {
        "id": "4fe070fd15b99bf1",
        "type": "function",
        "z": "620fe87e26a122f7",
        "name": "Check PV prodution",
        "func": "return {PAC:msg.payload.Body?.Data?.PAC?.Value ?? 0};",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 640,
        "y": 500,
        "wires": [
            [
                "ca76e1ffeec4db82"
            ]
        ]
    },
    {
        "id": "3258b0e2cda8ba1e",
        "type": "function",
        "z": "620fe87e26a122f7",
        "name": "Is cheap tariff?",
        "func": "const isCheap = global.get('isCheapTariff')\nconst onOff = isCheap.isCheapTariff() ? 'ON' : 'OFF'\nreturn {payload:onOff, message:`Cheap tariff. Heating ${onOff}`};",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 380,
        "y": 660,
        "wires": [
            [
                "0f1a557865989965",
                "e9206c9cb09141dd",
                "c50f76599d184b68",
                "d89fab58f598f5e0"
            ]
        ]
    },
    {
        "id": "0f1a557865989965",
        "type": "mqtt out",
        "z": "620fe87e26a122f7",
        "name": "Heater 6kV",
        "topic": "cmnd/kotlownia/POWER1",
        "qos": "1",
        "retain": "false",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "e867dbc3.269d78",
        "x": 610,
        "y": 560,
        "wires": []
    },
    {
        "id": "e9206c9cb09141dd",
        "type": "mqtt out",
        "z": "620fe87e26a122f7",
        "name": "Heater 4_2kV",
        "topic": "cmnd/kotlownia/POWER2",
        "qos": "1",
        "retain": "false",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "e867dbc3.269d78",
        "x": 620,
        "y": 620,
        "wires": []
    },
    {
        "id": "c50f76599d184b68",
        "type": "mqtt out",
        "z": "620fe87e26a122f7",
        "name": "Heater 2kV",
        "topic": "cmnd/kotlownia/POWER3",
        "qos": "1",
        "retain": "false",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "e867dbc3.269d78",
        "x": 610,
        "y": 680,
        "wires": []
    },
    {
        "id": "e867dbc3.269d78",
        "type": "mqtt-broker",
        "name": "NAS MQTT server",
        "broker": "192.168.2.2",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "sessionExpiry": ""
    }
]

You can send payloads to separate outputs of function node
Here is an example , not sure of contents of isCheap so you may have to edit slightly.

[{"id":"f9c179e6c0dcc08c","type":"inject","z":"bf9e1e33.030598","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":190,"y":4100,"wires":[["efff1d3429cdfc91"]]},{"id":"efff1d3429cdfc91","type":"http request","z":"bf9e1e33.030598","name":"","method":"GET","ret":"txt","paytoqs":"ignore","url":"https://fotowoltaikaapi.wachcio.pl/inverter-realtime-data/common-inverter-data","tls":"","persist":false,"proxy":"","authType":"","x":270,"y":4060,"wires":[["f3f9ad6281a8ef15"]]},{"id":"f3f9ad6281a8ef15","type":"json","z":"bf9e1e33.030598","name":"","property":"payload","action":"","pretty":false,"x":430,"y":4060,"wires":[["4fe070fd15b99bf1"]]},{"id":"4fe070fd15b99bf1","type":"function","z":"bf9e1e33.030598","name":"Check PV prodution","func":"return {PAC:(msg.payload.Body.Data.PAC.Value || 0)};","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":620,"y":4060,"wires":[["ca76e1ffeec4db82","3258b0e2cda8ba1e"]]},{"id":"ca76e1ffeec4db82","type":"debug","z":"bf9e1e33.030598","name":"Fronius","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":820,"y":4060,"wires":[]},{"id":"3258b0e2cda8ba1e","type":"function","z":"bf9e1e33.030598","name":"Is cheap tariff?","func":"const output = [null,null,null];\nconst isCheap = global.get('isCheapTariff');\nif(isCheap){\n    output[0] = {payload: \"ON\",  message: \"Cheap tariff. Heating On\"};\n}else if(msg.PAC > 6000){\n    output[1] = {payload: \"ON\",  message: \"two heaters on\"};\n}else if(msg.PAC > 2000){ \n    output[2] = {payload: \"ON\",  message: \"one heater on\"};\n}\nreturn output;","outputs":3,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":4220,"wires":[["d89fab58f598f5e0"],["6a82a440.371c44"],["6369dce3.54ab1c"]]},{"id":"db1af05c8e10d423","type":"inject","z":"bf9e1e33.030598","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Cheap tariff winter","payloadType":"str","x":140,"y":4220,"wires":[["3258b0e2cda8ba1e"]]},{"id":"d89fab58f598f5e0","type":"debug","z":"bf9e1e33.030598","name":"cheap","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":600,"y":4160,"wires":[]},{"id":"6a82a440.371c44","type":"debug","z":"bf9e1e33.030598","name":"chabove 6000","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":590,"y":4220,"wires":[]},{"id":"6369dce3.54ab1c","type":"debug","z":"bf9e1e33.030598","name":"above 2000","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":580,"y":4260,"wires":[]}]
const output = [null,null,null];
const isCheap = global.get('isCheapTariff');
if(isCheap){
    output[0] = {payload: "ON",  message: "Cheap tariff. Heating On"};
}else if(msg.PAC > 6000){
    output[1] = {payload: "ON",  message: "two heaters on"};
}else if(msg.PAC > 2000){ 
    output[2] = {payload: "ON",  message: "one heater on"};
}
return output;

You will probably edit the payloads and messages, you can do different things depending which output is returned. Hope it gives you some ideas.

I used the was @E1cid told you. I'm just experimenting and not really satisfied. But perhaps it helps you:

sorry, I can't input my code here, because than it exceeds the maximum size of a post.
If you are interrested, give me your eMail

The second path Boiler "Anbau" is working fine so far. The delay is for not to switch to often.

But I have to handle different heater with different consumption and I'm not sure, how to handle it in a simle way. As you mention, I have to calculate the best combination of heaters. So I have to know, which heater is on and add it from my "free" PV power.

e.g.if you have a h1 with 400W and h2 with 1000W than

overflow < 400: h1 = h2 = off
overflow > 400: h1 = on, h2 = off
overflow > 600: h1 = off, h2 = on ; PV overflow = 600+400 from h1 (if I switch off h1, than I can use more overflow from PV with h2)
and so on

I have to test, but for now I have set up global variables

[
    {
        "id": "c11f89348084aa68",
        "type": "inject",
        "z": "322b572fa9efb491",
        "name": "",
        "props": [
            {
                "p": "payload.pv_production",
                "v": "pv_production",
                "vt": "global"
            },
            {
                "p": "payload.season",
                "v": "season",
                "vt": "global"
            },
            {
                "p": "payload.edge1",
                "v": "edge1",
                "vt": "global"
            },
            {
                "p": "payload.edge2",
                "v": "edge2",
                "vt": "global"
            },
            {
                "p": "payload.edge3",
                "v": "edge3",
                "vt": "global"
            },
            {
                "p": "payload.heater_boiler",
                "v": "heater_boiler",
                "vt": "global"
            },
            {
                "p": "payload.heater_big",
                "v": "heater_big",
                "vt": "global"
            },
            {
                "p": "payload.heater_small",
                "v": "heater_small",
                "vt": "global"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 90,
        "y": 260,
        "wires": [
            [
                "194d28bb9ac21367"
            ]
        ]
    },
    {
        "id": "194d28bb9ac21367",
        "type": "debug",
        "z": "322b572fa9efb491",
        "name": "pv_production",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 280,
        "y": 260,
        "wires": []
    },
    {
        "id": "49c1b41b4d28fcf9",
        "type": "change",
        "z": "322b572fa9efb491",
        "name": "Set sommer season",
        "rules": [
            {
                "t": "set",
                "p": "season",
                "pt": "global",
                "to": "sommer",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 300,
        "y": 40,
        "wires": [
            []
        ]
    },
    {
        "id": "17f8ee9259f43dd8",
        "type": "inject",
        "z": "322b572fa9efb491",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 100,
        "y": 40,
        "wires": [
            [
                "49c1b41b4d28fcf9"
            ]
        ]
    },
    {
        "id": "77b6c3ecea904294",
        "type": "change",
        "z": "322b572fa9efb491",
        "name": "Set winter season",
        "rules": [
            {
                "t": "set",
                "p": "season",
                "pt": "global",
                "to": "winter",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 290,
        "y": 80,
        "wires": [
            []
        ]
    },
    {
        "id": "4f0e96476b4e435f",
        "type": "inject",
        "z": "322b572fa9efb491",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 100,
        "y": 80,
        "wires": [
            [
                "77b6c3ecea904294"
            ]
        ]
    },
    {
        "id": "f30684a57b788c6f",
        "type": "change",
        "z": "322b572fa9efb491",
        "name": "Set edges",
        "rules": [
            {
                "t": "set",
                "p": "edge1",
                "pt": "global",
                "to": "2500",
                "tot": "num"
            },
            {
                "t": "set",
                "p": "edge2",
                "pt": "global",
                "to": "4500",
                "tot": "num"
            },
            {
                "t": "set",
                "p": "edge3",
                "pt": "global",
                "to": "6500",
                "tot": "num"
            },
            {
                "t": "set",
                "p": "edge4",
                "pt": "global",
                "to": "8500",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 260,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "7a452cc132a6bdba",
        "type": "inject",
        "z": "322b572fa9efb491",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 100,
        "y": 120,
        "wires": [
            [
                "f30684a57b788c6f"
            ]
        ]
    },
    {
        "id": "d009ba8aae0595e5",
        "type": "change",
        "z": "322b572fa9efb491",
        "name": "Set heaters OFF",
        "rules": [
            {
                "t": "set",
                "p": "heater_boiler",
                "pt": "global",
                "to": "OFF",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "heater_big",
                "pt": "global",
                "to": "OFF",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "heater_small",
                "pt": "global",
                "to": "OFF",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 280,
        "y": 160,
        "wires": [
            []
        ]
    },
    {
        "id": "7f4dd09cd9b7fefd",
        "type": "inject",
        "z": "322b572fa9efb491",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 100,
        "y": 160,
        "wires": [
            [
                "d009ba8aae0595e5"
            ]
        ]
    },
    {
        "id": "966ee4f53c58f5a3",
        "type": "change",
        "z": "322b572fa9efb491",
        "name": "Set heaters ON",
        "rules": [
            {
                "t": "set",
                "p": "heater_boiler",
                "pt": "global",
                "to": "ON",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "heater_big",
                "pt": "global",
                "to": "ON",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "heater_small",
                "pt": "global",
                "to": "ON",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 280,
        "y": 200,
        "wires": [
            []
        ]
    },
    {
        "id": "66fac19ee8bc7be4",
        "type": "inject",
        "z": "322b572fa9efb491",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 100,
        "y": 200,
        "wires": [
            [
                "966ee4f53c58f5a3"
            ]
        ]
    }
]

And I control the heaters according to the conditions

[
    {
        "id": "ebbc9ed939760858",
        "type": "inject",
        "z": "5fec788e673a5c59",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "60",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 130,
        "y": 80,
        "wires": [
            [
                "3d3b8460ad33dab4"
            ]
        ]
    },
    {
        "id": "3d3b8460ad33dab4",
        "type": "http request",
        "z": "5fec788e673a5c59",
        "name": "",
        "method": "GET",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "https://fotowoltaikaapi.wachcio.pl/inverter-realtime-data/common-inverter-data",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 290,
        "y": 80,
        "wires": [
            [
                "23c1f4d86a498581"
            ]
        ]
    },
    {
        "id": "23c1f4d86a498581",
        "type": "json",
        "z": "5fec788e673a5c59",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 440,
        "y": 80,
        "wires": [
            [
                "aaf29bd1ce0858bd"
            ]
        ]
    },
    {
        "id": "aaf29bd1ce0858bd",
        "type": "function",
        "z": "5fec788e673a5c59",
        "name": "Check PV prodution",
        "func": "const PAC = msg.payload.Body?.Data?.PAC?.Value ?? 0;\nglobal.set('PV_production', PAC);\nreturn {PAC};",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 640,
        "y": 80,
        "wires": [
            [
                "0685d67d6fc03490",
                "35b39142c7a214de",
                "8f8c8e32627f738e"
            ]
        ]
    },
    {
        "id": "35b39142c7a214de",
        "type": "function",
        "z": "5fec788e673a5c59",
        "name": "Is cheap tariff?",
        "func": "const output = [null,null,null];\nconst isCheap = global.get('isCheapTariff');\nconst season = global.get('season');\nconst edge1 = global.get('edge1');\nconst edge2 = global.get('edge2');\nconst edge3 = global.get('edge3');\nconst edge4 = global.get('edge4');\n\nconst change_big_heater = (status) => {\n    global.set('heater_big',status);\n    output[0] = {payload: status,  message: `Big heater ${status}`};\n}\nconst change_small_heater = (status) => {\n    global.set('heater_small',status);\n    output[1] = {payload: status,  message: `Small heater ${status}`};\n}\nconst change_boiler = (status) => {\n    global.set('heater_boiler',status);\n    output[2] = {payload: status,  message: `Boiler heater ${status}`};\n}\n\n\nif (season=='sommer') {\n    if(isCheap || msg.PAC > edge1 ){\n        change_boiler('ON');\n        change_big_heater('OFF');\n       change_small_heater('OFF');\n    } else {\n    change_boiler('OFF');\n       change_big_heater('OFF');\n       change_small_heater('OFF');\n    }\n} else {\n    if(isCheap){\n       change_boiler('ON');\n       change_big_heater('ON');\n       change_small_heater('ON');\n    } else if(msg.PAC > edge1 && msg.PAC < edge2  ){\n        change_boiler('ON');\n       change_big_heater('OFF');\n       change_small_heater('OFF');\n    } else if(msg.PAC > edge2 && msg.PAC < edge3  ){\n        change_boiler('OFF');\n       change_big_heater('OFF');\n       change_small_heater('ON');\n    } else if(msg.PAC > edge3 && msg.PAC < edge4){\n        change_boiler('OFF');\n       change_big_heater('ON');\n       change_small_heater('OFF');\n    } else if(msg.PAC > edge4 ){\n        change_boiler('ON');\n       change_big_heater('ON');\n       change_small_heater('OFF');\n    }\n    \n    else {\n        change_boiler('OFF');\n       change_big_heater('OFF');\n       change_small_heater('OFF');\n    }\n     \n}\n\nreturn output;",
        "outputs": 3,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 380,
        "y": 320,
        "wires": [
            [
                "2f7d670920da77f7",
                "5bca51607c6d6c51"
            ],
            [
                "bdb544461d22e427",
                "9c4e99cee35264c3"
            ],
            [
                "f403a6e510400f69",
                "0a3b6b4cf52b6673"
            ]
        ]
    },
    {
        "id": "4397ba4842d851b8",
        "type": "inject",
        "z": "5fec788e673a5c59",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "Cheap tariff",
        "payloadType": "str",
        "x": 130,
        "y": 320,
        "wires": [
            [
                "35b39142c7a214de"
            ]
        ]
    },
    {
        "id": "8f8c8e32627f738e",
        "type": "change",
        "z": "5fec788e673a5c59",
        "name": "Save PV production",
        "rules": [
            {
                "t": "set",
                "p": "pv_production",
                "pt": "global",
                "to": "PAC",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "pv_production",
                "tot": "global"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 940,
        "y": 80,
        "wires": [
            []
        ]
    },
    {
        "id": "5bca51607c6d6c51",
        "type": "mqtt out",
        "z": "5fec788e673a5c59",
        "name": "Heater 6kV",
        "topic": "cmnd/kotlownia/POWER1",
        "qos": "1",
        "retain": "false",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "e867dbc3.269d78",
        "x": 850,
        "y": 220,
        "wires": []
    },
    {
        "id": "9c4e99cee35264c3",
        "type": "mqtt out",
        "z": "5fec788e673a5c59",
        "name": "Heater 4_2kV",
        "topic": "cmnd/kotlownia/POWER2",
        "qos": "1",
        "retain": "false",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "e867dbc3.269d78",
        "x": 860,
        "y": 320,
        "wires": []
    },
    {
        "id": "0a3b6b4cf52b6673",
        "type": "mqtt out",
        "z": "5fec788e673a5c59",
        "name": "Heater boiler",
        "topic": "cmnd/kotlownia/POWER3",
        "qos": "1",
        "retain": "false",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "e867dbc3.269d78",
        "x": 850,
        "y": 420,
        "wires": []
    },
    {
        "id": "2f7d670920da77f7",
        "type": "debug",
        "z": "5fec788e673a5c59",
        "name": "debug heater big",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 870,
        "y": 180,
        "wires": []
    },
    {
        "id": "bdb544461d22e427",
        "type": "debug",
        "z": "5fec788e673a5c59",
        "name": "debug heater small",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 870,
        "y": 280,
        "wires": []
    },
    {
        "id": "f403a6e510400f69",
        "type": "debug",
        "z": "5fec788e673a5c59",
        "name": "debug heatert boiler",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 880,
        "y": 380,
        "wires": []
    },
    {
        "id": "e867dbc3.269d78",
        "type": "mqtt-broker",
        "name": "NAS MQTT server",
        "broker": "192.168.2.2",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "sessionExpiry": ""
    }
]

I still have to physically add the cables to the circulation pumps and add them to the program. Thanks to everyone for your help. I'm going to test :slight_smile:

Thanks, for sharing your code. I learned some more. :slight_smile:

But you don't handle the case I described at my last post at the end. Or did you only connect your 3 heaters, than you don't need this?

I put the PAC into my installation and so the PAC don't tell me, if I have free power. My free power is PAC - used power (in my household).

I have three heaters. 2 in a 900l heat buffer for heating the house and 1 in a water boiler for washing. PAC is the production of electricity from photovoltaics. I would have to have an electricity meter and read from it what is the consumption of the whole house and what is the electricity production. But I don't have it yet.

Why not make up a table for all combinations of the heating and how much power they draw such as

H1, H2, H3 = 12000 (i.e. all 3 heaters on = 12000)
H1, H2 = 8000
etc etc

Put this into an array sorted by power draw from low to high

When you see you have excess solar of X amount you can consult the table/array/object and find the closest match which does not exceed the excess and turn on those heaters - then store the fact they are turned on in a context/global variable

Every minute you check your excess solar and you check what heaters are turned on - consult the array for how much power they are drawing and then add this to the excess solar and see if there is a better combination to turn on

Craig

3 Likes