bitte entschuldige, das ich deine Frage erst jetzt lese. Sicherlich hast du dein Setup mittlerweile am Laufen - hoffentlich erfolgreich. Vielleicht ist es aber für den ein oder anderen dennoch interessant.
Ich habe inzwischen komplett umgestellt auf einen node-red Workflow, der den Heizstab über den Home Assistant steuert. Das läuft wesentlich einfacher und stabiler als die dedizierte Microcontroller Variante. Man könnte es vielleicht noch etwas eleganter programmieren aber seit fast 1 Jahr läuft einfach unauffällig und problemlos im Hintergrund.
Einen extra Schütz o.ä. braucht es meiner Meinung nach nicht, denn der Shelly ist für die Last gut ausgelegt.
Code: Alles auswählen
[{"id":"0a0276460d829075","type":"tab","label":"Flow 1","disabled":true,"info":"","env":[]},{"id":"fc490da216fe860d","type":"server-state-changed","z":"0a0276460d829075","name":"Leistung Heizstab","server":"e9794b34.b6a698","version":5,"outputs":1,"exposeAsEntityConfig":"","entityId":"sensor.heizstab_leistung","entityIdType":"exact","outputInitially":true,"stateType":"str","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":230,"y":260,"wires":[["fb48de5814c27c80"]]},{"id":"f6aea31ef77b852b","type":"function","z":"0a0276460d829075","name":"Neue Zielleistung ermitteln","func":"var msg_out1 = { topic: 'netto_leistung', payload: 0 };\nvar msg_out2 = { topic: 'zielleistung', payload: 0 };\nvar netto_leistung = 0; // Netzbezug als wäre kein Heizstab aktiv\nvar zielleistung = 0; // Neue Heizstableistung gemäss Netzbezug\n\n// Wir duschen gerade\nif (flow.get('duschen') == '1') {\n node.warn(\"Leistungsänderung nicht erlaubt. Es wird geduscht.\");\n return null;\n}\n// Neuen Netzbzug speichern\nflow.set('evu', msg.payload); \n\nif (parseFloat(flow.get('evu')) > 0) {\n// Wenn Strom Importiert wird, muss die aktuelle Heizstab Leistung abgezogen werden\n netto_leistung = parseFloat(flow.get('evu')) - parseFloat(flow.get('hs'));\n} else {\n// Wenn Strom Exportiert wird, muss die aktuelle Heizstab Leistung addiert werden\n netto_leistung = parseFloat(flow.get('evu')) - parseFloat(flow.get('hs'));\n}\nmsg_out1.payload = netto_leistung;\n\n// Wenn Strom übrig wäre, wenn der Heizstab nicht eingeschaltet wäre\nif (netto_leistung >= -450 && netto_leistung <= 20) {\n msg_out2.payload = 0;\n} else if (netto_leistung >= -950 && netto_leistung <= -451) {\n msg_out2.payload = 500;\n} else if (netto_leistung >= -1450 && netto_leistung <= -951) {\n msg_out2.payload = 1000;\n} else if (netto_leistung >= -1950 && netto_leistung <= -1451) {\n msg_out2.payload = 1500;\n} else if (netto_leistung >= -2450 && netto_leistung <= -1951) {\n msg_out2.payload = 2000;\n} else if (netto_leistung >= -2950 && netto_leistung <= -2451) {\n msg_out2.payload = 2500;\n} else if (netto_leistung >= -3450 && netto_leistung <= -2951 ) {\n msg_out2.payload = 3000;\n} else if (netto_leistung >= -13000 && netto_leistung <= -3451) {\n msg_out2.payload = 3500;\n} else {\n msg_out2.payload = 0;\n}\n\nnode.warn(\"Neuer Netzbezug: \" + flow.get('evu') +\n \"\\nAktuelle HS Leistung: \" + flow.get('hs') +\n \"\\nNeue Nettoleistung: \" + netto_leistung +\n \"\\nNeue Zielleistung: \" + msg_out2.payload);\n\nreturn [msg_out1, msg_out2];","outputs":2,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":340,"wires":[[],["9773276d83cf71bd"]]},{"id":"ca5c0c6811367f59","type":"server-state-changed","z":"0a0276460d829075","name":"Ladekabel eingesteckt","server":"e9794b34.b6a698","version":5,"outputs":1,"exposeAsEntityConfig":"","entityId":"binary_sensor.openwb_cp1_ladekabel","entityIdType":"exact","outputInitially":true,"stateType":"str","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":240,"y":520,"wires":[["b57bace3acdc480f"]]},{"id":"0bb1872291f2c515","type":"server-state-changed","z":"0a0276460d829075","name":"Speicher Temp Mitte","server":"e9794b34.b6a698","version":5,"outputs":1,"exposeAsEntityConfig":"","entityId":"sensor.shelly_thermostat_temperature_2","entityIdType":"exact","outputInitially":true,"stateType":"str","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":230,"y":440,"wires":[["cfdd958ce5b5431a"]]},{"id":"9e48811ad904686c","type":"server-state-changed","z":"0a0276460d829075","name":"Ladeleistung","server":"e9794b34.b6a698","version":5,"outputs":1,"exposeAsEntityConfig":"","entityId":"sensor.openwb_cp1_ladeleistung","entityIdType":"exact","outputInitially":true,"stateType":"str","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":210,"y":680,"wires":[["a2406598d307acc8"]]},{"id":"eff131dd6becf337","type":"server-state-changed","z":"0a0276460d829075","name":"Netzbezug","server":"e9794b34.b6a698","version":5,"outputs":1,"exposeAsEntityConfig":"","entityId":"sensor.pv_actual_import_watt","entityIdType":"exact","outputInitially":true,"stateType":"str","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":200,"y":340,"wires":[["f6aea31ef77b852b"]]},{"id":"e46e4ed1cadaf0f8","type":"function","z":"0a0276460d829075","name":"Abschalten","func":"var maxtemp = 60.0;\nvar msg_out1 = { payload: false };\nvar msg_out2 = { payload: false };\nvar msg_out3 = { payload: false };\nvar msg_null = { payload: 'NULL' };\nvar timestamp = Date.now();\nvar tz = \"Europe/Berlin\";\nvar df = new Intl.DateTimeFormat('de-DE', { timeZone: tz, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' });\nvar formattedDate = df.format(timestamp);\n\nif (msg.payload === true) {\n node.warn(\"Abschaltung: \" + formattedDate);\n flow.set('letzte_schaltung', timestamp); \n flow.set('hs_zielleistung', 0);\n return [msg_out1, msg_out2, msg_out3];\n} ","outputs":3,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":810,"y":440,"wires":[["1bb1d9bdeda899b2"],["c32f96a441207cbf"],["fce33af06c32d18a"]]},{"id":"1bb1d9bdeda899b2","type":"switch","z":"0a0276460d829075","name":"An/Aus","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":1300,"y":200,"wires":[["04e772d78c8bee1d"],["cbd1f9236fedd6b1"]]},{"id":"04e772d78c8bee1d","type":"api-call-service","z":"0a0276460d829075","name":"Heizstab 0 AN","server":"e9794b34.b6a698","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.shellypro4pm_083af27ca9ec"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1480,"y":160,"wires":[[]]},{"id":"cbd1f9236fedd6b1","type":"api-call-service","z":"0a0276460d829075","name":"Heizstab 0 Aus","server":"e9794b34.b6a698","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.shellypro4pm_083af27ca9ec"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1480,"y":240,"wires":[[]]},{"id":"c32f96a441207cbf","type":"switch","z":"0a0276460d829075","name":"An/Aus","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":1300,"y":340,"wires":[["0dd59b5ce584ee7d"],["d308fd5ee9e4bb64"]]},{"id":"0dd59b5ce584ee7d","type":"api-call-service","z":"0a0276460d829075","name":"Heizstab 1 AN","server":"e9794b34.b6a698","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.shellypro4pm_083af27ca9ec_2"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1480,"y":300,"wires":[[]]},{"id":"d308fd5ee9e4bb64","type":"api-call-service","z":"0a0276460d829075","name":"Heizstab 1 Aus","server":"e9794b34.b6a698","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.shellypro4pm_083af27ca9ec_2"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1480,"y":380,"wires":[[]]},{"id":"fce33af06c32d18a","type":"switch","z":"0a0276460d829075","name":"An/Aus","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":1300,"y":480,"wires":[["52755be575fb2f90"],["f309020e6e320793"]]},{"id":"52755be575fb2f90","type":"api-call-service","z":"0a0276460d829075","name":"Heizstab 2 AN","server":"e9794b34.b6a698","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.shellypro4pm_083af27ca9ec_3"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1480,"y":440,"wires":[[]]},{"id":"f309020e6e320793","type":"api-call-service","z":"0a0276460d829075","name":"Heizstab 2 Aus","server":"e9794b34.b6a698","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.shellypro4pm_083af27ca9ec_3"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1480,"y":520,"wires":[[]]},{"id":"cfdd958ce5b5431a","type":"function","z":"0a0276460d829075","name":"Maximal Temperatur","func":"flow.set('act_temp', msg.payload); \n\nif (flow.get('act_temp') >= flow.get('max_temp')) {\n node.warn(\"Maximaltemperatur erreicht\");\n node.warn(flow.get('act_temp'));\n node.warn(flow.get('max_temp'));\n return { payload: true };\n} else {\n return { payload: 'NULL' };\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":440,"wires":[["e46e4ed1cadaf0f8"]]},{"id":"1aafa6d8d73e4995","type":"function","z":"0a0276460d829075","name":"Schaltzustand setzen","func":"var HS_1_0 = { payload: false };\nvar HS_2_0 = { payload: false };\nvar HS_3_0 = { payload: false };\nvar HS_1_1 = { payload: true };\nvar HS_2_1 = { payload: true };\nvar HS_3_1 = { payload: true };\nvar msg_null = { payload: 'NULL' };\nvar timestamp = Date.now();\nvar timestamp2 = Date.now();\nvar tz = \"Europe/Berlin\";\nvar df = new Intl.DateTimeFormat('de-DE', { timeZone: tz, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' });\nvar formattedDate = df.format(timestamp);\n\nflow.set('letzte_schaltung', timestamp2);\nnode.warn(\"Umschaltung: \" + formattedDate);\n\nflow.set('hs_zielleistung', msg.payload);\nnode.warn(\"Neue Zielleistung: \" + flow.get('hs_zielleistung'));\n\n\nswitch (msg.payload) {\n case 0:\n return [HS_1_0, HS_2_0, HS_3_0];\n break;\n case 500:\n return [HS_1_1, HS_2_0, HS_3_0];\n break;\n case 1000:\n return [HS_1_0, HS_2_1, HS_3_0];\n break;\n case 1500:\n return [HS_1_1, HS_2_1, HS_3_0];\n break;\n case 2000:\n return [HS_1_0, HS_2_0, HS_3_1];\n break;\n case 2500:\n return [HS_1_1, HS_2_0, HS_1_1];\n break;\n case 3000:\n return [HS_1_0, HS_2_1, HS_3_1];\n break;\n case 3500:\n return [HS_1_1, HS_2_1, HS_3_1];\n break;\n default:\n return [HS_1_0, HS_2_0, HS_3_0]; \n}\n\n","outputs":3,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1060,"y":340,"wires":[["1bb1d9bdeda899b2"],["c32f96a441207cbf"],["fce33af06c32d18a"]]},{"id":"9773276d83cf71bd","type":"function","z":"0a0276460d829075","name":"Schalten erlaubt?","func":"var timestamp = Date.now();\nvar diff = timestamp - flow.get('letzte_schaltung');\nvar diff2 = timestamp - flow.get('plug_time');\nvar tz = \"Europe/Berlin\";\nvar df = new Intl.DateTimeFormat('de-DE', { timeZone: tz, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' });\nvar formattedDate = df.format(timestamp);\nvar date = new Date(timestamp);\nvar hour = date.getHours();\n\n// Die INIT Funktion wurde noch nicht ausgeführt\nif (flow.get('init_done') != 'OK') {\n node.warn(\"Kein Schalten - Die INIT Funktion wurde noch nicht getriggert!\");\n return null;\n}\n\n// Es ist Nacht\nif ((msg.payload > 0) && (hour > 23 || hour < 6)) {\n node.warn(\"!!! LOGIK FEHLER - ES IST NACHT !!! \" + formattedDate);\n return null;\n}\n\n// Das Ladekabel wurde gerade erst eingesteckt\nif ((flow.get('ladekabel') == 'on') && (diff2 < flow.get('plug_wait_time'))) {\n node.warn(\"Noch kein Schalten erlaubt - Das Ladekabel wurde gerade eingesteckt!\" +\n \"\\nAktueller Timestamp: \" + timestamp + \n \"\\nKabel eingesteckt: \" + flow.get('plug_time') + \n \"\\nDifferenz: \" + diff2 + \n \"\\nWartezeit: \" + flow.get('plug_wait_time'));\n return null;\n}\n\n// Die Maximaltemperatur im Pufferspeicher ist erreicht\nif (flow.get('act_temp') >= flow.get('max_temp')) {\n node.warn(\"Schalten nicht möglich. Maximaltemperatur erreicht\");\n node.warn(flow.get('act_temp'));\n node.warn(flow.get('max_temp'));\n return null;\n} \n\n// Die Leistung wurde nicht verändert\nif (flow.get('hs_zielleistung') === msg.payload) {\n node.warn(\"Schalten nicht nötig. Neue Leistung = Alte Leistung\");\n node.warn(\"Tatsächliche Heizstab Leistung: \" + flow.get('hs') + \n \"\\nHinterlegte Heizstab Leistung: \" + flow.get('hs_zielleistung') + \n \"\\nGewünschte Heizstab Leistung: \" + msg.payload);\n return null;\n}\n\n/*\nif (flow.get('ladeleistung') < 0) {\n node.warn(\"Kein Schalten - Das Auto möchte laden!\");\n return null;\n}\n*/\n\n// Es wurde gerade erst ein Schaltvorgang durchgeführt\nif (diff < flow.get('retention')) {\n node.warn(\"Schalten noch nicht erlaubt. Zeitraum seit letzter Schaltung: \" + diff);\n return null;\n}\n\nreturn msg;\n\n\n\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":810,"y":340,"wires":[["1aafa6d8d73e4995"]]},{"id":"74ba08065973355e","type":"function","z":"0a0276460d829075","name":"INIT","func":"flow.set('init_done', 'OK'); // Stellt sicher das INIT ausgeführt wurde\n\nflow.set('letzte_schaltung', 0); // Zeitpunkt der letzten Umschaltung der Heizstableistung\nflow.set('retention', 60000); // Mindest Zeit zwischen 2 Schaltungen in ms (= 60 Sek.)\n\nflow.set('max_temp', 60.0); // Maximaltemperatur für Pufferspeicher\nflow.set('act_temp', 99.0); // Aktuelle Temperatur Pufferspeicher\n\nflow.set('evu', 0); // Aktueller Netzbezug\nflow.set('hs', 0); // Aktuelle Heizstab Leistung\n\nflow.set('hs_zielleistung', 0); // Zuletzt geschaltete Leistung\n\nflow.set('ladeleistung', 0); // Aktuelle Ladeleistung\nflow.set('ladekabel', 'off'); // Ladekabel eingesteckt\nflow.set('plug_time', 0); // Wann wurde das Ladekabel eingesteckt\nflow.set('plug_wait_time', 240000); // Wie lange soll gewartet werden, nachdem das Ladekabelkabel eingestekct wurde\n\nflow.set('duschen', 0); // Instant Hot Water\n\nnode.warn(\"***** GLOBALE VARIABLEN INITIALISIERT *****\");\nnode.warn(\"***** Heiszstab AUS nach Neustart ***** \");\n\nreturn msg;\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":200,"wires":[["e46e4ed1cadaf0f8"]]},{"id":"3ffb14f91ed7f2f8","type":"inject","z":"0a0276460d829075","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":190,"y":200,"wires":[["74ba08065973355e"]]},{"id":"a2406598d307acc8","type":"function","z":"0a0276460d829075","name":"Ladeleistung speichern","func":"flow.set('ladeleistung', msg.payload);\nnode.warn(\"Änderung Ladeleistung auf \" + msg.payload);\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":680,"wires":[[]]},{"id":"b57bace3acdc480f","type":"function","z":"0a0276460d829075","name":"Kabelstatus speichern","func":"var timestamp = Date.now();\nvar timestamp2 = Date.now();\nvar tz = \"Europe/Berlin\";\nvar df = new Intl.DateTimeFormat('de-DE', { timeZone: tz, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' });\nvar formattedDate = df.format(timestamp);\n\nflow.set('ladekabel', msg.payload);\nif (msg.payload == 'off') {\n node.warn(\"Ladekabel ausgezogen: \" + formattedDate);\n flow.set('plug_time', 0);\n return { payload: false };\n} else if (msg.payload == 'on') {\n node.warn(\"Ladekabel eingesteckt: \" + formattedDate);\n flow.set('plug_time', timestamp);\n return { payload: true };\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":520,"wires":[["e46e4ed1cadaf0f8"]]},{"id":"fb48de5814c27c80","type":"function","z":"0a0276460d829075","name":"Heizstableistung speichern","func":"flow.set('hs', msg.payload); \nnode.warn(\"Aktuelle Heizstab Leistung: \" + flow.get('hs'));","outputs":0,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":260,"wires":[]},{"id":"e9794b34.b6a698","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}]