Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Tester
  4. Neuer Adapter EMS-ESP für Bosch Heizungen

NEWS

  • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?
    apollon77A
    apollon77
    48
    3
    8.7k

  • Monatsrückblick – September 2025
    BluefoxB
    Bluefox
    13
    1
    2.2k

  • Neues Video "KI im Smart Home" - ioBroker plus n8n
    BluefoxB
    Bluefox
    16
    1
    3.1k

Neuer Adapter EMS-ESP für Bosch Heizungen

Geplant Angeheftet Gesperrt Verschoben Tester
apiboschbuderusems-espheizungssteuerungjunkerskm200netfit
743 Beiträge 61 Kommentatoren 209.0k Aufrufe 56 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • T tp1de

    @blackeagle998
    Kann die Grund dafür nicht so richtig nachvollziehen. Einen Neustart habe ich in den letzten Jahren nie gebraucht.
    Geht aber bestimmt mit einem command per http. Wie musst du im ems-esp Discord Forum klären.

    K Offline
    K Offline
    kunigunde
    schrieb am zuletzt editiert von kunigunde
    #722

    Hallo,
    ich suche Tester für das Zusammenspiel Buderus->kmxx->emsesp->NodeRed.

    Ich habe leider bisher noch nichts bestehendes gefunden, was es ermöglicht den Zeitplan komfortabel zu ändern.
    Dies geht entweder über das Bedienpult, oder über die APP.
    Ich wollte es aber über das Node-Red Dashboard anzeigen und ändern können.

    Dies ist nun mein 1. Versuch, und es scheint bei mir zu funktionieren.
    Ok, manchmal kann der EmsEsp nicht schreiben.....
    Aber dazu suche ich ja Tester, welche vielleicht Ideen haben, wie man dies prozesssicherer gestalten kann.

    Als extra node-module wird "node-red-contrib-ui-time-scheduler" benutzt.
    Screenshot.png
    Als Datei zum importieren: flows.json
    als code zum kopieren:


    [
    {
    "id": "b647dcc6973cef0a",
    "type": "subflow",
    "name": "ui->km",
    "category": "",
    "in": [
    {
    "x": 80,
    "y": 240,
    "wires": [
    {
    "id": "a45c49648996ca8c"
    }
    ]
    }
    ],
    "out": [
    {
    "x": 400,
    "y": 300,
    "wires": [
    {
    "id": "a45c49648996ca8c",
    "port": 3
    }
    ]
    }
    ],
    "env": [],
    "meta": {},
    "color": "#DDAA99"
    },
    {
    "id": "a45c49648996ca8c",
    "type": "function",
    "z": "b647dcc6973cef0a",
    "name": "ui->km",
    "func": "// Erzeuge ein Date-Objekt für den aktuellen Tag um 00:00 Uhr\nvar baseDate = new Date();\nbaseDate.setHours(0, 0, 0, 0);\nvar base = baseDate.getTime(); // Basis-Zeitstempel in Millisekunden\n\n// Hilfsfunktion: Wandelt einen absoluten Timestamp (Millisekunden) in Minuten seit Mitternacht um\nfunction fromTimestamp(timestamp) {\n return Math.round((timestamp - base) / 60000);\n}\n\n// Mapping der Tage: Für die Erzeugung der Strings nutzen wir die Reihenfolge, wie sie in den Eingabedaten vorkommen\n// Hier entspricht Index 0 "Su", 1 "Mo", 2 "Tu", usw.\nvar dayMapping = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];\n\n// Ergebnis-Arrays für die beiden Bereiche\nvar outputA = [];\nvar outputB = [];\n\n// Wir erwarten, dass msg.payload.timers ein Array von Timer-Objekten enthält, z. B.:\n// {\n// starttime: <absolute Zeit in ms>,\n// endtime: <absolute Zeit in ms>,\n// days: [0,1,0,0,0,0,0],\n// output: "0" oder "1"\n// }\nmsg.payload.timers.forEach(function (timer) {\n // Wandle absolute Zeiten in Minuten um\n var startMinutes = fromTimestamp(timer.starttime);\n var endMinutes = fromTimestamp(timer.endtime);\n\n // Für jeden Tag prüfen, ob dieser Timer aktiv ist (Wert 1 im days-Array)\n timer.days.forEach(function (active, index) {\n if (active === 1) {\n var day = dayMapping[index];\n // Wähle anhand von timer.output das Ziel-Array\n var targetArray = (timer.output === "0") ? outputA : outputB;\n\n // Erzeuge zwei Objekte: einen für den Start (setpoint "comfort2") und einen für das Ende (setpoint "eco")\n targetArray.push({ dayOfWeek: day, setpoint: "comfort2", time: startMinutes });\n targetArray.push({ dayOfWeek: day, setpoint: "eco", time: endMinutes });\n }\n });\n});\n\n// Sortierfunktion für die Ergebnisse (nach Wochentag und Uhrzeit)\n// Die Sortierreihenfolge wird so festgelegt, dass Montag als erstes kommt.\nvar dayOrder = { "Mo": 0, "Tu": 1, "We": 2, "Th": 3, "Fr": 4, "Sa": 5, "Su": 6 };\n\nfunction sortTimers(arr) {\n arr.sort(function (a, b) {\n // Vergleiche zunächst anhand der Wochentagsreihenfolge\n var dayA = dayOrder[a.dayOfWeek];\n var dayB = dayOrder[b.dayOfWeek];\n if (dayA !== dayB) return dayA - dayB;\n // Danach nach der Uhrzeit\n if (a.time !== b.time) return a.time - b.time;\n // Optional: Falls beides gleich ist, nach setpoint (alphabetisch)\n return a.setpoint.localeCompare(b.setpoint);\n });\n}\n\n// Sortiere beide Bereiche\nsortTimers(outputA);\nsortTimers(outputB);\n\n// Prüfe, ob outputA oder outputB leer sind, und setze sie gegebenenfalls auf einen Default-Array\nvar defaultArray = [\n {"dayOfWeek": "Mo", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "Tu", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "We", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "Th", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "Fr", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "Sa", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "Su", "setpoint": "eco", "time": 1380}\n];\n\nif (outputA.length === 0) {\n outputA = defaultArray;\n}\nif (outputB.length === 0) {\n outputB = defaultArray;\n}\n\n//##########################################\n// Hole das Array disabledDevices\nvar disabledDevices = msg.payload.settings.disabledDevices;\nvar activeSwitchProgram = 0;// Pauschal erstmal Programm A aktivieren\n\n// Überprüfe, ob disabledDevices existiert, ein Array ist und mindestens einen Eintrag enthält\nif (Array.isArray(disabledDevices) && disabledDevices.length > 0) {\n // Wenn disabledDevices[0] den Wert 1 hat, setze activeSwitchProgram auf 0 = Program A\n if (disabledDevices[0] === "1") {\n activeSwitchProgram = 0;\n }\n // Wenn disabledDevices[0] den Wert 0 hat, setze activeSwitchProgram auf 1 = Program B\n else if (disabledDevices[0] === "0") {\n activeSwitchProgram = 1;\n }\n else {\n // Optional: Für den Fall, dass ein anderer Wert vorliegt\n node.warn("Unerwarteter Wert in disabledDevices[0]: " + disabledDevices[0]);\n }\n} else {\n // Optional: Fehlermeldung, wenn disabledDevices nicht existiert oder leer ist\n node.error("disabledDevices existiert nicht oder ist leer.", msg);\n}\n//##########################################\n\n\n// Setze das Ergebnis in msg.payload mit getrennten Bereichen\nvar all_together = {\n timers_A: outputA,\n timers_B: outputB,\n activeSwitchProgram: activeSwitchProgram\n};\n\n//return msg;\n\n// Setze die sortierten Arrays als separate Outputs\nreturn [{ payload: outputA, topic:msg.topic_A }, { payload: outputB, topic: msg.topic_B }, { payload: activeSwitchProgram, topic: msg.activeSwitchProgram }, {payload:all_together}];",
    "outputs": 4,
    "timeout": 0,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 270,
    "y": 240,
    "wires": [
    [
    "3d5f9cdb5af9c500"
    ],
    [
    "ed0b32094cd93163"
    ],
    [
    "baf0eb9c1997313c"
    ],
    []
    ],
    "info": "# Beschreibung der Funktion\r\nDiese Funktion verarbeitet Timer-Daten und bereitet sie in einem spezifischen Format für die weitere Verarbeitung vor. \r\nDabei werden folgende Schritte durchgeführt:\r\n\r\n## 1. Zeit-Basis und Umrechnung:\r\nEs wird ein Basis-Zeitstempel für den aktuellen Tag um 00:00 Uhr erzeugt. Anhand dieses Basiswerts werden absolute Zeitstempel (in Millisekunden) in Minuten seit Mitternacht umgerechnet.\r\n\r\n## 2. Aufbereitung der Timer-Daten:\r\n- Aus der Eingangsstruktur msg.payload.timers werden einzelne Timer-Objekte verarbeitet. \r\n- Für jeden Timer werden die Start- und Endzeiten in Minuten berechnet.\r\n- Über das days-Array des Timers wird ermittelt, an welchen Wochentagen der Timer aktiv ist.\r\n- Für jeden aktiven Tag wird basierend auf dem Timer-Feld output (mit Wert "0" oder "1") ein Eintrag erzeugt:\r\n- - Ein Objekt mit setpoint: "comfort2" und der berechneten Startzeit.\r\n- - Ein Objekt mit setpoint: "eco" und der berechneten Endzeit.\r\n- Diese Objekte werden in zwei separate Arrays einsortiert:\r\n- - outputA für Timer mit output === "0".\r\n- - outputB für Timer mit output === "1".\r\n\r\n## 3. Sortierung:\r\nBeide Arrays werden nach Wochentagen und Uhrzeit sortiert – die Sortierreihenfolge ist so festgelegt, dass Montag als erster Tag erscheint.\r\n\r\n## 4. Fallback bei fehlenden Timer-Daten:\r\nFalls eines der Arrays (outputA oder outputB) nach der Verarbeitung leer sein sollte, \r\nwird es durch einen Default-Array ersetzt. \r\nDieser Default-Array enthält für alle Wochentage (Montag bis Sonntag) Timer-Einträge mit setpoint: "eco" und time: 1380 (entspricht 23:00 Uhr).\r\n\r\n## 5. Bestimmung des aktiven Programms:\r\nEs wird das Array disabledDevices aus msg.payload.settings ausgelesen.\r\nAnhand des Werts in disabledDevices[0] wird der Schalter activeSwitchProgram gesetzt:\r\n- Wenn disabledDevices[0] den String "1" enthält, wird Programm A (Wert 0) aktiv.\r\n- Wenn disabledDevices[0] den String "0" enthält, wird Programm B (Wert 1) aktiv.\r\n\r\n## 6. Ausgabe:\r\nDie Funktion gibt vier Outputs zurück:\r\n\r\n- Output 1: Das Array outputA (Timer für output "0").\r\n- Output 2: Das Array outputB (Timer für output "1").\r\n- Output 3: Der Wert activeSwitchProgram (der aktive Programmschalter).\r\n- Output 4: Ein kombiniertes Objekt (all_together), das beide Timer-Arrays und den aktiven Schalter enthält.\r\n\r\nDiese Funktion sorgt dafür, dass die Timer-Daten aus der Eingangsdatenstruktur in ein konsistentes, \r\nsortiertes Format überführt werden und dass bei fehlenden Timer-Daten ein Standard-Set an Timer-Einträgen bereitgestellt wird. \r\nGleichzeitig wird anhand der Einstellungen in disabledDevices der aktive Programmschalter dynamisch festgelegt."
    },
    {
    "id": "2dfa8c4c402abc2c",
    "type": "ioBroker out",
    "z": "b647dcc6973cef0a",
    "name": "",
    "topic": "",
    "ack": "false",
    "autoCreate": "false",
    "stateName": "",
    "role": "",
    "payloadType": "",
    "readonly": "",
    "stateUnit": "",
    "stateMin": "",
    "stateMax": "",
    "x": 540,
    "y": 180,
    "wires": []
    },
    {
    "id": "f4e8827f63c274f2",
    "type": "ioBroker out",
    "z": "b647dcc6973cef0a",
    "name": "",
    "topic": "",
    "ack": "false",
    "autoCreate": "false",
    "stateName": "",
    "role": "",
    "payloadType": "",
    "readonly": "",
    "stateUnit": "",
    "stateMin": "",
    "stateMax": "",
    "x": 540,
    "y": 220,
    "wires": []
    },
    {
    "id": "baf0eb9c1997313c",
    "type": "ioBroker out",
    "z": "b647dcc6973cef0a",
    "name": "",
    "topic": "",
    "ack": "false",
    "autoCreate": "false",
    "stateName": "",
    "role": "",
    "payloadType": "",
    "readonly": "",
    "stateUnit": "",
    "stateMin": "",
    "stateMax": "",
    "x": 540,
    "y": 260,
    "wires": []
    },
    {
    "id": "3d5f9cdb5af9c500",
    "type": "json",
    "z": "b647dcc6973cef0a",
    "name": "",
    "property": "payload",
    "action": "str",
    "pretty": false,
    "x": 390,
    "y": 180,
    "wires": [
    [
    "2dfa8c4c402abc2c"
    ]
    ]
    },
    {
    "id": "ed0b32094cd93163",
    "type": "json",
    "z": "b647dcc6973cef0a",
    "name": "",
    "property": "payload",
    "action": "str",
    "pretty": false,
    "x": 390,
    "y": 220,
    "wires": [
    [
    "f4e8827f63c274f2"
    ]
    ]
    },
    {
    "id": "110aeb09527e60b1",
    "type": "subflow",
    "name": "Fehlerauswertung",
    "info": "",
    "category": "",
    "in": [
    {
    "x": 60,
    "y": 160,
    "wires": [
    {
    "id": "14d67afb1eb1f40c"
    }
    ]
    }
    ],
    "out": [
    {
    "x": 300,
    "y": 120,
    "wires": [
    {
    "id": "14d67afb1eb1f40c",
    "port": 0
    }
    ]
    },
    {
    "x": 300,
    "y": 200,
    "wires": [
    {
    "id": "14d67afb1eb1f40c",
    "port": 1
    }
    ]
    }
    ],
    "env": [],
    "meta": {},
    "color": "#DDAA99"
    },
    {
    "id": "14d67afb1eb1f40c",
    "type": "function",
    "z": "110aeb09527e60b1",
    "name": "Fehler prüfen",
    "func": "// Variable zur Fehlernachricht initialisieren\nvar errorMessage = null;\n\n// DisabledDevices prüfen (niemals beide Programme oder keines)\n//#############################################################\n// Hole das Array\nvar disabledDevices = msg.payload.settings.disabledDevices;\n\n// Prüfe, ob disabledDevices existiert, ein Array ist und nicht leer ist \n// (Es muss ein Programm abgewählt sein.)\nif (!Array.isArray(disabledDevices) || disabledDevices.length === 0) {\n errorMessage = "Achtung, kein Programm abgewählt (Program A oder B).";\n}\n\n// Prüfe, ob das Array mehr als 1 Element enthält\nif (errorMessage === null && disabledDevices.length > 1) {\n errorMessage = "Achtung, mehr als 1 Programm abgewählt (Program A und B).";\n}\n//#############################################################\n\n// Wenn ein Fehler vorhanden ist, Fehlernachricht an Ausgang 1 senden,\n// ansonsten die Nachricht an Ausgang 2 senden.\nif (errorMessage !== null) {\n msg.highlight = "red";\n msg.topic = "Fehler";\n msg.payload = errorMessage;\n return [msg, null]; // msg an Ausgang 1, Ausgang 2 bleibt leer\n} else {\n msg.hide_save_button = false;// Button einblenden\n return [null, msg]; // msg an Ausgang 2, Ausgang 1 bleibt leer\n}",
    "outputs": 2,
    "timeout": 0,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 170,
    "y": 160,
    "wires": [
    [],
    []
    ]
    },
    {
    "id": "fd7f31397b058e66",
    "type": "subflow",
    "name": "km->ui",
    "info": "",
    "category": "",
    "in": [
    {
    "x": 60,
    "y": 220,
    "wires": [
    {
    "id": "8a64d2a144809a71"
    }
    ]
    }
    ],
    "out": [
    {
    "x": 1420,
    "y": 220,
    "wires": [
    {
    "id": "12c457a5ee91046c",
    "port": 0
    }
    ]
    }
    ],
    "env": [
    {
    "name": "PROGRAMS_A",
    "type": "str",
    "value": "ems-esp.0.heatingCircuits.hc1.switchPrograms.A"
    },
    {
    "name": "PROGRAMS_B",
    "type": "str",
    "value": "ems-esp.0.heatingCircuits.hc1.switchPrograms.B"
    },
    {
    "name": "ACTIVE",
    "type": "str",
    "value": "ems-esp.0.heatingCircuits.hc1.activeSwitchProgram"
    }
    ],
    "meta": {},
    "color": "#DDAA99"
    },
    {
    "id": "34e0423f1724b8f1",
    "type": "function",
    "z": "fd7f31397b058e66",
    "name": "timers_A",
    "func": "// Erzeugt einen Date-Objekt für den aktuellen Tag um 00:00 Uhr\nvar baseDate = new Date();\nbaseDate.setHours(0, 0, 0, 0);\nvar base = baseDate.getTime(); // Basis-Zeitstempel in Millisekunden\n\n// Hilfsfunktion: Wandelt Minuten in einen absoluten Timestamp (Millisekunden) um\nfunction toTimestamp(minutes) {\n return base + minutes * 60000;\n}\n\n/\n Schritt 1: Für jeden Wochentag Start- und Endzeit ermitteln\n Dabei gilt:\n - setpoint "comfort2" → Startzeit\n - setpoint "eco" → Endzeit\n \n Wir erwarten, dass msg.hc1_a ein Array mit Objekten ist, z. B.:\n [\n {"dayOfWeek":"Mo","setpoint":"comfort2","time":270},\n {"dayOfWeek":"Mo","setpoint":"eco","time":420},\n {"dayOfWeek":"Mo","setpoint":"comfort2","time":840},\n {"dayOfWeek":"Mo","setpoint":"eco","time":1200},\n {"dayOfWeek":"Tu","setpoint":"comfort2","time":270},\n {"dayOfWeek":"Tu","setpoint":"eco","time":420},\n ...\n ]\n \n Es können auch mehrere Intervalle pro Tag vorhanden sein. In diesem Beispiel gruppieren\n wir jeweils je 2 Einträge (ein Start- und ein Endwert) – vorausgesetzt, die Daten liegen\n sortiert nach Zeit vor.\n/\nvar dayIntervals = {}; // Beispiel: { "Mo": [ {start:270, end:420}, {start:840, end:1200} ], ... }\n\nmsg.payload.forEach(function(item) {\n var day = item.dayOfWeek;\n // Initialisiere den Tag, falls noch nicht vorhanden\n if (!dayIntervals[day]) {\n dayIntervals[day] = [];\n }\n \n // Wir speichern die Werte vorerst als temporäres Objekt pro Tag\n // Es wird angenommen, dass immer zwei Einträge hintereinander zum gleichen Intervall gehören.\n // Daher speichern wir einfach in der Reihenfolge der Verarbeitung:\n if (item.setpoint === "comfort2") {\n // Neuer Intervall: speichern den Startwert\n dayIntervals[day].push({ start: item.time });\n } else if (item.setpoint === "eco") {\n // Es wird erwartet, dass ein entsprechender Startwert bereits existiert.\n // Daher ergänzen wir den zuletzt eingefügten Intervall.\n if (dayIntervals[day].length > 0 && typeof dayIntervals[day][dayIntervals[day].length - 1].end === "undefined") {\n dayIntervals[day][dayIntervals[day].length - 1].end = item.time;\n } else {\n // Falls nicht vorhanden, kann man hier optional einen neuen Eintrag anlegen oder loggen\n dayIntervals[day].push({ end: item.time });\n }\n }\n});\n\n/\n Schritt 2: Gruppieren über alle Tage – es werden Gruppen gebildet, die dieselbe\n Kombination aus Start‑ und Endzeit besitzen.\n \n Dabei gehen wir wie folgt vor:\n - Für jeden Tag und jedes Intervall wird ein Schlüssel "start_end" erzeugt.\n - Wenn diese Gruppe bereits existiert, wird der Tag hinzugefügt.\n - Existiert sie nicht, wird eine neue Gruppe angelegt.\n \n Wir erstellen ein Objekt "groups" mit folgendem Aufbau:\n \n {\n "270_420": {\n start: 270,\n end: 420,\n days: [0, 0, 0, 0, 0, 0, 0] // 7 Stellen für Su, Mo, Di, Mi, Do, Fr, Sa\n },\n "840_1200": {\n start: 840,\n end: 1200,\n days: [0, 0, 0, 0, 0, 0, 0]\n }\n }\n \n Dabei wird für jeden Tag in der entsprechenden Gruppe der Index im days-Array auf 1 gesetzt.\n/\n\n// Mapping der Wochentage auf Index im days-Array:\n// Index 0 = Sonntag, 1 = Montag, 2 = Dienstag, 3 = Mittwoch, 4 = Donnerstag, 5 = Freitag, 6 = Samstag\nvar weekDayIndex = {\n "Su": 0,\n "Mo": 1,\n "Tu": 2,\n "We": 3,\n "Th": 4,\n "Fr": 5,\n "Sa": 6\n};\n\nvar groups = {};\n\n// Gehe alle Tage durch und alle Intervalle des jeweiligen Tages\nfor (var day in dayIntervals) {\n var intervals = dayIntervals[day];\n intervals.forEach(function(interval) {\n // Nur weiter verarbeiten, wenn sowohl start als auch end vorhanden sind\n if (typeof interval.start !== "undefined" && typeof interval.end !== "undefined") {\n var key = interval.start + "" + interval.end;\n // Falls Gruppe noch nicht existiert, anlegen\n if (!groups[key]) {\n groups[key] = {\n start: interval.start,\n end: interval.end,\n days: [0, 0, 0, 0, 0, 0, 0]\n };\n }\n // Bestimme den Index des aktuellen Tages (ggf. alternative Schreibweisen beachten)\n var idx = weekDayIndex[day];\n if (typeof idx !== "undefined") {\n groups[key].days[idx] = 1;\n }\n }\n });\n}\n\n/\n Schritt 3: Aufbau des finalen Timers-Arrays.\n Für jede Gruppe wird ein Objekt erstellt, das folgende Struktur hat:\n \n {\n "starttime": <absolute Startzeit>,\n "days": [Array mit 7 Werten],\n "output": "0", // oder ein anderer fest definierter Wert\n "endtime": <absolute Endzeit>\n }\n \n Dabei werden die Zeiten über die Funktion toTimestamp() umgerechnet.\n/\n// Zuerst bestimmen wir den Wert von output abhängig vom msg.program\nvar outputValue = "0"; // Standardwert\nif (msg.program === "B") {\n outputValue = "1";\n} else if (msg.program === "A") {\n outputValue = "0";\n}\n\nvar timers = [];\nfor (var key in groups) {\n var grp = groups[key];\n timers.push({\n starttime: toTimestamp(grp.start),\n endtime: toTimestamp(grp.end),\n days: grp.days,\n output: outputValue\n });\n}\n\n/\n Schritt 4: Aufbau des finalen Ausgabeobjekts.\n/\nmsg.payload = timers;\n\nreturn msg;",
    "outputs": 1,
    "timeout": 0,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 760,
    "y": 120,
    "wires": [
    [
    "73d6757f1e5fdf61"
    ]
    ]
    },
    {
    "id": "46437c651a59543a",
    "type": "ioBroker get",
    "z": "fd7f31397b058e66",
    "name": "switchPrograms.A",
    "topic": "",
    "attrname": "payload",
    "payloadType": "value",
    "x": 470,
    "y": 120,
    "wires": [
    [
    "32302fdcb491d2a5"
    ]
    ]
    },
    {
    "id": "32302fdcb491d2a5",
    "type": "json",
    "z": "fd7f31397b058e66",
    "name": "",
    "property": "payload",
    "action": "obj",
    "pretty": false,
    "x": 630,
    "y": 120,
    "wires": [
    [
    "34e0423f1724b8f1"
    ]
    ]
    },
    {
    "id": "8a64d2a144809a71",
    "type": "change",
    "z": "fd7f31397b058e66",
    "name": "SETTINGS",
    "rules": [
    {
    "t": "set",
    "p": "topic_A",
    "pt": "msg",
    "to": "${PROGRAMS_A}",
    "tot": "str"
    },
    {
    "t": "set",
    "p": "topic_B",
    "pt": "msg",
    "to": "${PROGRAMS_B}",
    "tot": "str"
    },
    {
    "t": "set",
    "p": "activeSwitchProgram",
    "pt": "msg",
    "to": "${ACTIVE}",
    "tot": "str"
    }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 210,
    "y": 220,
    "wires": [
    [
    "ceba323fdfd481ed",
    "9a3dc889e642ccd9",
    "3e17abbc9832a241"
    ]
    ]
    },
    {
    "id": "ceba323fdfd481ed",
    "type": "change",
    "z": "fd7f31397b058e66",
    "name": "topic_A->topic",
    "rules": [
    {
    "t": "move",
    "p": "topic_A",
    "pt": "msg",
    "to": "topic",
    "tot": "msg"
    },
    {
    "t": "set",
    "p": "program",
    "pt": "msg",
    "to": "A",
    "tot": "str"
    }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 480,
    "y": 80,
    "wires": [
    [
    "46437c651a59543a"
    ]
    ]
    },
    {
    "id": "dd20b12c905c22fc",
    "type": "function",
    "z": "fd7f31397b058e66",
    "name": "timers_B",
    "func": "// Erzeugt einen Date-Objekt für den aktuellen Tag um 00:00 Uhr\nvar baseDate = new Date();\nbaseDate.setHours(0, 0, 0, 0);\nvar base = baseDate.getTime(); // Basis-Zeitstempel in Millisekunden\n\n// Hilfsfunktion: Wandelt Minuten in einen absoluten Timestamp (Millisekunden) um\nfunction toTimestamp(minutes) {\n return base + minutes * 60000;\n}\n\n/\n Schritt 1: Für jeden Wochentag Start- und Endzeit ermitteln\n Dabei gilt:\n - setpoint "comfort2" → Startzeit\n - setpoint "eco" → Endzeit\n \n Wir erwarten, dass msg.hc1_a ein Array mit Objekten ist, z. B.:\n [\n {"dayOfWeek":"Mo","setpoint":"comfort2","time":270},\n {"dayOfWeek":"Mo","setpoint":"eco","time":420},\n {"dayOfWeek":"Mo","setpoint":"comfort2","time":840},\n {"dayOfWeek":"Mo","setpoint":"eco","time":1200},\n {"dayOfWeek":"Tu","setpoint":"comfort2","time":270},\n {"dayOfWeek":"Tu","setpoint":"eco","time":420},\n ...\n ]\n \n Es können auch mehrere Intervalle pro Tag vorhanden sein. In diesem Beispiel gruppieren\n wir jeweils je 2 Einträge (ein Start- und ein Endwert) – vorausgesetzt, die Daten liegen\n sortiert nach Zeit vor.\n/\nvar dayIntervals = {}; // Beispiel: { "Mo": [ {start:270, end:420}, {start:840, end:1200} ], ... }\n\nmsg.payload.forEach(function(item) {\n var day = item.dayOfWeek;\n // Initialisiere den Tag, falls noch nicht vorhanden\n if (!dayIntervals[day]) {\n dayIntervals[day] = [];\n }\n \n // Wir speichern die Werte vorerst als temporäres Objekt pro Tag\n // Es wird angenommen, dass immer zwei Einträge hintereinander zum gleichen Intervall gehören.\n // Daher speichern wir einfach in der Reihenfolge der Verarbeitung:\n if (item.setpoint === "comfort2") {\n // Neuer Intervall: speichern den Startwert\n dayIntervals[day].push({ start: item.time });\n } else if (item.setpoint === "eco") {\n // Es wird erwartet, dass ein entsprechender Startwert bereits existiert.\n // Daher ergänzen wir den zuletzt eingefügten Intervall.\n if (dayIntervals[day].length > 0 && typeof dayIntervals[day][dayIntervals[day].length - 1].end === "undefined") {\n dayIntervals[day][dayIntervals[day].length - 1].end = item.time;\n } else {\n // Falls nicht vorhanden, kann man hier optional einen neuen Eintrag anlegen oder loggen\n dayIntervals[day].push({ end: item.time });\n }\n }\n});\n\n/\n Schritt 2: Gruppieren über alle Tage – es werden Gruppen gebildet, die dieselbe\n Kombination aus Start‑ und Endzeit besitzen.\n \n Dabei gehen wir wie folgt vor:\n - Für jeden Tag und jedes Intervall wird ein Schlüssel "start_end" erzeugt.\n - Wenn diese Gruppe bereits existiert, wird der Tag hinzugefügt.\n - Existiert sie nicht, wird eine neue Gruppe angelegt.\n \n Wir erstellen ein Objekt "groups" mit folgendem Aufbau:\n \n {\n "270_420": {\n start: 270,\n end: 420,\n days: [0, 0, 0, 0, 0, 0, 0] // 7 Stellen für Su, Mo, Di, Mi, Do, Fr, Sa\n },\n "840_1200": {\n start: 840,\n end: 1200,\n days: [0, 0, 0, 0, 0, 0, 0]\n }\n }\n \n Dabei wird für jeden Tag in der entsprechenden Gruppe der Index im days-Array auf 1 gesetzt.\n/\n\n// Mapping der Wochentage auf Index im days-Array:\n// Index 0 = Sonntag, 1 = Montag, 2 = Dienstag, 3 = Mittwoch, 4 = Donnerstag, 5 = Freitag, 6 = Samstag\nvar weekDayIndex = {\n "Su": 0,\n "Mo": 1,\n "Tu": 2,\n "We": 3,\n "Th": 4,\n "Fr": 5,\n "Sa": 6\n};\n\nvar groups = {};\n\n// Gehe alle Tage durch und alle Intervalle des jeweiligen Tages\nfor (var day in dayIntervals) {\n var intervals = dayIntervals[day];\n intervals.forEach(function(interval) {\n // Nur weiter verarbeiten, wenn sowohl start als auch end vorhanden sind\n if (typeof interval.start !== "undefined" && typeof interval.end !== "undefined") {\n var key = interval.start + "
    " + interval.end;\n // Falls Gruppe noch nicht existiert, anlegen\n if (!groups[key]) {\n groups[key] = {\n start: interval.start,\n end: interval.end,\n days: [0, 0, 0, 0, 0, 0, 0]\n };\n }\n // Bestimme den Index des aktuellen Tages (ggf. alternative Schreibweisen beachten)\n var idx = weekDayIndex[day];\n if (typeof idx !== "undefined") {\n groups[key].days[idx] = 1;\n }\n }\n });\n}\n\n/\n Schritt 3: Aufbau des finalen Timers-Arrays.\n Für jede Gruppe wird ein Objekt erstellt, das folgende Struktur hat:\n \n {\n "starttime": <absolute Startzeit>,\n "days": [Array mit 7 Werten],\n "output": "0", // oder ein anderer fest definierter Wert\n "endtime": <absolute Endzeit>\n }\n \n Dabei werden die Zeiten über die Funktion toTimestamp() umgerechnet.\n/\n// Zuerst bestimmen wir den Wert von output abhängig vom msg.program\nvar outputValue = "0"; // Standardwert\nif (msg.program === "B") {\n outputValue = "1";\n} else if (msg.program === "A") {\n outputValue = "0";\n}\n\nvar timers = [];\nfor (var key in groups) {\n var grp = groups[key];\n timers.push({\n starttime: toTimestamp(grp.start),\n endtime: toTimestamp(grp.end),\n days: grp.days,\n output: outputValue\n });\n}\n\n/\n Schritt 4: Aufbau des finalen Ausgabeobjekts.\n Hier wird zusätzlich ein fester "settings"-Block angehängt.\n\nmsg.payload = {\n timers: timers,\n settings: {\n disabledDevices: ["1"],\n overviewFilter: "all"\n }\n};\n/\nmsg.payload = timers;\n\nreturn msg;",
    "outputs": 1,
    "timeout": 0,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 760,
    "y": 220,
    "wires": [
    [
    "a787ad3e7e0c0361"
    ]
    ]
    },
    {
    "id": "823d912cec3f4a16",
    "type": "ioBroker get",
    "z": "fd7f31397b058e66",
    "name": "switchPrograms.B",
    "topic": "",
    "attrname": "payload",
    "payloadType": "value",
    "x": 470,
    "y": 220,
    "wires": [
    [
    "80c474d6b95f4577"
    ]
    ]
    },
    {
    "id": "80c474d6b95f4577",
    "type": "json",
    "z": "fd7f31397b058e66",
    "name": "",
    "property": "payload",
    "action": "obj",
    "pretty": false,
    "x": 630,
    "y": 220,
    "wires": [
    [
    "dd20b12c905c22fc"
    ]
    ]
    },
    {
    "id": "9a3dc889e642ccd9",
    "type": "change",
    "z": "fd7f31397b058e66",
    "name": "topic_B->topic",
    "rules": [
    {
    "t": "move",
    "p": "topic_B",
    "pt": "msg",
    "to": "topic",
    "tot": "msg"
    },
    {
    "t": "set",
    "p": "program",
    "pt": "msg",
    "to": "B",
    "tot": "str"
    }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 480,
    "y": 180,
    "wires": [
    [
    "823d912cec3f4a16"
    ]
    ]
    },
    {
    "id": "0fae35c54a79d5af",
    "type": "ioBroker get",
    "z": "fd7f31397b058e66",
    "name": "activeSwitchProgram",
    "topic": "",
    "attrname": "payload",
    "payloadType": "value",
    "x": 480,
    "y": 320,
    "wires": [
    [
    "d175fb6bdb10e573"
    ]
    ]
    },
    {
    "id": "3e17abbc9832a241",
    "type": "change",
    "z": "fd7f31397b058e66",
    "name": "activeSwitchProgram->topic",
    "rules": [
    {
    "t": "move",
    "p": "activeSwitchProgram",
    "pt": "msg",
    "to": "topic",
    "tot": "msg"
    }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 500,
    "y": 280,
    "wires": [
    [
    "0fae35c54a79d5af"
    ]
    ]
    },
    {
    "id": "d175fb6bdb10e573",
    "type": "change",
    "z": "fd7f31397b058e66",
    "name": "topic activeSwitchProgram",
    "rules": [
    {
    "t": "set",
    "p": "topic",
    "pt": "msg",
    "to": "activeSwitchProgram",
    "tot": "str"
    }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 900,
    "y": 320,
    "wires": [
    [
    "0b028760a51cdc98"
    ]
    ]
    },
    {
    "id": "73d6757f1e5fdf61",
    "type": "change",
    "z": "fd7f31397b058e66",
    "name": "topic timers_A",
    "rules": [
    {
    "t": "set",
    "p": "topic",
    "pt": "msg",
    "to": "timers_A",
    "tot": "str"
    }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 940,
    "y": 140,
    "wires": [
    [
    "0b028760a51cdc98"
    ]
    ]
    },
    {
    "id": "a787ad3e7e0c0361",
    "type": "change",
    "z": "fd7f31397b058e66",
    "name": "topic timers_B",
    "rules": [
    {
    "t": "set",
    "p": "topic",
    "pt": "msg",
    "to": "timers_B",
    "tot": "str"
    }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 940,
    "y": 220,
    "wires": [
    [
    "0b028760a51cdc98"
    ]
    ]
    },
    {
    "id": "0b028760a51cdc98",
    "type": "join",
    "z": "fd7f31397b058e66",
    "name": "",
    "mode": "custom",
    "build": "object",
    "property": "payload",
    "propertyType": "msg",
    "key": "topic",
    "joiner": "\n",
    "joinerType": "str",
    "accumulate": false,
    "timeout": "",
    "count": "3",
    "reduceRight": false,
    "reduceExp": "",
    "reduceInit": "",
    "reduceInitType": "",
    "reduceFixup": "",
    "x": 1170,
    "y": 220,
    "wires": [
    [
    "12c457a5ee91046c"
    ]
    ]
    },
    {
    "id": "12c457a5ee91046c",
    "type": "function",
    "z": "fd7f31397b058e66",
    "name": "timers",
    "func": "//node.warn(JSON.stringify(msg));\n\nvar timers_A = msg.payload.timers_A || [];\nvar timers_B = msg.payload.timers_B || [];\n\nvar timers = timers_A.concat(timers_B);\n\n// Prüfe den Wert von activeSwitchProgram\nvar disabledDevices = (msg.payload.activeSwitchProgram === 0) ? ["1"] : ["0"];\n\nmsg.payload = {\n timers: timers,\n settings: {\n disabledDevices: disabledDevices,\n overviewFilter: "all"\n }\n};\n\nreturn msg;",
    "outputs": 1,
    "timeout": 0,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 1290,
    "y": 220,
    "wires": [
    []
    ]
    },
    {
    "id": "1dc1799ec4ec93f2",
    "type": "tab",
    "label": "Heizung Zeitprogramm",
    "disabled": false,
    "info": "",
    "env": []
    },
    {
    "id": "34521d86747bd8bd",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "9749f3015788db19",
    "8408c6fde425aef6",
    "b9e930348330f1a7",
    "c419a0b8794650c8",
    "87fcae0db87d8ec7",
    "b71674975643d4c6",
    "64c274d3e12760cd",
    "d86ee423ac01ac1d"
    ],
    "x": 34,
    "y": 13,
    "w": 998,
    "h": 314
    },
    {
    "id": "c6885eaad8ba447a",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "e87bd978436f175d",
    "7a6e354a4dc03390",
    "4497347fc72f4c67"
    ],
    "x": 1054,
    "y": 19,
    "w": 292,
    "h": 122
    },
    {
    "id": "5607675eb8b28b91",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "f22a7d5a405263b9",
    "86b5b09cb63644c1",
    "ce8ae0fc54a4d15a"
    ],
    "x": 34,
    "y": 339,
    "w": 438,
    "h": 475.5
    },
    {
    "id": "87aa28c70609a83b",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "3ee41d6b6e418725",
    "4c8b49a972950636",
    "73cfa9d90e59c8f2"
    ],
    "x": 488,
    "y": 339,
    "w": 344,
    "h": 468
    },
    {
    "id": "bc982b55b594a1be",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "804b40577d582f81",
    "2d7a89955c419a2e",
    "3bd48465ca73fb26"
    ],
    "x": 848,
    "y": 339,
    "w": 644,
    "h": 468
    },
    {
    "id": "64c274d3e12760cd",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "g": "34521d86747bd8bd",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "adcb82cb3b2ca1c0",
    "e991c15e29c9e763",
    "48ce6a5974ab43e7",
    "ae32871f3dfe3184",
    "57973e094f933eaf"
    ],
    "x": 634,
    "y": 179,
    "w": 372,
    "h": 122
    },
    {
    "id": "d86ee423ac01ac1d",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "g": "34521d86747bd8bd",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "72b13674ea7cb2c6",
    "5ae27aa26c57fb34",
    "8a494a52f8a84a72",
    "4730defac1edfe3a",
    "b181124ed41c964c"
    ],
    "x": 634,
    "y": 39,
    "w": 372,
    "h": 122
    },
    {
    "id": "86b5b09cb63644c1",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "g": "5607675eb8b28b91",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "80cbeafa06501ebd",
    "aadc625bce68705a",
    "f27da676130dbb50",
    "a8a5c24a6c414379"
    ],
    "x": 64,
    "y": 659,
    "w": 382,
    "h": 129.5
    },
    {
    "id": "ce8ae0fc54a4d15a",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "g": "5607675eb8b28b91",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "b4059551aadd1ba8",
    "b58755c1aff6132f",
    "494583202e3e8494",
    "10b6a4012722e8ad"
    ],
    "x": 64,
    "y": 399,
    "w": 382,
    "h": 129.5
    },
    {
    "id": "4c8b49a972950636",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "g": "87aa28c70609a83b",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "69b371c33441d3dc",
    "8b1a501f36b9733a",
    "3b325db587bbcdb6"
    ],
    "x": 514,
    "y": 659,
    "w": 292,
    "h": 122
    },
    {
    "id": "73cfa9d90e59c8f2",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "g": "87aa28c70609a83b",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "51e634864245163f",
    "fb67c94b04023f8d",
    "4fce0eb37daebf39"
    ],
    "x": 514,
    "y": 399,
    "w": 292,
    "h": 122
    },
    {
    "id": "2d7a89955c419a2e",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "g": "bc982b55b594a1be",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "e67070774e76ceed",
    "73623095fc2284e5",
    "282f35b8fde4649d",
    "439e908c416c021c",
    "a56607f2cbb3e1d5",
    "bc3a160867da8da4",
    "d0ce33fc89b9d55c"
    ],
    "x": 874,
    "y": 599,
    "w": 592,
    "h": 182
    },
    {
    "id": "3bd48465ca73fb26",
    "type": "group",
    "z": "1dc1799ec4ec93f2",
    "g": "bc982b55b594a1be",
    "style": {
    "stroke": "#999999",
    "stroke-opacity": "1",
    "fill": "none",
    "fill-opacity": "1",
    "label": true,
    "label-position": "nw",
    "color": "#a4a4a4"
    },
    "nodes": [
    "1fbebe44e887c212",
    "4c97aeaa84cf0530",
    "49b29ab6eae048ec",
    "e15fe52156f0da29",
    "62f8829176a49bbb",
    "b6ac03883a2c5fae",
    "0162469a4966cc8d"
    ],
    "x": 874,
    "y": 399,
    "w": 592,
    "h": 182
    },
    {
    "id": "9749f3015788db19",
    "type": "inject",
    "z": "1dc1799ec4ec93f2",
    "g": "34521d86747bd8bd",
    "name": "Trigger",
    "props": [
    {
    "p": "payload"
    },
    {
    "p": "topic",
    "vt": "str"
    }
    ],
    "repeat": "1",
    "crontab": "",
    "once": true,
    "onceDelay": 0.1,
    "topic": "",
    "payload": "",
    "payloadType": "date",
    "x": 140,
    "y": 180,
    "wires": [
    [
    "b9e930348330f1a7"
    ]
    ]
    },
    {
    "id": "72b13674ea7cb2c6",
    "type": "subflow:fd7f31397b058e66",
    "z": "1dc1799ec4ec93f2",
    "g": "d86ee423ac01ac1d",
    "name": "",
    "x": 730,
    "y": 120,
    "wires": [
    [
    "5ae27aa26c57fb34"
    ]
    ],
    "icon": "node-red/batch.svg"
    },
    {
    "id": "adcb82cb3b2ca1c0",
    "type": "subflow:fd7f31397b058e66",
    "z": "1dc1799ec4ec93f2",
    "g": "64c274d3e12760cd",
    "name": "",
    "env": [
    {
    "name": "PROGRAMS_A",
    "value": "ems-esp.0.heatingCircuits.hc2.switchPrograms.A",
    "type": "str"
    },
    {
    "name": "PROGRAMS_B",
    "value": "ems-esp.0.heatingCircuits.hc2.switchPrograms.B",
    "type": "str"
    },
    {
    "name": "ACTIVE",
    "value": "ems-esp.0.heatingCircuits.hc2.activeSwitchProgram",
    "type": "str"
    }
    ],
    "x": 730,
    "y": 220,
    "wires": [
    [
    "e991c15e29c9e763"
    ]
    ],
    "icon": "node-red/batch.svg"
    },
    {
    "id": "8408c6fde425aef6",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "34521d86747bd8bd",
    "name": "### hole Daten von Heizung ###",
    "info": "",
    "x": 190,
    "y": 60,
    "wires": []
    },
    {
    "id": "b4059551aadd1ba8",
    "type": "ui_time_scheduler",
    "z": "1dc1799ec4ec93f2",
    "g": "ce8ae0fc54a4d15a",
    "group": "bfe48a3e.1b0f4",
    "name": "ui scheduler hc1",
    "startDay": "1",
    "refresh": "10",
    "devices": [
    "switchPrograms.A",
    "switchPrograms.B"
    ],
    "singleOff": false,
    "onlySendChange": false,
    "customPayload": false,
    "eventMode": false,
    "eventOptions": [],
    "sendTopic": false,
    "lat": "",
    "lon": "",
    "customContextStore": "",
    "outputs": 3,
    "order": 2,
    "width": 7,
    "height": 7,
    "x": 220,
    "y": 480,
    "wires": [
    [
    "494583202e3e8494"
    ],
    [],
    []
    ]
    },
    {
    "id": "80cbeafa06501ebd",
    "type": "ui_time_scheduler",
    "z": "1dc1799ec4ec93f2",
    "g": "86b5b09cb63644c1",
    "group": "fc9e25e07ff9439d",
    "name": "ui scheduler hc2",
    "startDay": "1",
    "refresh": "10",
    "devices": [
    "switchPrograms.A",
    "switchPrograms.B"
    ],
    "singleOff": false,
    "onlySendChange": false,
    "customPayload": false,
    "eventMode": false,
    "eventOptions": [],
    "sendTopic": false,
    "lat": "",
    "lon": "",
    "customContextStore": "",
    "outputs": 3,
    "order": 2,
    "width": 7,
    "height": 7,
    "x": 220,
    "y": 740,
    "wires": [
    [
    "f27da676130dbb50"
    ],
    [],
    []
    ]
    },
    {
    "id": "5ae27aa26c57fb34",
    "type": "json",
    "z": "1dc1799ec4ec93f2",
    "g": "d86ee423ac01ac1d",
    "name": "",
    "property": "payload",
    "action": "str",
    "pretty": false,
    "x": 850,
    "y": 120,
    "wires": [
    [
    "8a494a52f8a84a72"
    ]
    ]
    },
    {
    "id": "e991c15e29c9e763",
    "type": "json",
    "z": "1dc1799ec4ec93f2",
    "g": "64c274d3e12760cd",
    "name": "",
    "property": "payload",
    "action": "str",
    "pretty": false,
    "x": 850,
    "y": 220,
    "wires": [
    [
    "48ce6a5974ab43e7"
    ]
    ]
    },
    {
    "id": "b9e930348330f1a7",
    "type": "ui_ui_control",
    "z": "1dc1799ec4ec93f2",
    "g": "34521d86747bd8bd",
    "name": "Tab Activity Control",
    "events": "change",
    "x": 310,
    "y": 180,
    "wires": [
    [
    "c419a0b8794650c8",
    "87fcae0db87d8ec7"
    ]
    ]
    },
    {
    "id": "c419a0b8794650c8",
    "type": "switch",
    "z": "1dc1799ec4ec93f2",
    "g": "34521d86747bd8bd",
    "name": "Check if Tab is Active",
    "property": "name",
    "propertyType": "msg",
    "rules": [
    {
    "t": "eq",
    "v": "Heizung Dashboard",
    "vt": "str"
    }
    ],
    "checkall": "true",
    "repair": false,
    "outputs": 1,
    "x": 520,
    "y": 180,
    "wires": [
    [
    "72b13674ea7cb2c6",
    "adcb82cb3b2ca1c0"
    ]
    ]
    },
    {
    "id": "87fcae0db87d8ec7",
    "type": "debug",
    "z": "1dc1799ec4ec93f2",
    "g": "34521d86747bd8bd",
    "name": "Ausgabe Tab Name",
    "active": false,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "name",
    "targetType": "msg",
    "statusVal": "",
    "statusType": "auto",
    "x": 510,
    "y": 220,
    "wires": []
    },
    {
    "id": "b71674975643d4c6",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "34521d86747bd8bd",
    "name": "Tab Name anpassen",
    "info": "",
    "x": 510,
    "y": 140,
    "wires": [],
    "icon": "node-red/alert.svg"
    },
    {
    "id": "8a494a52f8a84a72",
    "type": "link out",
    "z": "1dc1799ec4ec93f2",
    "g": "d86ee423ac01ac1d",
    "name": "Data HC1",
    "mode": "link",
    "links": [
    "b58755c1aff6132f"
    ],
    "x": 945,
    "y": 120,
    "wires": []
    },
    {
    "id": "b58755c1aff6132f",
    "type": "link in",
    "z": "1dc1799ec4ec93f2",
    "g": "ce8ae0fc54a4d15a",
    "name": "Data HC1",
    "links": [
    "8a494a52f8a84a72"
    ],
    "x": 105,
    "y": 480,
    "wires": [
    [
    "b4059551aadd1ba8"
    ]
    ]
    },
    {
    "id": "48ce6a5974ab43e7",
    "type": "link out",
    "z": "1dc1799ec4ec93f2",
    "g": "64c274d3e12760cd",
    "name": "Data HC2",
    "mode": "link",
    "links": [
    "aadc625bce68705a"
    ],
    "x": 945,
    "y": 220,
    "wires": []
    },
    {
    "id": "aadc625bce68705a",
    "type": "link in",
    "z": "1dc1799ec4ec93f2",
    "g": "86b5b09cb63644c1",
    "name": "Data HC2",
    "links": [
    "48ce6a5974ab43e7"
    ],
    "x": 105,
    "y": 740,
    "wires": [
    [
    "80cbeafa06501ebd"
    ]
    ]
    },
    {
    "id": "4730defac1edfe3a",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "d86ee423ac01ac1d",
    "name": "Path zu objecten anpassen",
    "info": "",
    "x": 770,
    "y": 80,
    "wires": [],
    "icon": "node-red/alert.svg"
    },
    {
    "id": "e87bd978436f175d",
    "type": "ui_toast",
    "z": "1dc1799ec4ec93f2",
    "g": "c6885eaad8ba447a",
    "position": "top right",
    "displayTime": "5",
    "highlight": "",
    "sendall": true,
    "outputs": 0,
    "ok": "OK",
    "cancel": "",
    "raw": false,
    "className": "",
    "topic": "",
    "name": "alert Messages",
    "x": 1240,
    "y": 100,
    "wires": []
    },
    {
    "id": "494583202e3e8494",
    "type": "json",
    "z": "1dc1799ec4ec93f2",
    "g": "ce8ae0fc54a4d15a",
    "name": "",
    "property": "payload",
    "action": "",
    "pretty": false,
    "x": 370,
    "y": 480,
    "wires": [
    [
    "fb67c94b04023f8d"
    ]
    ]
    },
    {
    "id": "f27da676130dbb50",
    "type": "json",
    "z": "1dc1799ec4ec93f2",
    "g": "86b5b09cb63644c1",
    "name": "",
    "property": "payload",
    "action": "",
    "pretty": false,
    "x": 370,
    "y": 740,
    "wires": [
    [
    "69b371c33441d3dc"
    ]
    ]
    },
    {
    "id": "1fbebe44e887c212",
    "type": "ui_template",
    "z": "1dc1799ec4ec93f2",
    "g": "3bd48465ca73fb26",
    "group": "bfe48a3e.1b0f4",
    "name": "Button speichern",
    "order": 1,
    "width": 0,
    "height": 0,
    "format": "<style>\n /* Eigene CSS-Klasse für den deaktivierten Zustand /\n .my-disabled-btn {\n display: none !important;\n }\n</style>\n\n<div layout="column" style="text-align: center;">\n <md-button class="md-raised" ng-disabled="msg.hide_save_button" ng-click="send(msg)"\n ng-class="{'my-disabled-btn': msg.hide_save_button, 'md-primary': !msg.hide_save_button}">\n <md-icon>save</md-icon>\n jetzt speichern\n </md-button>\n</div>\n \n<script>\n (function(scope) {\n // Falls noch kein msg vorhanden ist, initialisieren wir es.\n if (!scope.msg) {\n scope.msg = {};\n }\n // Wenn msg.hide_save_button nicht definiert ist, setzen wir es auf true (Button deaktiviert)\n if (typeof scope.msg.hide_save_button === 'undefined') {\n scope.msg.hide_save_button = true;\n }\n })(scope);\n</script>",
    "storeOutMessages": true,
    "fwdInMessages": false,
    "resendOnRefresh": true,
    "templateScope": "local",
    "className": "",
    "x": 1070,
    "y": 540,
    "wires": [
    [
    "62f8829176a49bbb"
    ]
    ]
    },
    {
    "id": "4c97aeaa84cf0530",
    "type": "inject",
    "z": "1dc1799ec4ec93f2",
    "g": "3bd48465ca73fb26",
    "name": "Init Button",
    "props": [
    {
    "p": "hide_save_button",
    "v": "true",
    "vt": "bool"
    }
    ],
    "repeat": "",
    "crontab": "",
    "once": true,
    "onceDelay": "0.1",
    "topic": "",
    "x": 990,
    "y": 480,
    "wires": [
    [
    "1fbebe44e887c212"
    ]
    ]
    },
    {
    "id": "49b29ab6eae048ec",
    "type": "change",
    "z": "1dc1799ec4ec93f2",
    "g": "3bd48465ca73fb26",
    "name": "done hide Button",
    "rules": [
    {
    "t": "set",
    "p": "hide_save_button",
    "pt": "msg",
    "to": "true",
    "tot": "bool"
    },
    {
    "t": "set",
    "p": "highlight",
    "pt": "msg",
    "to": "green",
    "tot": "str"
    },
    {
    "t": "set",
    "p": "topic",
    "pt": "msg",
    "to": "Info",
    "tot": "str"
    },
    {
    "t": "set",
    "p": "payload",
    "pt": "msg",
    "to": "Erfolgreich gespeichert",
    "tot": "str"
    }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 1230,
    "y": 480,
    "wires": [
    [
    "1fbebe44e887c212",
    "e15fe52156f0da29"
    ]
    ]
    },
    {
    "id": "51e634864245163f",
    "type": "link out",
    "z": "1dc1799ec4ec93f2",
    "g": "73cfa9d90e59c8f2",
    "name": "To_Messages",
    "mode": "link",
    "links": [
    "7a6e354a4dc03390"
    ],
    "x": 765,
    "y": 460,
    "wires": []
    },
    {
    "id": "7a6e354a4dc03390",
    "type": "link in",
    "z": "1dc1799ec4ec93f2",
    "g": "c6885eaad8ba447a",
    "name": "Messages_IN",
    "links": [
    "51e634864245163f",
    "e15fe52156f0da29",
    "8b1a501f36b9733a",
    "8b92bee47f47148b",
    "439e908c416c021c"
    ],
    "x": 1105,
    "y": 100,
    "wires": [
    [
    "e87bd978436f175d"
    ]
    ]
    },
    {
    "id": "e15fe52156f0da29",
    "type": "link out",
    "z": "1dc1799ec4ec93f2",
    "g": "3bd48465ca73fb26",
    "name": "To_Messages",
    "mode": "link",
    "links": [
    "7a6e354a4dc03390"
    ],
    "x": 1385,
    "y": 480,
    "wires": []
    },
    {
    "id": "4497347fc72f4c67",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "c6885eaad8ba447a",
    "name": "### Benachrichtigungen ###",
    "info": "",
    "x": 1200,
    "y": 60,
    "wires": []
    },
    {
    "id": "fb67c94b04023f8d",
    "type": "subflow:110aeb09527e60b1",
    "z": "1dc1799ec4ec93f2",
    "g": "73cfa9d90e59c8f2",
    "name": "",
    "x": 630,
    "y": 480,
    "wires": [
    [
    "51e634864245163f"
    ],
    [
    "1fbebe44e887c212"
    ]
    ]
    },
    {
    "id": "69b371c33441d3dc",
    "type": "subflow:110aeb09527e60b1",
    "z": "1dc1799ec4ec93f2",
    "g": "4c8b49a972950636",
    "name": "",
    "x": 630,
    "y": 740,
    "wires": [
    [
    "8b1a501f36b9733a"
    ],
    [
    "e67070774e76ceed"
    ]
    ]
    },
    {
    "id": "8b1a501f36b9733a",
    "type": "link out",
    "z": "1dc1799ec4ec93f2",
    "g": "4c8b49a972950636",
    "name": "To_Messages",
    "mode": "link",
    "links": [
    "7a6e354a4dc03390"
    ],
    "x": 765,
    "y": 720,
    "wires": []
    },
    {
    "id": "62f8829176a49bbb",
    "type": "subflow:b647dcc6973cef0a",
    "z": "1dc1799ec4ec93f2",
    "g": "3bd48465ca73fb26",
    "name": "",
    "x": 1230,
    "y": 540,
    "wires": [
    [
    "49b29ab6eae048ec",
    "b6ac03883a2c5fae"
    ]
    ]
    },
    {
    "id": "e67070774e76ceed",
    "type": "ui_template",
    "z": "1dc1799ec4ec93f2",
    "g": "2d7a89955c419a2e",
    "group": "fc9e25e07ff9439d",
    "name": "Button speichern",
    "order": 1,
    "width": 0,
    "height": 0,
    "format": "<style>\n /
    Eigene CSS-Klasse für den deaktivierten Zustand */\n .my-disabled-btn {\n display: none !important;\n }\n</style>\n\n<div layout="column" style="text-align: center;">\n <md-button class="md-raised" ng-disabled="msg.hide_save_button" ng-click="send(msg)"\n ng-class="{'my-disabled-btn': msg.hide_save_button, 'md-primary': !msg.hide_save_button}">\n <md-icon>save</md-icon>\n jetzt speichern\n </md-button>\n</div>\n \n<script>\n (function(scope) {\n // Falls noch kein msg vorhanden ist, initialisieren wir es.\n if (!scope.msg) {\n scope.msg = {};\n }\n // Wenn msg.hide_save_button nicht definiert ist, setzen wir es auf true (Button deaktiviert)\n if (typeof scope.msg.hide_save_button === 'undefined') {\n scope.msg.hide_save_button = true;\n }\n })(scope);\n</script>",
    "storeOutMessages": true,
    "fwdInMessages": false,
    "resendOnRefresh": true,
    "templateScope": "local",
    "className": "",
    "x": 1070,
    "y": 740,
    "wires": [
    [
    "a56607f2cbb3e1d5"
    ]
    ]
    },
    {
    "id": "73623095fc2284e5",
    "type": "inject",
    "z": "1dc1799ec4ec93f2",
    "g": "2d7a89955c419a2e",
    "name": "Init Button",
    "props": [
    {
    "p": "hide_save_button",
    "v": "true",
    "vt": "bool"
    }
    ],
    "repeat": "",
    "crontab": "",
    "once": true,
    "onceDelay": "0.1",
    "topic": "",
    "x": 990,
    "y": 680,
    "wires": [
    [
    "e67070774e76ceed"
    ]
    ]
    },
    {
    "id": "282f35b8fde4649d",
    "type": "change",
    "z": "1dc1799ec4ec93f2",
    "g": "2d7a89955c419a2e",
    "name": "done hide Button",
    "rules": [
    {
    "t": "set",
    "p": "hide_save_button",
    "pt": "msg",
    "to": "true",
    "tot": "bool"
    },
    {
    "t": "set",
    "p": "highlight",
    "pt": "msg",
    "to": "green",
    "tot": "str"
    },
    {
    "t": "set",
    "p": "topic",
    "pt": "msg",
    "to": "Info",
    "tot": "str"
    },
    {
    "t": "set",
    "p": "payload",
    "pt": "msg",
    "to": "Erfolgreich gespeichert",
    "tot": "str"
    }
    ],
    "action": "",
    "property": "",
    "from": "",
    "to": "",
    "reg": false,
    "x": 1230,
    "y": 680,
    "wires": [
    [
    "e67070774e76ceed",
    "439e908c416c021c"
    ]
    ]
    },
    {
    "id": "439e908c416c021c",
    "type": "link out",
    "z": "1dc1799ec4ec93f2",
    "g": "2d7a89955c419a2e",
    "name": "To_Messages",
    "mode": "link",
    "links": [
    "7a6e354a4dc03390"
    ],
    "x": 1385,
    "y": 680,
    "wires": []
    },
    {
    "id": "a56607f2cbb3e1d5",
    "type": "subflow:b647dcc6973cef0a",
    "z": "1dc1799ec4ec93f2",
    "g": "2d7a89955c419a2e",
    "name": "",
    "x": 1230,
    "y": 740,
    "wires": [
    [
    "282f35b8fde4649d",
    "bc3a160867da8da4"
    ]
    ]
    },
    {
    "id": "bc3a160867da8da4",
    "type": "debug",
    "z": "1dc1799ec4ec93f2",
    "g": "2d7a89955c419a2e",
    "name": "debug",
    "active": true,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "payload",
    "targetType": "msg",
    "statusVal": "",
    "statusType": "auto",
    "x": 1370,
    "y": 740,
    "wires": []
    },
    {
    "id": "b6ac03883a2c5fae",
    "type": "debug",
    "z": "1dc1799ec4ec93f2",
    "g": "3bd48465ca73fb26",
    "name": "debug",
    "active": true,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "payload",
    "targetType": "msg",
    "statusVal": "",
    "statusType": "auto",
    "x": 1370,
    "y": 540,
    "wires": []
    },
    {
    "id": "ae32871f3dfe3184",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "64c274d3e12760cd",
    "name": "Path zu objecten anpassen",
    "info": "",
    "x": 770,
    "y": 260,
    "wires": [],
    "icon": "node-red/alert.svg"
    },
    {
    "id": "3ee41d6b6e418725",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "87aa28c70609a83b",
    "name": "### Fehlerauswertung ###",
    "info": "",
    "x": 630,
    "y": 380,
    "wires": []
    },
    {
    "id": "804b40577d582f81",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "bc982b55b594a1be",
    "name": "### sende Daten zu Heizung ###",
    "info": "",
    "x": 1010,
    "y": 380,
    "wires": []
    },
    {
    "id": "f22a7d5a405263b9",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "5607675eb8b28b91",
    "name": "### Scheduler ###",
    "info": "",
    "x": 150,
    "y": 380,
    "wires": []
    },
    {
    "id": "b181124ed41c964c",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "d86ee423ac01ac1d",
    "name": "## HC1",
    "info": "",
    "x": 930,
    "y": 80,
    "wires": []
    },
    {
    "id": "57973e094f933eaf",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "64c274d3e12760cd",
    "name": "## HC2",
    "info": "",
    "x": 930,
    "y": 260,
    "wires": []
    },
    {
    "id": "10b6a4012722e8ad",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "ce8ae0fc54a4d15a",
    "name": "## HC1",
    "info": "",
    "x": 190,
    "y": 440,
    "wires": []
    },
    {
    "id": "a8a5c24a6c414379",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "86b5b09cb63644c1",
    "name": "## HC2",
    "info": "",
    "x": 190,
    "y": 700,
    "wires": []
    },
    {
    "id": "3b325db587bbcdb6",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "4c8b49a972950636",
    "name": "## HC2",
    "info": "",
    "x": 590,
    "y": 700,
    "wires": []
    },
    {
    "id": "d0ce33fc89b9d55c",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "2d7a89955c419a2e",
    "name": "## HC2",
    "info": "",
    "x": 950,
    "y": 640,
    "wires": []
    },
    {
    "id": "4fce0eb37daebf39",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "73cfa9d90e59c8f2",
    "name": "## HC1",
    "info": "",
    "x": 590,
    "y": 440,
    "wires": []
    },
    {
    "id": "0162469a4966cc8d",
    "type": "comment",
    "z": "1dc1799ec4ec93f2",
    "g": "3bd48465ca73fb26",
    "name": "## HC1",
    "info": "",
    "x": 950,
    "y": 440,
    "wires": []
    },
    {
    "id": "bfe48a3e.1b0f4",
    "type": "ui_group",
    "name": "HC1",
    "tab": "2936b813.6cde68",
    "order": 1,
    "disp": true,
    "width": "7",
    "collapse": false,
    "className": ""
    },
    {
    "id": "fc9e25e07ff9439d",
    "type": "ui_group",
    "name": "HC2",
    "tab": "2936b813.6cde68",
    "order": 2,
    "disp": true,
    "width": "7",
    "collapse": false,
    "className": ""
    },
    {
    "id": "2936b813.6cde68",
    "type": "ui_tab",
    "name": "Heizung Dashboard",
    "icon": "fa-fire",
    "order": 1,
    "disabled": false,
    "hidden": false
    }
    ]

    T 1 Antwort Letzte Antwort
    0
    • K kunigunde

      Hallo,
      ich suche Tester für das Zusammenspiel Buderus->kmxx->emsesp->NodeRed.

      Ich habe leider bisher noch nichts bestehendes gefunden, was es ermöglicht den Zeitplan komfortabel zu ändern.
      Dies geht entweder über das Bedienpult, oder über die APP.
      Ich wollte es aber über das Node-Red Dashboard anzeigen und ändern können.

      Dies ist nun mein 1. Versuch, und es scheint bei mir zu funktionieren.
      Ok, manchmal kann der EmsEsp nicht schreiben.....
      Aber dazu suche ich ja Tester, welche vielleicht Ideen haben, wie man dies prozesssicherer gestalten kann.

      Als extra node-module wird "node-red-contrib-ui-time-scheduler" benutzt.
      Screenshot.png
      Als Datei zum importieren: flows.json
      als code zum kopieren:


      [
      {
      "id": "b647dcc6973cef0a",
      "type": "subflow",
      "name": "ui->km",
      "category": "",
      "in": [
      {
      "x": 80,
      "y": 240,
      "wires": [
      {
      "id": "a45c49648996ca8c"
      }
      ]
      }
      ],
      "out": [
      {
      "x": 400,
      "y": 300,
      "wires": [
      {
      "id": "a45c49648996ca8c",
      "port": 3
      }
      ]
      }
      ],
      "env": [],
      "meta": {},
      "color": "#DDAA99"
      },
      {
      "id": "a45c49648996ca8c",
      "type": "function",
      "z": "b647dcc6973cef0a",
      "name": "ui->km",
      "func": "// Erzeuge ein Date-Objekt für den aktuellen Tag um 00:00 Uhr\nvar baseDate = new Date();\nbaseDate.setHours(0, 0, 0, 0);\nvar base = baseDate.getTime(); // Basis-Zeitstempel in Millisekunden\n\n// Hilfsfunktion: Wandelt einen absoluten Timestamp (Millisekunden) in Minuten seit Mitternacht um\nfunction fromTimestamp(timestamp) {\n return Math.round((timestamp - base) / 60000);\n}\n\n// Mapping der Tage: Für die Erzeugung der Strings nutzen wir die Reihenfolge, wie sie in den Eingabedaten vorkommen\n// Hier entspricht Index 0 "Su", 1 "Mo", 2 "Tu", usw.\nvar dayMapping = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];\n\n// Ergebnis-Arrays für die beiden Bereiche\nvar outputA = [];\nvar outputB = [];\n\n// Wir erwarten, dass msg.payload.timers ein Array von Timer-Objekten enthält, z. B.:\n// {\n// starttime: <absolute Zeit in ms>,\n// endtime: <absolute Zeit in ms>,\n// days: [0,1,0,0,0,0,0],\n// output: "0" oder "1"\n// }\nmsg.payload.timers.forEach(function (timer) {\n // Wandle absolute Zeiten in Minuten um\n var startMinutes = fromTimestamp(timer.starttime);\n var endMinutes = fromTimestamp(timer.endtime);\n\n // Für jeden Tag prüfen, ob dieser Timer aktiv ist (Wert 1 im days-Array)\n timer.days.forEach(function (active, index) {\n if (active === 1) {\n var day = dayMapping[index];\n // Wähle anhand von timer.output das Ziel-Array\n var targetArray = (timer.output === "0") ? outputA : outputB;\n\n // Erzeuge zwei Objekte: einen für den Start (setpoint "comfort2") und einen für das Ende (setpoint "eco")\n targetArray.push({ dayOfWeek: day, setpoint: "comfort2", time: startMinutes });\n targetArray.push({ dayOfWeek: day, setpoint: "eco", time: endMinutes });\n }\n });\n});\n\n// Sortierfunktion für die Ergebnisse (nach Wochentag und Uhrzeit)\n// Die Sortierreihenfolge wird so festgelegt, dass Montag als erstes kommt.\nvar dayOrder = { "Mo": 0, "Tu": 1, "We": 2, "Th": 3, "Fr": 4, "Sa": 5, "Su": 6 };\n\nfunction sortTimers(arr) {\n arr.sort(function (a, b) {\n // Vergleiche zunächst anhand der Wochentagsreihenfolge\n var dayA = dayOrder[a.dayOfWeek];\n var dayB = dayOrder[b.dayOfWeek];\n if (dayA !== dayB) return dayA - dayB;\n // Danach nach der Uhrzeit\n if (a.time !== b.time) return a.time - b.time;\n // Optional: Falls beides gleich ist, nach setpoint (alphabetisch)\n return a.setpoint.localeCompare(b.setpoint);\n });\n}\n\n// Sortiere beide Bereiche\nsortTimers(outputA);\nsortTimers(outputB);\n\n// Prüfe, ob outputA oder outputB leer sind, und setze sie gegebenenfalls auf einen Default-Array\nvar defaultArray = [\n {"dayOfWeek": "Mo", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "Tu", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "We", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "Th", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "Fr", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "Sa", "setpoint": "eco", "time": 1380},\n {"dayOfWeek": "Su", "setpoint": "eco", "time": 1380}\n];\n\nif (outputA.length === 0) {\n outputA = defaultArray;\n}\nif (outputB.length === 0) {\n outputB = defaultArray;\n}\n\n//##########################################\n// Hole das Array disabledDevices\nvar disabledDevices = msg.payload.settings.disabledDevices;\nvar activeSwitchProgram = 0;// Pauschal erstmal Programm A aktivieren\n\n// Überprüfe, ob disabledDevices existiert, ein Array ist und mindestens einen Eintrag enthält\nif (Array.isArray(disabledDevices) && disabledDevices.length > 0) {\n // Wenn disabledDevices[0] den Wert 1 hat, setze activeSwitchProgram auf 0 = Program A\n if (disabledDevices[0] === "1") {\n activeSwitchProgram = 0;\n }\n // Wenn disabledDevices[0] den Wert 0 hat, setze activeSwitchProgram auf 1 = Program B\n else if (disabledDevices[0] === "0") {\n activeSwitchProgram = 1;\n }\n else {\n // Optional: Für den Fall, dass ein anderer Wert vorliegt\n node.warn("Unerwarteter Wert in disabledDevices[0]: " + disabledDevices[0]);\n }\n} else {\n // Optional: Fehlermeldung, wenn disabledDevices nicht existiert oder leer ist\n node.error("disabledDevices existiert nicht oder ist leer.", msg);\n}\n//##########################################\n\n\n// Setze das Ergebnis in msg.payload mit getrennten Bereichen\nvar all_together = {\n timers_A: outputA,\n timers_B: outputB,\n activeSwitchProgram: activeSwitchProgram\n};\n\n//return msg;\n\n// Setze die sortierten Arrays als separate Outputs\nreturn [{ payload: outputA, topic:msg.topic_A }, { payload: outputB, topic: msg.topic_B }, { payload: activeSwitchProgram, topic: msg.activeSwitchProgram }, {payload:all_together}];",
      "outputs": 4,
      "timeout": 0,
      "noerr": 0,
      "initialize": "",
      "finalize": "",
      "libs": [],
      "x": 270,
      "y": 240,
      "wires": [
      [
      "3d5f9cdb5af9c500"
      ],
      [
      "ed0b32094cd93163"
      ],
      [
      "baf0eb9c1997313c"
      ],
      []
      ],
      "info": "# Beschreibung der Funktion\r\nDiese Funktion verarbeitet Timer-Daten und bereitet sie in einem spezifischen Format für die weitere Verarbeitung vor. \r\nDabei werden folgende Schritte durchgeführt:\r\n\r\n## 1. Zeit-Basis und Umrechnung:\r\nEs wird ein Basis-Zeitstempel für den aktuellen Tag um 00:00 Uhr erzeugt. Anhand dieses Basiswerts werden absolute Zeitstempel (in Millisekunden) in Minuten seit Mitternacht umgerechnet.\r\n\r\n## 2. Aufbereitung der Timer-Daten:\r\n- Aus der Eingangsstruktur msg.payload.timers werden einzelne Timer-Objekte verarbeitet. \r\n- Für jeden Timer werden die Start- und Endzeiten in Minuten berechnet.\r\n- Über das days-Array des Timers wird ermittelt, an welchen Wochentagen der Timer aktiv ist.\r\n- Für jeden aktiven Tag wird basierend auf dem Timer-Feld output (mit Wert "0" oder "1") ein Eintrag erzeugt:\r\n- - Ein Objekt mit setpoint: "comfort2" und der berechneten Startzeit.\r\n- - Ein Objekt mit setpoint: "eco" und der berechneten Endzeit.\r\n- Diese Objekte werden in zwei separate Arrays einsortiert:\r\n- - outputA für Timer mit output === "0".\r\n- - outputB für Timer mit output === "1".\r\n\r\n## 3. Sortierung:\r\nBeide Arrays werden nach Wochentagen und Uhrzeit sortiert – die Sortierreihenfolge ist so festgelegt, dass Montag als erster Tag erscheint.\r\n\r\n## 4. Fallback bei fehlenden Timer-Daten:\r\nFalls eines der Arrays (outputA oder outputB) nach der Verarbeitung leer sein sollte, \r\nwird es durch einen Default-Array ersetzt. \r\nDieser Default-Array enthält für alle Wochentage (Montag bis Sonntag) Timer-Einträge mit setpoint: "eco" und time: 1380 (entspricht 23:00 Uhr).\r\n\r\n## 5. Bestimmung des aktiven Programms:\r\nEs wird das Array disabledDevices aus msg.payload.settings ausgelesen.\r\nAnhand des Werts in disabledDevices[0] wird der Schalter activeSwitchProgram gesetzt:\r\n- Wenn disabledDevices[0] den String "1" enthält, wird Programm A (Wert 0) aktiv.\r\n- Wenn disabledDevices[0] den String "0" enthält, wird Programm B (Wert 1) aktiv.\r\n\r\n## 6. Ausgabe:\r\nDie Funktion gibt vier Outputs zurück:\r\n\r\n- Output 1: Das Array outputA (Timer für output "0").\r\n- Output 2: Das Array outputB (Timer für output "1").\r\n- Output 3: Der Wert activeSwitchProgram (der aktive Programmschalter).\r\n- Output 4: Ein kombiniertes Objekt (all_together), das beide Timer-Arrays und den aktiven Schalter enthält.\r\n\r\nDiese Funktion sorgt dafür, dass die Timer-Daten aus der Eingangsdatenstruktur in ein konsistentes, \r\nsortiertes Format überführt werden und dass bei fehlenden Timer-Daten ein Standard-Set an Timer-Einträgen bereitgestellt wird. \r\nGleichzeitig wird anhand der Einstellungen in disabledDevices der aktive Programmschalter dynamisch festgelegt."
      },
      {
      "id": "2dfa8c4c402abc2c",
      "type": "ioBroker out",
      "z": "b647dcc6973cef0a",
      "name": "",
      "topic": "",
      "ack": "false",
      "autoCreate": "false",
      "stateName": "",
      "role": "",
      "payloadType": "",
      "readonly": "",
      "stateUnit": "",
      "stateMin": "",
      "stateMax": "",
      "x": 540,
      "y": 180,
      "wires": []
      },
      {
      "id": "f4e8827f63c274f2",
      "type": "ioBroker out",
      "z": "b647dcc6973cef0a",
      "name": "",
      "topic": "",
      "ack": "false",
      "autoCreate": "false",
      "stateName": "",
      "role": "",
      "payloadType": "",
      "readonly": "",
      "stateUnit": "",
      "stateMin": "",
      "stateMax": "",
      "x": 540,
      "y": 220,
      "wires": []
      },
      {
      "id": "baf0eb9c1997313c",
      "type": "ioBroker out",
      "z": "b647dcc6973cef0a",
      "name": "",
      "topic": "",
      "ack": "false",
      "autoCreate": "false",
      "stateName": "",
      "role": "",
      "payloadType": "",
      "readonly": "",
      "stateUnit": "",
      "stateMin": "",
      "stateMax": "",
      "x": 540,
      "y": 260,
      "wires": []
      },
      {
      "id": "3d5f9cdb5af9c500",
      "type": "json",
      "z": "b647dcc6973cef0a",
      "name": "",
      "property": "payload",
      "action": "str",
      "pretty": false,
      "x": 390,
      "y": 180,
      "wires": [
      [
      "2dfa8c4c402abc2c"
      ]
      ]
      },
      {
      "id": "ed0b32094cd93163",
      "type": "json",
      "z": "b647dcc6973cef0a",
      "name": "",
      "property": "payload",
      "action": "str",
      "pretty": false,
      "x": 390,
      "y": 220,
      "wires": [
      [
      "f4e8827f63c274f2"
      ]
      ]
      },
      {
      "id": "110aeb09527e60b1",
      "type": "subflow",
      "name": "Fehlerauswertung",
      "info": "",
      "category": "",
      "in": [
      {
      "x": 60,
      "y": 160,
      "wires": [
      {
      "id": "14d67afb1eb1f40c"
      }
      ]
      }
      ],
      "out": [
      {
      "x": 300,
      "y": 120,
      "wires": [
      {
      "id": "14d67afb1eb1f40c",
      "port": 0
      }
      ]
      },
      {
      "x": 300,
      "y": 200,
      "wires": [
      {
      "id": "14d67afb1eb1f40c",
      "port": 1
      }
      ]
      }
      ],
      "env": [],
      "meta": {},
      "color": "#DDAA99"
      },
      {
      "id": "14d67afb1eb1f40c",
      "type": "function",
      "z": "110aeb09527e60b1",
      "name": "Fehler prüfen",
      "func": "// Variable zur Fehlernachricht initialisieren\nvar errorMessage = null;\n\n// DisabledDevices prüfen (niemals beide Programme oder keines)\n//#############################################################\n// Hole das Array\nvar disabledDevices = msg.payload.settings.disabledDevices;\n\n// Prüfe, ob disabledDevices existiert, ein Array ist und nicht leer ist \n// (Es muss ein Programm abgewählt sein.)\nif (!Array.isArray(disabledDevices) || disabledDevices.length === 0) {\n errorMessage = "Achtung, kein Programm abgewählt (Program A oder B).";\n}\n\n// Prüfe, ob das Array mehr als 1 Element enthält\nif (errorMessage === null && disabledDevices.length > 1) {\n errorMessage = "Achtung, mehr als 1 Programm abgewählt (Program A und B).";\n}\n//#############################################################\n\n// Wenn ein Fehler vorhanden ist, Fehlernachricht an Ausgang 1 senden,\n// ansonsten die Nachricht an Ausgang 2 senden.\nif (errorMessage !== null) {\n msg.highlight = "red";\n msg.topic = "Fehler";\n msg.payload = errorMessage;\n return [msg, null]; // msg an Ausgang 1, Ausgang 2 bleibt leer\n} else {\n msg.hide_save_button = false;// Button einblenden\n return [null, msg]; // msg an Ausgang 2, Ausgang 1 bleibt leer\n}",
      "outputs": 2,
      "timeout": 0,
      "noerr": 0,
      "initialize": "",
      "finalize": "",
      "libs": [],
      "x": 170,
      "y": 160,
      "wires": [
      [],
      []
      ]
      },
      {
      "id": "fd7f31397b058e66",
      "type": "subflow",
      "name": "km->ui",
      "info": "",
      "category": "",
      "in": [
      {
      "x": 60,
      "y": 220,
      "wires": [
      {
      "id": "8a64d2a144809a71"
      }
      ]
      }
      ],
      "out": [
      {
      "x": 1420,
      "y": 220,
      "wires": [
      {
      "id": "12c457a5ee91046c",
      "port": 0
      }
      ]
      }
      ],
      "env": [
      {
      "name": "PROGRAMS_A",
      "type": "str",
      "value": "ems-esp.0.heatingCircuits.hc1.switchPrograms.A"
      },
      {
      "name": "PROGRAMS_B",
      "type": "str",
      "value": "ems-esp.0.heatingCircuits.hc1.switchPrograms.B"
      },
      {
      "name": "ACTIVE",
      "type": "str",
      "value": "ems-esp.0.heatingCircuits.hc1.activeSwitchProgram"
      }
      ],
      "meta": {},
      "color": "#DDAA99"
      },
      {
      "id": "34e0423f1724b8f1",
      "type": "function",
      "z": "fd7f31397b058e66",
      "name": "timers_A",
      "func": "// Erzeugt einen Date-Objekt für den aktuellen Tag um 00:00 Uhr\nvar baseDate = new Date();\nbaseDate.setHours(0, 0, 0, 0);\nvar base = baseDate.getTime(); // Basis-Zeitstempel in Millisekunden\n\n// Hilfsfunktion: Wandelt Minuten in einen absoluten Timestamp (Millisekunden) um\nfunction toTimestamp(minutes) {\n return base + minutes * 60000;\n}\n\n/\n Schritt 1: Für jeden Wochentag Start- und Endzeit ermitteln\n Dabei gilt:\n - setpoint "comfort2" → Startzeit\n - setpoint "eco" → Endzeit\n \n Wir erwarten, dass msg.hc1_a ein Array mit Objekten ist, z. B.:\n [\n {"dayOfWeek":"Mo","setpoint":"comfort2","time":270},\n {"dayOfWeek":"Mo","setpoint":"eco","time":420},\n {"dayOfWeek":"Mo","setpoint":"comfort2","time":840},\n {"dayOfWeek":"Mo","setpoint":"eco","time":1200},\n {"dayOfWeek":"Tu","setpoint":"comfort2","time":270},\n {"dayOfWeek":"Tu","setpoint":"eco","time":420},\n ...\n ]\n \n Es können auch mehrere Intervalle pro Tag vorhanden sein. In diesem Beispiel gruppieren\n wir jeweils je 2 Einträge (ein Start- und ein Endwert) – vorausgesetzt, die Daten liegen\n sortiert nach Zeit vor.\n/\nvar dayIntervals = {}; // Beispiel: { "Mo": [ {start:270, end:420}, {start:840, end:1200} ], ... }\n\nmsg.payload.forEach(function(item) {\n var day = item.dayOfWeek;\n // Initialisiere den Tag, falls noch nicht vorhanden\n if (!dayIntervals[day]) {\n dayIntervals[day] = [];\n }\n \n // Wir speichern die Werte vorerst als temporäres Objekt pro Tag\n // Es wird angenommen, dass immer zwei Einträge hintereinander zum gleichen Intervall gehören.\n // Daher speichern wir einfach in der Reihenfolge der Verarbeitung:\n if (item.setpoint === "comfort2") {\n // Neuer Intervall: speichern den Startwert\n dayIntervals[day].push({ start: item.time });\n } else if (item.setpoint === "eco") {\n // Es wird erwartet, dass ein entsprechender Startwert bereits existiert.\n // Daher ergänzen wir den zuletzt eingefügten Intervall.\n if (dayIntervals[day].length > 0 && typeof dayIntervals[day][dayIntervals[day].length - 1].end === "undefined") {\n dayIntervals[day][dayIntervals[day].length - 1].end = item.time;\n } else {\n // Falls nicht vorhanden, kann man hier optional einen neuen Eintrag anlegen oder loggen\n dayIntervals[day].push({ end: item.time });\n }\n }\n});\n\n/\n Schritt 2: Gruppieren über alle Tage – es werden Gruppen gebildet, die dieselbe\n Kombination aus Start‑ und Endzeit besitzen.\n \n Dabei gehen wir wie folgt vor:\n - Für jeden Tag und jedes Intervall wird ein Schlüssel "start_end" erzeugt.\n - Wenn diese Gruppe bereits existiert, wird der Tag hinzugefügt.\n - Existiert sie nicht, wird eine neue Gruppe angelegt.\n \n Wir erstellen ein Objekt "groups" mit folgendem Aufbau:\n \n {\n "270_420": {\n start: 270,\n end: 420,\n days: [0, 0, 0, 0, 0, 0, 0] // 7 Stellen für Su, Mo, Di, Mi, Do, Fr, Sa\n },\n "840_1200": {\n start: 840,\n end: 1200,\n days: [0, 0, 0, 0, 0, 0, 0]\n }\n }\n \n Dabei wird für jeden Tag in der entsprechenden Gruppe der Index im days-Array auf 1 gesetzt.\n/\n\n// Mapping der Wochentage auf Index im days-Array:\n// Index 0 = Sonntag, 1 = Montag, 2 = Dienstag, 3 = Mittwoch, 4 = Donnerstag, 5 = Freitag, 6 = Samstag\nvar weekDayIndex = {\n "Su": 0,\n "Mo": 1,\n "Tu": 2,\n "We": 3,\n "Th": 4,\n "Fr": 5,\n "Sa": 6\n};\n\nvar groups = {};\n\n// Gehe alle Tage durch und alle Intervalle des jeweiligen Tages\nfor (var day in dayIntervals) {\n var intervals = dayIntervals[day];\n intervals.forEach(function(interval) {\n // Nur weiter verarbeiten, wenn sowohl start als auch end vorhanden sind\n if (typeof interval.start !== "undefined" && typeof interval.end !== "undefined") {\n var key = interval.start + "" + interval.end;\n // Falls Gruppe noch nicht existiert, anlegen\n if (!groups[key]) {\n groups[key] = {\n start: interval.start,\n end: interval.end,\n days: [0, 0, 0, 0, 0, 0, 0]\n };\n }\n // Bestimme den Index des aktuellen Tages (ggf. alternative Schreibweisen beachten)\n var idx = weekDayIndex[day];\n if (typeof idx !== "undefined") {\n groups[key].days[idx] = 1;\n }\n }\n });\n}\n\n/\n Schritt 3: Aufbau des finalen Timers-Arrays.\n Für jede Gruppe wird ein Objekt erstellt, das folgende Struktur hat:\n \n {\n "starttime": <absolute Startzeit>,\n "days": [Array mit 7 Werten],\n "output": "0", // oder ein anderer fest definierter Wert\n "endtime": <absolute Endzeit>\n }\n \n Dabei werden die Zeiten über die Funktion toTimestamp() umgerechnet.\n/\n// Zuerst bestimmen wir den Wert von output abhängig vom msg.program\nvar outputValue = "0"; // Standardwert\nif (msg.program === "B") {\n outputValue = "1";\n} else if (msg.program === "A") {\n outputValue = "0";\n}\n\nvar timers = [];\nfor (var key in groups) {\n var grp = groups[key];\n timers.push({\n starttime: toTimestamp(grp.start),\n endtime: toTimestamp(grp.end),\n days: grp.days,\n output: outputValue\n });\n}\n\n/\n Schritt 4: Aufbau des finalen Ausgabeobjekts.\n/\nmsg.payload = timers;\n\nreturn msg;",
      "outputs": 1,
      "timeout": 0,
      "noerr": 0,
      "initialize": "",
      "finalize": "",
      "libs": [],
      "x": 760,
      "y": 120,
      "wires": [
      [
      "73d6757f1e5fdf61"
      ]
      ]
      },
      {
      "id": "46437c651a59543a",
      "type": "ioBroker get",
      "z": "fd7f31397b058e66",
      "name": "switchPrograms.A",
      "topic": "",
      "attrname": "payload",
      "payloadType": "value",
      "x": 470,
      "y": 120,
      "wires": [
      [
      "32302fdcb491d2a5"
      ]
      ]
      },
      {
      "id": "32302fdcb491d2a5",
      "type": "json",
      "z": "fd7f31397b058e66",
      "name": "",
      "property": "payload",
      "action": "obj",
      "pretty": false,
      "x": 630,
      "y": 120,
      "wires": [
      [
      "34e0423f1724b8f1"
      ]
      ]
      },
      {
      "id": "8a64d2a144809a71",
      "type": "change",
      "z": "fd7f31397b058e66",
      "name": "SETTINGS",
      "rules": [
      {
      "t": "set",
      "p": "topic_A",
      "pt": "msg",
      "to": "${PROGRAMS_A}",
      "tot": "str"
      },
      {
      "t": "set",
      "p": "topic_B",
      "pt": "msg",
      "to": "${PROGRAMS_B}",
      "tot": "str"
      },
      {
      "t": "set",
      "p": "activeSwitchProgram",
      "pt": "msg",
      "to": "${ACTIVE}",
      "tot": "str"
      }
      ],
      "action": "",
      "property": "",
      "from": "",
      "to": "",
      "reg": false,
      "x": 210,
      "y": 220,
      "wires": [
      [
      "ceba323fdfd481ed",
      "9a3dc889e642ccd9",
      "3e17abbc9832a241"
      ]
      ]
      },
      {
      "id": "ceba323fdfd481ed",
      "type": "change",
      "z": "fd7f31397b058e66",
      "name": "topic_A->topic",
      "rules": [
      {
      "t": "move",
      "p": "topic_A",
      "pt": "msg",
      "to": "topic",
      "tot": "msg"
      },
      {
      "t": "set",
      "p": "program",
      "pt": "msg",
      "to": "A",
      "tot": "str"
      }
      ],
      "action": "",
      "property": "",
      "from": "",
      "to": "",
      "reg": false,
      "x": 480,
      "y": 80,
      "wires": [
      [
      "46437c651a59543a"
      ]
      ]
      },
      {
      "id": "dd20b12c905c22fc",
      "type": "function",
      "z": "fd7f31397b058e66",
      "name": "timers_B",
      "func": "// Erzeugt einen Date-Objekt für den aktuellen Tag um 00:00 Uhr\nvar baseDate = new Date();\nbaseDate.setHours(0, 0, 0, 0);\nvar base = baseDate.getTime(); // Basis-Zeitstempel in Millisekunden\n\n// Hilfsfunktion: Wandelt Minuten in einen absoluten Timestamp (Millisekunden) um\nfunction toTimestamp(minutes) {\n return base + minutes * 60000;\n}\n\n/\n Schritt 1: Für jeden Wochentag Start- und Endzeit ermitteln\n Dabei gilt:\n - setpoint "comfort2" → Startzeit\n - setpoint "eco" → Endzeit\n \n Wir erwarten, dass msg.hc1_a ein Array mit Objekten ist, z. B.:\n [\n {"dayOfWeek":"Mo","setpoint":"comfort2","time":270},\n {"dayOfWeek":"Mo","setpoint":"eco","time":420},\n {"dayOfWeek":"Mo","setpoint":"comfort2","time":840},\n {"dayOfWeek":"Mo","setpoint":"eco","time":1200},\n {"dayOfWeek":"Tu","setpoint":"comfort2","time":270},\n {"dayOfWeek":"Tu","setpoint":"eco","time":420},\n ...\n ]\n \n Es können auch mehrere Intervalle pro Tag vorhanden sein. In diesem Beispiel gruppieren\n wir jeweils je 2 Einträge (ein Start- und ein Endwert) – vorausgesetzt, die Daten liegen\n sortiert nach Zeit vor.\n/\nvar dayIntervals = {}; // Beispiel: { "Mo": [ {start:270, end:420}, {start:840, end:1200} ], ... }\n\nmsg.payload.forEach(function(item) {\n var day = item.dayOfWeek;\n // Initialisiere den Tag, falls noch nicht vorhanden\n if (!dayIntervals[day]) {\n dayIntervals[day] = [];\n }\n \n // Wir speichern die Werte vorerst als temporäres Objekt pro Tag\n // Es wird angenommen, dass immer zwei Einträge hintereinander zum gleichen Intervall gehören.\n // Daher speichern wir einfach in der Reihenfolge der Verarbeitung:\n if (item.setpoint === "comfort2") {\n // Neuer Intervall: speichern den Startwert\n dayIntervals[day].push({ start: item.time });\n } else if (item.setpoint === "eco") {\n // Es wird erwartet, dass ein entsprechender Startwert bereits existiert.\n // Daher ergänzen wir den zuletzt eingefügten Intervall.\n if (dayIntervals[day].length > 0 && typeof dayIntervals[day][dayIntervals[day].length - 1].end === "undefined") {\n dayIntervals[day][dayIntervals[day].length - 1].end = item.time;\n } else {\n // Falls nicht vorhanden, kann man hier optional einen neuen Eintrag anlegen oder loggen\n dayIntervals[day].push({ end: item.time });\n }\n }\n});\n\n/\n Schritt 2: Gruppieren über alle Tage – es werden Gruppen gebildet, die dieselbe\n Kombination aus Start‑ und Endzeit besitzen.\n \n Dabei gehen wir wie folgt vor:\n - Für jeden Tag und jedes Intervall wird ein Schlüssel "start_end" erzeugt.\n - Wenn diese Gruppe bereits existiert, wird der Tag hinzugefügt.\n - Existiert sie nicht, wird eine neue Gruppe angelegt.\n \n Wir erstellen ein Objekt "groups" mit folgendem Aufbau:\n \n {\n "270_420": {\n start: 270,\n end: 420,\n days: [0, 0, 0, 0, 0, 0, 0] // 7 Stellen für Su, Mo, Di, Mi, Do, Fr, Sa\n },\n "840_1200": {\n start: 840,\n end: 1200,\n days: [0, 0, 0, 0, 0, 0, 0]\n }\n }\n \n Dabei wird für jeden Tag in der entsprechenden Gruppe der Index im days-Array auf 1 gesetzt.\n/\n\n// Mapping der Wochentage auf Index im days-Array:\n// Index 0 = Sonntag, 1 = Montag, 2 = Dienstag, 3 = Mittwoch, 4 = Donnerstag, 5 = Freitag, 6 = Samstag\nvar weekDayIndex = {\n "Su": 0,\n "Mo": 1,\n "Tu": 2,\n "We": 3,\n "Th": 4,\n "Fr": 5,\n "Sa": 6\n};\n\nvar groups = {};\n\n// Gehe alle Tage durch und alle Intervalle des jeweiligen Tages\nfor (var day in dayIntervals) {\n var intervals = dayIntervals[day];\n intervals.forEach(function(interval) {\n // Nur weiter verarbeiten, wenn sowohl start als auch end vorhanden sind\n if (typeof interval.start !== "undefined" && typeof interval.end !== "undefined") {\n var key = interval.start + "
      " + interval.end;\n // Falls Gruppe noch nicht existiert, anlegen\n if (!groups[key]) {\n groups[key] = {\n start: interval.start,\n end: interval.end,\n days: [0, 0, 0, 0, 0, 0, 0]\n };\n }\n // Bestimme den Index des aktuellen Tages (ggf. alternative Schreibweisen beachten)\n var idx = weekDayIndex[day];\n if (typeof idx !== "undefined") {\n groups[key].days[idx] = 1;\n }\n }\n });\n}\n\n/\n Schritt 3: Aufbau des finalen Timers-Arrays.\n Für jede Gruppe wird ein Objekt erstellt, das folgende Struktur hat:\n \n {\n "starttime": <absolute Startzeit>,\n "days": [Array mit 7 Werten],\n "output": "0", // oder ein anderer fest definierter Wert\n "endtime": <absolute Endzeit>\n }\n \n Dabei werden die Zeiten über die Funktion toTimestamp() umgerechnet.\n/\n// Zuerst bestimmen wir den Wert von output abhängig vom msg.program\nvar outputValue = "0"; // Standardwert\nif (msg.program === "B") {\n outputValue = "1";\n} else if (msg.program === "A") {\n outputValue = "0";\n}\n\nvar timers = [];\nfor (var key in groups) {\n var grp = groups[key];\n timers.push({\n starttime: toTimestamp(grp.start),\n endtime: toTimestamp(grp.end),\n days: grp.days,\n output: outputValue\n });\n}\n\n/\n Schritt 4: Aufbau des finalen Ausgabeobjekts.\n Hier wird zusätzlich ein fester "settings"-Block angehängt.\n\nmsg.payload = {\n timers: timers,\n settings: {\n disabledDevices: ["1"],\n overviewFilter: "all"\n }\n};\n/\nmsg.payload = timers;\n\nreturn msg;",
      "outputs": 1,
      "timeout": 0,
      "noerr": 0,
      "initialize": "",
      "finalize": "",
      "libs": [],
      "x": 760,
      "y": 220,
      "wires": [
      [
      "a787ad3e7e0c0361"
      ]
      ]
      },
      {
      "id": "823d912cec3f4a16",
      "type": "ioBroker get",
      "z": "fd7f31397b058e66",
      "name": "switchPrograms.B",
      "topic": "",
      "attrname": "payload",
      "payloadType": "value",
      "x": 470,
      "y": 220,
      "wires": [
      [
      "80c474d6b95f4577"
      ]
      ]
      },
      {
      "id": "80c474d6b95f4577",
      "type": "json",
      "z": "fd7f31397b058e66",
      "name": "",
      "property": "payload",
      "action": "obj",
      "pretty": false,
      "x": 630,
      "y": 220,
      "wires": [
      [
      "dd20b12c905c22fc"
      ]
      ]
      },
      {
      "id": "9a3dc889e642ccd9",
      "type": "change",
      "z": "fd7f31397b058e66",
      "name": "topic_B->topic",
      "rules": [
      {
      "t": "move",
      "p": "topic_B",
      "pt": "msg",
      "to": "topic",
      "tot": "msg"
      },
      {
      "t": "set",
      "p": "program",
      "pt": "msg",
      "to": "B",
      "tot": "str"
      }
      ],
      "action": "",
      "property": "",
      "from": "",
      "to": "",
      "reg": false,
      "x": 480,
      "y": 180,
      "wires": [
      [
      "823d912cec3f4a16"
      ]
      ]
      },
      {
      "id": "0fae35c54a79d5af",
      "type": "ioBroker get",
      "z": "fd7f31397b058e66",
      "name": "activeSwitchProgram",
      "topic": "",
      "attrname": "payload",
      "payloadType": "value",
      "x": 480,
      "y": 320,
      "wires": [
      [
      "d175fb6bdb10e573"
      ]
      ]
      },
      {
      "id": "3e17abbc9832a241",
      "type": "change",
      "z": "fd7f31397b058e66",
      "name": "activeSwitchProgram->topic",
      "rules": [
      {
      "t": "move",
      "p": "activeSwitchProgram",
      "pt": "msg",
      "to": "topic",
      "tot": "msg"
      }
      ],
      "action": "",
      "property": "",
      "from": "",
      "to": "",
      "reg": false,
      "x": 500,
      "y": 280,
      "wires": [
      [
      "0fae35c54a79d5af"
      ]
      ]
      },
      {
      "id": "d175fb6bdb10e573",
      "type": "change",
      "z": "fd7f31397b058e66",
      "name": "topic activeSwitchProgram",
      "rules": [
      {
      "t": "set",
      "p": "topic",
      "pt": "msg",
      "to": "activeSwitchProgram",
      "tot": "str"
      }
      ],
      "action": "",
      "property": "",
      "from": "",
      "to": "",
      "reg": false,
      "x": 900,
      "y": 320,
      "wires": [
      [
      "0b028760a51cdc98"
      ]
      ]
      },
      {
      "id": "73d6757f1e5fdf61",
      "type": "change",
      "z": "fd7f31397b058e66",
      "name": "topic timers_A",
      "rules": [
      {
      "t": "set",
      "p": "topic",
      "pt": "msg",
      "to": "timers_A",
      "tot": "str"
      }
      ],
      "action": "",
      "property": "",
      "from": "",
      "to": "",
      "reg": false,
      "x": 940,
      "y": 140,
      "wires": [
      [
      "0b028760a51cdc98"
      ]
      ]
      },
      {
      "id": "a787ad3e7e0c0361",
      "type": "change",
      "z": "fd7f31397b058e66",
      "name": "topic timers_B",
      "rules": [
      {
      "t": "set",
      "p": "topic",
      "pt": "msg",
      "to": "timers_B",
      "tot": "str"
      }
      ],
      "action": "",
      "property": "",
      "from": "",
      "to": "",
      "reg": false,
      "x": 940,
      "y": 220,
      "wires": [
      [
      "0b028760a51cdc98"
      ]
      ]
      },
      {
      "id": "0b028760a51cdc98",
      "type": "join",
      "z": "fd7f31397b058e66",
      "name": "",
      "mode": "custom",
      "build": "object",
      "property": "payload",
      "propertyType": "msg",
      "key": "topic",
      "joiner": "\n",
      "joinerType": "str",
      "accumulate": false,
      "timeout": "",
      "count": "3",
      "reduceRight": false,
      "reduceExp": "",
      "reduceInit": "",
      "reduceInitType": "",
      "reduceFixup": "",
      "x": 1170,
      "y": 220,
      "wires": [
      [
      "12c457a5ee91046c"
      ]
      ]
      },
      {
      "id": "12c457a5ee91046c",
      "type": "function",
      "z": "fd7f31397b058e66",
      "name": "timers",
      "func": "//node.warn(JSON.stringify(msg));\n\nvar timers_A = msg.payload.timers_A || [];\nvar timers_B = msg.payload.timers_B || [];\n\nvar timers = timers_A.concat(timers_B);\n\n// Prüfe den Wert von activeSwitchProgram\nvar disabledDevices = (msg.payload.activeSwitchProgram === 0) ? ["1"] : ["0"];\n\nmsg.payload = {\n timers: timers,\n settings: {\n disabledDevices: disabledDevices,\n overviewFilter: "all"\n }\n};\n\nreturn msg;",
      "outputs": 1,
      "timeout": 0,
      "noerr": 0,
      "initialize": "",
      "finalize": "",
      "libs": [],
      "x": 1290,
      "y": 220,
      "wires": [
      []
      ]
      },
      {
      "id": "1dc1799ec4ec93f2",
      "type": "tab",
      "label": "Heizung Zeitprogramm",
      "disabled": false,
      "info": "",
      "env": []
      },
      {
      "id": "34521d86747bd8bd",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "9749f3015788db19",
      "8408c6fde425aef6",
      "b9e930348330f1a7",
      "c419a0b8794650c8",
      "87fcae0db87d8ec7",
      "b71674975643d4c6",
      "64c274d3e12760cd",
      "d86ee423ac01ac1d"
      ],
      "x": 34,
      "y": 13,
      "w": 998,
      "h": 314
      },
      {
      "id": "c6885eaad8ba447a",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "e87bd978436f175d",
      "7a6e354a4dc03390",
      "4497347fc72f4c67"
      ],
      "x": 1054,
      "y": 19,
      "w": 292,
      "h": 122
      },
      {
      "id": "5607675eb8b28b91",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "f22a7d5a405263b9",
      "86b5b09cb63644c1",
      "ce8ae0fc54a4d15a"
      ],
      "x": 34,
      "y": 339,
      "w": 438,
      "h": 475.5
      },
      {
      "id": "87aa28c70609a83b",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "3ee41d6b6e418725",
      "4c8b49a972950636",
      "73cfa9d90e59c8f2"
      ],
      "x": 488,
      "y": 339,
      "w": 344,
      "h": 468
      },
      {
      "id": "bc982b55b594a1be",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "804b40577d582f81",
      "2d7a89955c419a2e",
      "3bd48465ca73fb26"
      ],
      "x": 848,
      "y": 339,
      "w": 644,
      "h": 468
      },
      {
      "id": "64c274d3e12760cd",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "g": "34521d86747bd8bd",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "adcb82cb3b2ca1c0",
      "e991c15e29c9e763",
      "48ce6a5974ab43e7",
      "ae32871f3dfe3184",
      "57973e094f933eaf"
      ],
      "x": 634,
      "y": 179,
      "w": 372,
      "h": 122
      },
      {
      "id": "d86ee423ac01ac1d",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "g": "34521d86747bd8bd",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "72b13674ea7cb2c6",
      "5ae27aa26c57fb34",
      "8a494a52f8a84a72",
      "4730defac1edfe3a",
      "b181124ed41c964c"
      ],
      "x": 634,
      "y": 39,
      "w": 372,
      "h": 122
      },
      {
      "id": "86b5b09cb63644c1",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "g": "5607675eb8b28b91",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "80cbeafa06501ebd",
      "aadc625bce68705a",
      "f27da676130dbb50",
      "a8a5c24a6c414379"
      ],
      "x": 64,
      "y": 659,
      "w": 382,
      "h": 129.5
      },
      {
      "id": "ce8ae0fc54a4d15a",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "g": "5607675eb8b28b91",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "b4059551aadd1ba8",
      "b58755c1aff6132f",
      "494583202e3e8494",
      "10b6a4012722e8ad"
      ],
      "x": 64,
      "y": 399,
      "w": 382,
      "h": 129.5
      },
      {
      "id": "4c8b49a972950636",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "g": "87aa28c70609a83b",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "69b371c33441d3dc",
      "8b1a501f36b9733a",
      "3b325db587bbcdb6"
      ],
      "x": 514,
      "y": 659,
      "w": 292,
      "h": 122
      },
      {
      "id": "73cfa9d90e59c8f2",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "g": "87aa28c70609a83b",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "51e634864245163f",
      "fb67c94b04023f8d",
      "4fce0eb37daebf39"
      ],
      "x": 514,
      "y": 399,
      "w": 292,
      "h": 122
      },
      {
      "id": "2d7a89955c419a2e",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "g": "bc982b55b594a1be",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "e67070774e76ceed",
      "73623095fc2284e5",
      "282f35b8fde4649d",
      "439e908c416c021c",
      "a56607f2cbb3e1d5",
      "bc3a160867da8da4",
      "d0ce33fc89b9d55c"
      ],
      "x": 874,
      "y": 599,
      "w": 592,
      "h": 182
      },
      {
      "id": "3bd48465ca73fb26",
      "type": "group",
      "z": "1dc1799ec4ec93f2",
      "g": "bc982b55b594a1be",
      "style": {
      "stroke": "#999999",
      "stroke-opacity": "1",
      "fill": "none",
      "fill-opacity": "1",
      "label": true,
      "label-position": "nw",
      "color": "#a4a4a4"
      },
      "nodes": [
      "1fbebe44e887c212",
      "4c97aeaa84cf0530",
      "49b29ab6eae048ec",
      "e15fe52156f0da29",
      "62f8829176a49bbb",
      "b6ac03883a2c5fae",
      "0162469a4966cc8d"
      ],
      "x": 874,
      "y": 399,
      "w": 592,
      "h": 182
      },
      {
      "id": "9749f3015788db19",
      "type": "inject",
      "z": "1dc1799ec4ec93f2",
      "g": "34521d86747bd8bd",
      "name": "Trigger",
      "props": [
      {
      "p": "payload"
      },
      {
      "p": "topic",
      "vt": "str"
      }
      ],
      "repeat": "1",
      "crontab": "",
      "once": true,
      "onceDelay": 0.1,
      "topic": "",
      "payload": "",
      "payloadType": "date",
      "x": 140,
      "y": 180,
      "wires": [
      [
      "b9e930348330f1a7"
      ]
      ]
      },
      {
      "id": "72b13674ea7cb2c6",
      "type": "subflow:fd7f31397b058e66",
      "z": "1dc1799ec4ec93f2",
      "g": "d86ee423ac01ac1d",
      "name": "",
      "x": 730,
      "y": 120,
      "wires": [
      [
      "5ae27aa26c57fb34"
      ]
      ],
      "icon": "node-red/batch.svg"
      },
      {
      "id": "adcb82cb3b2ca1c0",
      "type": "subflow:fd7f31397b058e66",
      "z": "1dc1799ec4ec93f2",
      "g": "64c274d3e12760cd",
      "name": "",
      "env": [
      {
      "name": "PROGRAMS_A",
      "value": "ems-esp.0.heatingCircuits.hc2.switchPrograms.A",
      "type": "str"
      },
      {
      "name": "PROGRAMS_B",
      "value": "ems-esp.0.heatingCircuits.hc2.switchPrograms.B",
      "type": "str"
      },
      {
      "name": "ACTIVE",
      "value": "ems-esp.0.heatingCircuits.hc2.activeSwitchProgram",
      "type": "str"
      }
      ],
      "x": 730,
      "y": 220,
      "wires": [
      [
      "e991c15e29c9e763"
      ]
      ],
      "icon": "node-red/batch.svg"
      },
      {
      "id": "8408c6fde425aef6",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "34521d86747bd8bd",
      "name": "### hole Daten von Heizung ###",
      "info": "",
      "x": 190,
      "y": 60,
      "wires": []
      },
      {
      "id": "b4059551aadd1ba8",
      "type": "ui_time_scheduler",
      "z": "1dc1799ec4ec93f2",
      "g": "ce8ae0fc54a4d15a",
      "group": "bfe48a3e.1b0f4",
      "name": "ui scheduler hc1",
      "startDay": "1",
      "refresh": "10",
      "devices": [
      "switchPrograms.A",
      "switchPrograms.B"
      ],
      "singleOff": false,
      "onlySendChange": false,
      "customPayload": false,
      "eventMode": false,
      "eventOptions": [],
      "sendTopic": false,
      "lat": "",
      "lon": "",
      "customContextStore": "",
      "outputs": 3,
      "order": 2,
      "width": 7,
      "height": 7,
      "x": 220,
      "y": 480,
      "wires": [
      [
      "494583202e3e8494"
      ],
      [],
      []
      ]
      },
      {
      "id": "80cbeafa06501ebd",
      "type": "ui_time_scheduler",
      "z": "1dc1799ec4ec93f2",
      "g": "86b5b09cb63644c1",
      "group": "fc9e25e07ff9439d",
      "name": "ui scheduler hc2",
      "startDay": "1",
      "refresh": "10",
      "devices": [
      "switchPrograms.A",
      "switchPrograms.B"
      ],
      "singleOff": false,
      "onlySendChange": false,
      "customPayload": false,
      "eventMode": false,
      "eventOptions": [],
      "sendTopic": false,
      "lat": "",
      "lon": "",
      "customContextStore": "",
      "outputs": 3,
      "order": 2,
      "width": 7,
      "height": 7,
      "x": 220,
      "y": 740,
      "wires": [
      [
      "f27da676130dbb50"
      ],
      [],
      []
      ]
      },
      {
      "id": "5ae27aa26c57fb34",
      "type": "json",
      "z": "1dc1799ec4ec93f2",
      "g": "d86ee423ac01ac1d",
      "name": "",
      "property": "payload",
      "action": "str",
      "pretty": false,
      "x": 850,
      "y": 120,
      "wires": [
      [
      "8a494a52f8a84a72"
      ]
      ]
      },
      {
      "id": "e991c15e29c9e763",
      "type": "json",
      "z": "1dc1799ec4ec93f2",
      "g": "64c274d3e12760cd",
      "name": "",
      "property": "payload",
      "action": "str",
      "pretty": false,
      "x": 850,
      "y": 220,
      "wires": [
      [
      "48ce6a5974ab43e7"
      ]
      ]
      },
      {
      "id": "b9e930348330f1a7",
      "type": "ui_ui_control",
      "z": "1dc1799ec4ec93f2",
      "g": "34521d86747bd8bd",
      "name": "Tab Activity Control",
      "events": "change",
      "x": 310,
      "y": 180,
      "wires": [
      [
      "c419a0b8794650c8",
      "87fcae0db87d8ec7"
      ]
      ]
      },
      {
      "id": "c419a0b8794650c8",
      "type": "switch",
      "z": "1dc1799ec4ec93f2",
      "g": "34521d86747bd8bd",
      "name": "Check if Tab is Active",
      "property": "name",
      "propertyType": "msg",
      "rules": [
      {
      "t": "eq",
      "v": "Heizung Dashboard",
      "vt": "str"
      }
      ],
      "checkall": "true",
      "repair": false,
      "outputs": 1,
      "x": 520,
      "y": 180,
      "wires": [
      [
      "72b13674ea7cb2c6",
      "adcb82cb3b2ca1c0"
      ]
      ]
      },
      {
      "id": "87fcae0db87d8ec7",
      "type": "debug",
      "z": "1dc1799ec4ec93f2",
      "g": "34521d86747bd8bd",
      "name": "Ausgabe Tab Name",
      "active": false,
      "tosidebar": true,
      "console": false,
      "tostatus": false,
      "complete": "name",
      "targetType": "msg",
      "statusVal": "",
      "statusType": "auto",
      "x": 510,
      "y": 220,
      "wires": []
      },
      {
      "id": "b71674975643d4c6",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "34521d86747bd8bd",
      "name": "Tab Name anpassen",
      "info": "",
      "x": 510,
      "y": 140,
      "wires": [],
      "icon": "node-red/alert.svg"
      },
      {
      "id": "8a494a52f8a84a72",
      "type": "link out",
      "z": "1dc1799ec4ec93f2",
      "g": "d86ee423ac01ac1d",
      "name": "Data HC1",
      "mode": "link",
      "links": [
      "b58755c1aff6132f"
      ],
      "x": 945,
      "y": 120,
      "wires": []
      },
      {
      "id": "b58755c1aff6132f",
      "type": "link in",
      "z": "1dc1799ec4ec93f2",
      "g": "ce8ae0fc54a4d15a",
      "name": "Data HC1",
      "links": [
      "8a494a52f8a84a72"
      ],
      "x": 105,
      "y": 480,
      "wires": [
      [
      "b4059551aadd1ba8"
      ]
      ]
      },
      {
      "id": "48ce6a5974ab43e7",
      "type": "link out",
      "z": "1dc1799ec4ec93f2",
      "g": "64c274d3e12760cd",
      "name": "Data HC2",
      "mode": "link",
      "links": [
      "aadc625bce68705a"
      ],
      "x": 945,
      "y": 220,
      "wires": []
      },
      {
      "id": "aadc625bce68705a",
      "type": "link in",
      "z": "1dc1799ec4ec93f2",
      "g": "86b5b09cb63644c1",
      "name": "Data HC2",
      "links": [
      "48ce6a5974ab43e7"
      ],
      "x": 105,
      "y": 740,
      "wires": [
      [
      "80cbeafa06501ebd"
      ]
      ]
      },
      {
      "id": "4730defac1edfe3a",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "d86ee423ac01ac1d",
      "name": "Path zu objecten anpassen",
      "info": "",
      "x": 770,
      "y": 80,
      "wires": [],
      "icon": "node-red/alert.svg"
      },
      {
      "id": "e87bd978436f175d",
      "type": "ui_toast",
      "z": "1dc1799ec4ec93f2",
      "g": "c6885eaad8ba447a",
      "position": "top right",
      "displayTime": "5",
      "highlight": "",
      "sendall": true,
      "outputs": 0,
      "ok": "OK",
      "cancel": "",
      "raw": false,
      "className": "",
      "topic": "",
      "name": "alert Messages",
      "x": 1240,
      "y": 100,
      "wires": []
      },
      {
      "id": "494583202e3e8494",
      "type": "json",
      "z": "1dc1799ec4ec93f2",
      "g": "ce8ae0fc54a4d15a",
      "name": "",
      "property": "payload",
      "action": "",
      "pretty": false,
      "x": 370,
      "y": 480,
      "wires": [
      [
      "fb67c94b04023f8d"
      ]
      ]
      },
      {
      "id": "f27da676130dbb50",
      "type": "json",
      "z": "1dc1799ec4ec93f2",
      "g": "86b5b09cb63644c1",
      "name": "",
      "property": "payload",
      "action": "",
      "pretty": false,
      "x": 370,
      "y": 740,
      "wires": [
      [
      "69b371c33441d3dc"
      ]
      ]
      },
      {
      "id": "1fbebe44e887c212",
      "type": "ui_template",
      "z": "1dc1799ec4ec93f2",
      "g": "3bd48465ca73fb26",
      "group": "bfe48a3e.1b0f4",
      "name": "Button speichern",
      "order": 1,
      "width": 0,
      "height": 0,
      "format": "<style>\n /* Eigene CSS-Klasse für den deaktivierten Zustand /\n .my-disabled-btn {\n display: none !important;\n }\n</style>\n\n<div layout="column" style="text-align: center;">\n <md-button class="md-raised" ng-disabled="msg.hide_save_button" ng-click="send(msg)"\n ng-class="{'my-disabled-btn': msg.hide_save_button, 'md-primary': !msg.hide_save_button}">\n <md-icon>save</md-icon>\n jetzt speichern\n </md-button>\n</div>\n \n<script>\n (function(scope) {\n // Falls noch kein msg vorhanden ist, initialisieren wir es.\n if (!scope.msg) {\n scope.msg = {};\n }\n // Wenn msg.hide_save_button nicht definiert ist, setzen wir es auf true (Button deaktiviert)\n if (typeof scope.msg.hide_save_button === 'undefined') {\n scope.msg.hide_save_button = true;\n }\n })(scope);\n</script>",
      "storeOutMessages": true,
      "fwdInMessages": false,
      "resendOnRefresh": true,
      "templateScope": "local",
      "className": "",
      "x": 1070,
      "y": 540,
      "wires": [
      [
      "62f8829176a49bbb"
      ]
      ]
      },
      {
      "id": "4c97aeaa84cf0530",
      "type": "inject",
      "z": "1dc1799ec4ec93f2",
      "g": "3bd48465ca73fb26",
      "name": "Init Button",
      "props": [
      {
      "p": "hide_save_button",
      "v": "true",
      "vt": "bool"
      }
      ],
      "repeat": "",
      "crontab": "",
      "once": true,
      "onceDelay": "0.1",
      "topic": "",
      "x": 990,
      "y": 480,
      "wires": [
      [
      "1fbebe44e887c212"
      ]
      ]
      },
      {
      "id": "49b29ab6eae048ec",
      "type": "change",
      "z": "1dc1799ec4ec93f2",
      "g": "3bd48465ca73fb26",
      "name": "done hide Button",
      "rules": [
      {
      "t": "set",
      "p": "hide_save_button",
      "pt": "msg",
      "to": "true",
      "tot": "bool"
      },
      {
      "t": "set",
      "p": "highlight",
      "pt": "msg",
      "to": "green",
      "tot": "str"
      },
      {
      "t": "set",
      "p": "topic",
      "pt": "msg",
      "to": "Info",
      "tot": "str"
      },
      {
      "t": "set",
      "p": "payload",
      "pt": "msg",
      "to": "Erfolgreich gespeichert",
      "tot": "str"
      }
      ],
      "action": "",
      "property": "",
      "from": "",
      "to": "",
      "reg": false,
      "x": 1230,
      "y": 480,
      "wires": [
      [
      "1fbebe44e887c212",
      "e15fe52156f0da29"
      ]
      ]
      },
      {
      "id": "51e634864245163f",
      "type": "link out",
      "z": "1dc1799ec4ec93f2",
      "g": "73cfa9d90e59c8f2",
      "name": "To_Messages",
      "mode": "link",
      "links": [
      "7a6e354a4dc03390"
      ],
      "x": 765,
      "y": 460,
      "wires": []
      },
      {
      "id": "7a6e354a4dc03390",
      "type": "link in",
      "z": "1dc1799ec4ec93f2",
      "g": "c6885eaad8ba447a",
      "name": "Messages_IN",
      "links": [
      "51e634864245163f",
      "e15fe52156f0da29",
      "8b1a501f36b9733a",
      "8b92bee47f47148b",
      "439e908c416c021c"
      ],
      "x": 1105,
      "y": 100,
      "wires": [
      [
      "e87bd978436f175d"
      ]
      ]
      },
      {
      "id": "e15fe52156f0da29",
      "type": "link out",
      "z": "1dc1799ec4ec93f2",
      "g": "3bd48465ca73fb26",
      "name": "To_Messages",
      "mode": "link",
      "links": [
      "7a6e354a4dc03390"
      ],
      "x": 1385,
      "y": 480,
      "wires": []
      },
      {
      "id": "4497347fc72f4c67",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "c6885eaad8ba447a",
      "name": "### Benachrichtigungen ###",
      "info": "",
      "x": 1200,
      "y": 60,
      "wires": []
      },
      {
      "id": "fb67c94b04023f8d",
      "type": "subflow:110aeb09527e60b1",
      "z": "1dc1799ec4ec93f2",
      "g": "73cfa9d90e59c8f2",
      "name": "",
      "x": 630,
      "y": 480,
      "wires": [
      [
      "51e634864245163f"
      ],
      [
      "1fbebe44e887c212"
      ]
      ]
      },
      {
      "id": "69b371c33441d3dc",
      "type": "subflow:110aeb09527e60b1",
      "z": "1dc1799ec4ec93f2",
      "g": "4c8b49a972950636",
      "name": "",
      "x": 630,
      "y": 740,
      "wires": [
      [
      "8b1a501f36b9733a"
      ],
      [
      "e67070774e76ceed"
      ]
      ]
      },
      {
      "id": "8b1a501f36b9733a",
      "type": "link out",
      "z": "1dc1799ec4ec93f2",
      "g": "4c8b49a972950636",
      "name": "To_Messages",
      "mode": "link",
      "links": [
      "7a6e354a4dc03390"
      ],
      "x": 765,
      "y": 720,
      "wires": []
      },
      {
      "id": "62f8829176a49bbb",
      "type": "subflow:b647dcc6973cef0a",
      "z": "1dc1799ec4ec93f2",
      "g": "3bd48465ca73fb26",
      "name": "",
      "x": 1230,
      "y": 540,
      "wires": [
      [
      "49b29ab6eae048ec",
      "b6ac03883a2c5fae"
      ]
      ]
      },
      {
      "id": "e67070774e76ceed",
      "type": "ui_template",
      "z": "1dc1799ec4ec93f2",
      "g": "2d7a89955c419a2e",
      "group": "fc9e25e07ff9439d",
      "name": "Button speichern",
      "order": 1,
      "width": 0,
      "height": 0,
      "format": "<style>\n /
      Eigene CSS-Klasse für den deaktivierten Zustand */\n .my-disabled-btn {\n display: none !important;\n }\n</style>\n\n<div layout="column" style="text-align: center;">\n <md-button class="md-raised" ng-disabled="msg.hide_save_button" ng-click="send(msg)"\n ng-class="{'my-disabled-btn': msg.hide_save_button, 'md-primary': !msg.hide_save_button}">\n <md-icon>save</md-icon>\n jetzt speichern\n </md-button>\n</div>\n \n<script>\n (function(scope) {\n // Falls noch kein msg vorhanden ist, initialisieren wir es.\n if (!scope.msg) {\n scope.msg = {};\n }\n // Wenn msg.hide_save_button nicht definiert ist, setzen wir es auf true (Button deaktiviert)\n if (typeof scope.msg.hide_save_button === 'undefined') {\n scope.msg.hide_save_button = true;\n }\n })(scope);\n</script>",
      "storeOutMessages": true,
      "fwdInMessages": false,
      "resendOnRefresh": true,
      "templateScope": "local",
      "className": "",
      "x": 1070,
      "y": 740,
      "wires": [
      [
      "a56607f2cbb3e1d5"
      ]
      ]
      },
      {
      "id": "73623095fc2284e5",
      "type": "inject",
      "z": "1dc1799ec4ec93f2",
      "g": "2d7a89955c419a2e",
      "name": "Init Button",
      "props": [
      {
      "p": "hide_save_button",
      "v": "true",
      "vt": "bool"
      }
      ],
      "repeat": "",
      "crontab": "",
      "once": true,
      "onceDelay": "0.1",
      "topic": "",
      "x": 990,
      "y": 680,
      "wires": [
      [
      "e67070774e76ceed"
      ]
      ]
      },
      {
      "id": "282f35b8fde4649d",
      "type": "change",
      "z": "1dc1799ec4ec93f2",
      "g": "2d7a89955c419a2e",
      "name": "done hide Button",
      "rules": [
      {
      "t": "set",
      "p": "hide_save_button",
      "pt": "msg",
      "to": "true",
      "tot": "bool"
      },
      {
      "t": "set",
      "p": "highlight",
      "pt": "msg",
      "to": "green",
      "tot": "str"
      },
      {
      "t": "set",
      "p": "topic",
      "pt": "msg",
      "to": "Info",
      "tot": "str"
      },
      {
      "t": "set",
      "p": "payload",
      "pt": "msg",
      "to": "Erfolgreich gespeichert",
      "tot": "str"
      }
      ],
      "action": "",
      "property": "",
      "from": "",
      "to": "",
      "reg": false,
      "x": 1230,
      "y": 680,
      "wires": [
      [
      "e67070774e76ceed",
      "439e908c416c021c"
      ]
      ]
      },
      {
      "id": "439e908c416c021c",
      "type": "link out",
      "z": "1dc1799ec4ec93f2",
      "g": "2d7a89955c419a2e",
      "name": "To_Messages",
      "mode": "link",
      "links": [
      "7a6e354a4dc03390"
      ],
      "x": 1385,
      "y": 680,
      "wires": []
      },
      {
      "id": "a56607f2cbb3e1d5",
      "type": "subflow:b647dcc6973cef0a",
      "z": "1dc1799ec4ec93f2",
      "g": "2d7a89955c419a2e",
      "name": "",
      "x": 1230,
      "y": 740,
      "wires": [
      [
      "282f35b8fde4649d",
      "bc3a160867da8da4"
      ]
      ]
      },
      {
      "id": "bc3a160867da8da4",
      "type": "debug",
      "z": "1dc1799ec4ec93f2",
      "g": "2d7a89955c419a2e",
      "name": "debug",
      "active": true,
      "tosidebar": true,
      "console": false,
      "tostatus": false,
      "complete": "payload",
      "targetType": "msg",
      "statusVal": "",
      "statusType": "auto",
      "x": 1370,
      "y": 740,
      "wires": []
      },
      {
      "id": "b6ac03883a2c5fae",
      "type": "debug",
      "z": "1dc1799ec4ec93f2",
      "g": "3bd48465ca73fb26",
      "name": "debug",
      "active": true,
      "tosidebar": true,
      "console": false,
      "tostatus": false,
      "complete": "payload",
      "targetType": "msg",
      "statusVal": "",
      "statusType": "auto",
      "x": 1370,
      "y": 540,
      "wires": []
      },
      {
      "id": "ae32871f3dfe3184",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "64c274d3e12760cd",
      "name": "Path zu objecten anpassen",
      "info": "",
      "x": 770,
      "y": 260,
      "wires": [],
      "icon": "node-red/alert.svg"
      },
      {
      "id": "3ee41d6b6e418725",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "87aa28c70609a83b",
      "name": "### Fehlerauswertung ###",
      "info": "",
      "x": 630,
      "y": 380,
      "wires": []
      },
      {
      "id": "804b40577d582f81",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "bc982b55b594a1be",
      "name": "### sende Daten zu Heizung ###",
      "info": "",
      "x": 1010,
      "y": 380,
      "wires": []
      },
      {
      "id": "f22a7d5a405263b9",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "5607675eb8b28b91",
      "name": "### Scheduler ###",
      "info": "",
      "x": 150,
      "y": 380,
      "wires": []
      },
      {
      "id": "b181124ed41c964c",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "d86ee423ac01ac1d",
      "name": "## HC1",
      "info": "",
      "x": 930,
      "y": 80,
      "wires": []
      },
      {
      "id": "57973e094f933eaf",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "64c274d3e12760cd",
      "name": "## HC2",
      "info": "",
      "x": 930,
      "y": 260,
      "wires": []
      },
      {
      "id": "10b6a4012722e8ad",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "ce8ae0fc54a4d15a",
      "name": "## HC1",
      "info": "",
      "x": 190,
      "y": 440,
      "wires": []
      },
      {
      "id": "a8a5c24a6c414379",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "86b5b09cb63644c1",
      "name": "## HC2",
      "info": "",
      "x": 190,
      "y": 700,
      "wires": []
      },
      {
      "id": "3b325db587bbcdb6",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "4c8b49a972950636",
      "name": "## HC2",
      "info": "",
      "x": 590,
      "y": 700,
      "wires": []
      },
      {
      "id": "d0ce33fc89b9d55c",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "2d7a89955c419a2e",
      "name": "## HC2",
      "info": "",
      "x": 950,
      "y": 640,
      "wires": []
      },
      {
      "id": "4fce0eb37daebf39",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "73cfa9d90e59c8f2",
      "name": "## HC1",
      "info": "",
      "x": 590,
      "y": 440,
      "wires": []
      },
      {
      "id": "0162469a4966cc8d",
      "type": "comment",
      "z": "1dc1799ec4ec93f2",
      "g": "3bd48465ca73fb26",
      "name": "## HC1",
      "info": "",
      "x": 950,
      "y": 440,
      "wires": []
      },
      {
      "id": "bfe48a3e.1b0f4",
      "type": "ui_group",
      "name": "HC1",
      "tab": "2936b813.6cde68",
      "order": 1,
      "disp": true,
      "width": "7",
      "collapse": false,
      "className": ""
      },
      {
      "id": "fc9e25e07ff9439d",
      "type": "ui_group",
      "name": "HC2",
      "tab": "2936b813.6cde68",
      "order": 2,
      "disp": true,
      "width": "7",
      "collapse": false,
      "className": ""
      },
      {
      "id": "2936b813.6cde68",
      "type": "ui_tab",
      "name": "Heizung Dashboard",
      "icon": "fa-fire",
      "order": 1,
      "disabled": false,
      "hidden": false
      }
      ]

      T Offline
      T Offline
      tp1de
      schrieb am zuletzt editiert von
      #723

      @kunigunde Willst du die Zeitprogramme per km200 oder ems-esp Gateway ändern?

      K 1 Antwort Letzte Antwort
      0
      • T tp1de

        @kunigunde Willst du die Zeitprogramme per km200 oder ems-esp Gateway ändern?

        K Offline
        K Offline
        kunigunde
        schrieb am zuletzt editiert von kunigunde
        #724

        @tp1de
        ich habe den emsesp Adapter mit aktiviertem km Gateway am laufen, und dieser schreibt dann die werte zur Heizung.
        Dies funktioniert bei mir auch, bis auf das ab und an die Kommunikation zu klemmen scheint.
        Ich bräuchte Ideen, wie ich sicherstellen kann, das es wirklich bei der Heizung angekommen ist nachdem ich mit Node-Red das command gesendet habe.
        Adapter Config: e72384cb-8370-404c-afd4-63020b4d0844-image.png982230ed-93b0-4ad8-a56e-155a0b4e9db6-image.png
        EMSESP: 34f0cae7-2b56-4d87-83c9-e24902a59cf6-image.png

        T 1 Antwort Letzte Antwort
        0
        • K kunigunde

          @tp1de
          ich habe den emsesp Adapter mit aktiviertem km Gateway am laufen, und dieser schreibt dann die werte zur Heizung.
          Dies funktioniert bei mir auch, bis auf das ab und an die Kommunikation zu klemmen scheint.
          Ich bräuchte Ideen, wie ich sicherstellen kann, das es wirklich bei der Heizung angekommen ist nachdem ich mit Node-Red das command gesendet habe.
          Adapter Config: e72384cb-8370-404c-afd4-63020b4d0844-image.png982230ed-93b0-4ad8-a56e-155a0b4e9db6-image.png
          EMSESP: 34f0cae7-2b56-4d87-83c9-e24902a59cf6-image.png

          T Offline
          T Offline
          tp1de
          schrieb am zuletzt editiert von
          #725

          @kunigunde Ich verstehe deine Antwort nicht richtig:

          Wenn du das km200 Gateway aktiv hast, dann kannst du doch direkt in die JSON-Struktur des Zeitprogrammes schreiben.

          Nur ohne km200 - also nur mit ems-esp - solltest du mit dem ems-esp schreiben.
          Dazu bräuchtest du die Testversion der Firmware von MichaelDvP: https://github.com/MichaelDvP/EMS-ESP32/releases

          Diese funktioniert ein bisschen anders: Das aktuelle Switchprogramm (A oder B) in Abhängigkeit der Zeitprogramm-Art (Level oder Absolut) wird als JSON geschrieben und steht im Adapter dann als Datenpunkt zur Verfügung. Bei mir muss ich Änderungen 3 Mal schreiben mit jeweils 2-3 Sekunden dazwischen, bis diese richtig geschrieben werden. Beim km200 Gateway funktioniert das direkt.

          M 1 Antwort Letzte Antwort
          0
          • T tp1de

            @kunigunde Ich verstehe deine Antwort nicht richtig:

            Wenn du das km200 Gateway aktiv hast, dann kannst du doch direkt in die JSON-Struktur des Zeitprogrammes schreiben.

            Nur ohne km200 - also nur mit ems-esp - solltest du mit dem ems-esp schreiben.
            Dazu bräuchtest du die Testversion der Firmware von MichaelDvP: https://github.com/MichaelDvP/EMS-ESP32/releases

            Diese funktioniert ein bisschen anders: Das aktuelle Switchprogramm (A oder B) in Abhängigkeit der Zeitprogramm-Art (Level oder Absolut) wird als JSON geschrieben und steht im Adapter dann als Datenpunkt zur Verfügung. Bei mir muss ich Änderungen 3 Mal schreiben mit jeweils 2-3 Sekunden dazwischen, bis diese richtig geschrieben werden. Beim km200 Gateway funktioniert das direkt.

            M Offline
            M Offline
            Megathomas
            schrieb am zuletzt editiert von
            #726

            Hallo,
            ich habe nun den ems-esp und einen MB Lan2 mit einer Junkers Gastherme.
            Mit Home Assistant bin ich noch nicht wirklich weit gekommen, aber das nur als Nebeninfo zu meinen "Fähigkeiten".

            Die Therme ist zu groß dimensioniert und scheitert bei gemäßigten Außentemperaturen beim Start. Ist es draußen über 5°C und die Therme will starten steigt die Vorlauftemperatur sehr schnell über die Maximaltemperatur der aktuellen Heizkurve -> Therme geht sofort wieder aus. Das wiederholt sich dann in den nächsten Stunden. Dann ist es aber schon kalt im Haus.

            Ich versuche das aktuell manuell abzufangen und stelle kurzfristig die Wunschtemperatur 10°C höher, um diese dann nach dem Modulieren auf ca. 15% wieder auf den Ursprungswert abzusenken. Bosch kennt keine Lösung und der Heizungsbauer kennt nur Heizkurve raufsetzen, weil ja viiiel zu niedrig.
            Kann man das nicht vielleicht automatisieren?

            Ja, ist eine Laienfrage, aber jeder hat Mal klein angefangen. Vielen Dank!

            LG Thomas

            T 1 Antwort Letzte Antwort
            0
            • M Megathomas

              Hallo,
              ich habe nun den ems-esp und einen MB Lan2 mit einer Junkers Gastherme.
              Mit Home Assistant bin ich noch nicht wirklich weit gekommen, aber das nur als Nebeninfo zu meinen "Fähigkeiten".

              Die Therme ist zu groß dimensioniert und scheitert bei gemäßigten Außentemperaturen beim Start. Ist es draußen über 5°C und die Therme will starten steigt die Vorlauftemperatur sehr schnell über die Maximaltemperatur der aktuellen Heizkurve -> Therme geht sofort wieder aus. Das wiederholt sich dann in den nächsten Stunden. Dann ist es aber schon kalt im Haus.

              Ich versuche das aktuell manuell abzufangen und stelle kurzfristig die Wunschtemperatur 10°C höher, um diese dann nach dem Modulieren auf ca. 15% wieder auf den Ursprungswert abzusenken. Bosch kennt keine Lösung und der Heizungsbauer kennt nur Heizkurve raufsetzen, weil ja viiiel zu niedrig.
              Kann man das nicht vielleicht automatisieren?

              Ja, ist eine Laienfrage, aber jeder hat Mal klein angefangen. Vielen Dank!

              LG Thomas

              T Offline
              T Offline
              tp1de
              schrieb am zuletzt editiert von tp1de
              #727

              @megathomas Das sind Fragen für ein Heizungsforum.

              Gas Brennwertgeräte starten mit > 50% Modulation für die ersten 1-2 Minuten um ein sauberes Flammenbild zu garantieren.
              Wenn das schon ausreicht um die Solltemperatur zu erreichen und sich wieder auszuschalten, dann stimmt etwas nicht.
              Wahrscheinlich wurde auch kein hydraulischer Abgleich gemacht.
              Weitere Ursachen können sein:

              • Modulation / Leistung der Heizungspumpe zu klein.
              • Heizkurve ist zu niedrig eingestellt
              • Alle Heizkörperventile sind geschlossen, d.h. die Brennerleistung kann nicht abgenommen werden
              • maximale Brennerleistung ist nicht begrenzt
              • etc.

              Ich rate dringend dazu das mit deinem Installateur zu besprechen und nicht selber Werte zu verändern, wenn du die Konsequenzen nicht verstehst.

              Im ioBroker Adapter gibt es die Möglichkeit die Wärmebedarfsregelung einzuschalten. Das setzt aber über ioBroker regelbare Thermostatventile voraus. Bei den aktuellen Temperaturen sollte das aber noch nicht notwendig sein.

              M 1 Antwort Letzte Antwort
              0
              • T tp1de

                @megathomas Das sind Fragen für ein Heizungsforum.

                Gas Brennwertgeräte starten mit > 50% Modulation für die ersten 1-2 Minuten um ein sauberes Flammenbild zu garantieren.
                Wenn das schon ausreicht um die Solltemperatur zu erreichen und sich wieder auszuschalten, dann stimmt etwas nicht.
                Wahrscheinlich wurde auch kein hydraulischer Abgleich gemacht.
                Weitere Ursachen können sein:

                • Modulation / Leistung der Heizungspumpe zu klein.
                • Heizkurve ist zu niedrig eingestellt
                • Alle Heizkörperventile sind geschlossen, d.h. die Brennerleistung kann nicht abgenommen werden
                • maximale Brennerleistung ist nicht begrenzt
                • etc.

                Ich rate dringend dazu das mit deinem Installateur zu besprechen und nicht selber Werte zu verändern, wenn du die Konsequenzen nicht verstehst.

                Im ioBroker Adapter gibt es die Möglichkeit die Wärmebedarfsregelung einzuschalten. Das setzt aber über ioBroker regelbare Thermostatventile voraus. Bei den aktuellen Temperaturen sollte das aber noch nicht notwendig sein.

                M Offline
                M Offline
                Megathomas
                schrieb am zuletzt editiert von
                #728

                @tp1de

                Hallo,
                ich habe natürlich schon mit der Firma die diese Heizung eingebaut hat gesprochen und dieses Verhalten reklamiert. Der Chef kam vorbei und hat meinen mühselig erstellen thermischen Abgleich in die Tonne geworfen und alle Tacosetter auf die Normalstellung zurück gesetzt. Weiterhin hat er alle Einstellungen der Therme auf den Auslieferungszustand zurück gesetzt. Mag sein, dass das die richtige Einstellung war, um diese Heizung zu Laufen zu bringen, aber eben nicht, um Energie zu sparen.

                Natürlich wurde kein hydraulischer Abgleich erstellt - davon hält der Herr nichts in einem Einfamilienhaus (dem kann ich teilweise zustimmen).
                Die Pumpe läuft auf 100% Leistung.
                Natürlich ist die Heizkurve auf den niedrigstmöglichen Wert eingestellt, mit der soeben die 20°C im Haus erreicht werden.
                Alle Stellventile sind zu 100% offen.
                Die maximale Brennerleistung zu reduzieren hat keinen Effekt auf den Startvorgang. Auch die Einstellung von 20% Maximalleistung lässt die Therme bei 58% starten.

                Ich habe auch schon mit Bosch telefoniert und zwei verschiedene Mitarbeiter haben nur gemutmaßt, dass die Leistung der Therme mit 24kW weit über dem Wert der Heizlast des Hauses liegt.
                Ein zweiter Heizungsbauer war auch schon vor Ort. Der meinte, es könne nicht mit dem Heizungsrohrdurchmesser zu den Verteilern liegen und meinte auch, ich müsse das reklamieren.

                Das ist alles schön, aber löst mein Problem nicht. Ich such also nur nach einem Workaround. Und wenn mich nur Home Assistent zumindest darauf hinweisen könnte ACHTUNG! Brenner ist aus! Ich würde keine Werte ändern wollen deren Funktion ich nicht kenne. Ich wollte einfach nur eine Verschiebung der Heizkurve für 1 Minute automatisieren. Also genau das, was ich händisch immer machen muss.

                LG Thomas

                T 1 Antwort Letzte Antwort
                0
                • M Megathomas

                  @tp1de

                  Hallo,
                  ich habe natürlich schon mit der Firma die diese Heizung eingebaut hat gesprochen und dieses Verhalten reklamiert. Der Chef kam vorbei und hat meinen mühselig erstellen thermischen Abgleich in die Tonne geworfen und alle Tacosetter auf die Normalstellung zurück gesetzt. Weiterhin hat er alle Einstellungen der Therme auf den Auslieferungszustand zurück gesetzt. Mag sein, dass das die richtige Einstellung war, um diese Heizung zu Laufen zu bringen, aber eben nicht, um Energie zu sparen.

                  Natürlich wurde kein hydraulischer Abgleich erstellt - davon hält der Herr nichts in einem Einfamilienhaus (dem kann ich teilweise zustimmen).
                  Die Pumpe läuft auf 100% Leistung.
                  Natürlich ist die Heizkurve auf den niedrigstmöglichen Wert eingestellt, mit der soeben die 20°C im Haus erreicht werden.
                  Alle Stellventile sind zu 100% offen.
                  Die maximale Brennerleistung zu reduzieren hat keinen Effekt auf den Startvorgang. Auch die Einstellung von 20% Maximalleistung lässt die Therme bei 58% starten.

                  Ich habe auch schon mit Bosch telefoniert und zwei verschiedene Mitarbeiter haben nur gemutmaßt, dass die Leistung der Therme mit 24kW weit über dem Wert der Heizlast des Hauses liegt.
                  Ein zweiter Heizungsbauer war auch schon vor Ort. Der meinte, es könne nicht mit dem Heizungsrohrdurchmesser zu den Verteilern liegen und meinte auch, ich müsse das reklamieren.

                  Das ist alles schön, aber löst mein Problem nicht. Ich such also nur nach einem Workaround. Und wenn mich nur Home Assistent zumindest darauf hinweisen könnte ACHTUNG! Brenner ist aus! Ich würde keine Werte ändern wollen deren Funktion ich nicht kenne. Ich wollte einfach nur eine Verschiebung der Heizkurve für 1 Minute automatisieren. Also genau das, was ich händisch immer machen muss.

                  LG Thomas

                  T Offline
                  T Offline
                  tp1de
                  schrieb am zuletzt editiert von tp1de
                  #729

                  @megathomas
                  Du hast dir ja bereits unterschiedliche Meinungen eingeholt. Wenn die Heizung neu ist und du noch Garantie hast, dann hilft nur schriftlich reklamieren und eine Frist zur Fehlerbeseitigung stellen (ggfs. per Rechtsanwalt).
                  Wer hat denn die Heizung mit 24 KW dimensioniert? Du oder der Heizungsbauer?

                  Es ist fast immer so, dass die Heizkessel zu groß dimensioniert werden. Das für dann zu häufigen Takten und früheren Verschleiß.
                  Aber dass die 1-2 Minuten der Startphase ausreichen um die Heizung auszuschalten ist nicht normal und ist ein Hinweis, dass die Hydraulik der gesamten Anlage nicht stimmt.

                  Mein bodenstehender Kessel (22 KW) zuhause hat 17 Liter Wasserinhalt und braucht mehrere Minuten um aufzuheizen.
                  Im Ferienhaus (90 m2) haben wir einen 25 KW wandhängenden Kessel und die Warmwasserbereitung läuft als Durchlauferhitzer.
                  Dieser hat einen sehr geringen Wasserinhalt und heizt sehr schnell auf. Aber ein Abschalten in der Startphase gibt es nicht.

                  Ich bin kein Heizungsbauer und kenne deine Anlage nicht.
                  Probleme mit der Hydraulik über ioBroker zu lösen halte ich für falsch und würde ich nie machen. (würde aber ggfs. über Javascript / Node-Red gehen)
                  Ggfs. must du über einen Pufferspeicher zwischen Kessel und Heizungsanlage nachdenken, wenn du das nicht über die Heizkurve regeln kannst.
                  Ich vermute, dass du nur Fußbodenheizung hast. Regelst du Außentemperatur-abhängig und hängt der Thermostat im Wohnzimmer?
                  Welche Werte hat denn bei dir die Heizkurve? (HK1 min./ max. Vorlauftemperatur in ems-esp)

                  M 1 Antwort Letzte Antwort
                  0
                  • T tp1de

                    @megathomas
                    Du hast dir ja bereits unterschiedliche Meinungen eingeholt. Wenn die Heizung neu ist und du noch Garantie hast, dann hilft nur schriftlich reklamieren und eine Frist zur Fehlerbeseitigung stellen (ggfs. per Rechtsanwalt).
                    Wer hat denn die Heizung mit 24 KW dimensioniert? Du oder der Heizungsbauer?

                    Es ist fast immer so, dass die Heizkessel zu groß dimensioniert werden. Das für dann zu häufigen Takten und früheren Verschleiß.
                    Aber dass die 1-2 Minuten der Startphase ausreichen um die Heizung auszuschalten ist nicht normal und ist ein Hinweis, dass die Hydraulik der gesamten Anlage nicht stimmt.

                    Mein bodenstehender Kessel (22 KW) zuhause hat 17 Liter Wasserinhalt und braucht mehrere Minuten um aufzuheizen.
                    Im Ferienhaus (90 m2) haben wir einen 25 KW wandhängenden Kessel und die Warmwasserbereitung läuft als Durchlauferhitzer.
                    Dieser hat einen sehr geringen Wasserinhalt und heizt sehr schnell auf. Aber ein Abschalten in der Startphase gibt es nicht.

                    Ich bin kein Heizungsbauer und kenne deine Anlage nicht.
                    Probleme mit der Hydraulik über ioBroker zu lösen halte ich für falsch und würde ich nie machen. (würde aber ggfs. über Javascript / Node-Red gehen)
                    Ggfs. must du über einen Pufferspeicher zwischen Kessel und Heizungsanlage nachdenken, wenn du das nicht über die Heizkurve regeln kannst.
                    Ich vermute, dass du nur Fußbodenheizung hast. Regelst du Außentemperatur-abhängig und hängt der Thermostat im Wohnzimmer?
                    Welche Werte hat denn bei dir die Heizkurve? (HK1 min./ max. Vorlauftemperatur in ems-esp)

                    M Offline
                    M Offline
                    Megathomas
                    schrieb am zuletzt editiert von
                    #730

                    @tp1de
                    Hallo,
                    als diese Heizung eingebaut wurde war die Welt noch in Ordnung und somit hatte ich mich noch nicht besonders mit Heizungen beschäftigt. Ich wusste nur Fußbodenheizung ist eine Voraussetzung für eine Wärmepumpe (ok, sagt man heute nicht mehr). Das Haus sollte von einem Zweifamilienhaus zu einem Einfamilienhaus umgebaut werden und somit aus zwei Gasthermen nur noch eine gemacht werden. Wahrscheinlich waren es zwei 14kW Thermen die dann zu einer 24kW Therme zusammengelegt wurden - Warmwasser lief da noch über Stromdurchlauferhitzer.
                    Aber leider habe ich damals keinen Einspruch gemacht - ich dachte, die wissen was die machen. Nach weiteren Wechseln der Heizung weiß ich nun, das machen alle so, wenn man nicht genau sagt was man will.

                    Als die Anlage eingebaut wurde gab es aber noch ein Dachgeschoss mit Heizkörpern. Und genau das macht die Sache kompiziert. Waren die Heizkörper nicht zugedreht, gab es das Problem nicht. Weil das Dachgeschoss umgebaut wird, gibt es diese Heizkörper aber nicht mehr. In Zukunft soll dort ebenfalls eine Fußbodenheizung installiert werden. Da liegt auch meine Hoffnung, dass dann mehr Wärme abgenommen werden kann und das Problem dadurch verschwindet.

                    Und ja, es gibt ein Hydraulikproblem. Ich möchte aber keine Böden wieder aufreißen, wenn ich das Problem auch anders lösen kann. Pufferspeicher kenne ich nur von Wärmepumpen. Der zweite Heizungsbauer ist der Meinung, dass die Pumpe zu schwach ist. Dabei ging es ja mit weiteren Heizkörpern. Warum sollte weniger dann ein Problem sein.

                    Nach dem Einbauer der Heizung sind ja meine Einstellung Schuld an diesem Verhalten. Die Heizkurve ist auf 36°C bei NAT -8° eingestellt. EMS-ESP läuft zwar, aber ich habe keinen Fernzugang aktuell. Beim Test mit einer anderen EMS Heizung habe ich allerdings dort merkwürdige Angaben gelesen, sowas wie 20 und 90 bei tatsächlich eingestellten 46°C bei NAT -8°C.

                    Interessant ist ja auch, dass das Problem aktuell nicht auftritt, weil es eben kalt genug draußen ist. Solange draußen die Temperaturen unter 5°C sind läuft alles super. Ich könnte bestimmt auch die Heizkurve anheben, um das Problem zu beseitigen, aber das würde höhere Heizkosten (wahrscheinlich) bedeuten.

                    Die Heizung ist im Haus meiner Tochter eingebaut und ich bin dort nicht dauerhaft. Es ist also nicht so wie Zuhause wo man 24 Stunden die Heizung überwachen kann. Fußbodenheizung ist dazu noch super schwerfällig im Gegensatz zu Heizköpern.

                    Aber das hier ist alles Heizungsforum-Kram und kein EMS-ESP Problem.

                    LG Thomas

                    1 Antwort Letzte Antwort
                    0
                    • wendy2702W Online
                      wendy2702W Online
                      wendy2702
                      schrieb am zuletzt editiert von
                      #731

                      Hi,

                      Kann mir jemand sagen ob man diese Kombi mit dem Adapter auslesen und eventuell auch steuern kann?

                      Inneneinheit: Compress CS6800i AW
                      Außeneinheit: AW 7 OR-S

                      Bitte keine Fragen per PN, die gehören ins Forum!

                      Benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.

                      vowillV Pedder007P 2 Antworten Letzte Antwort
                      0
                      • wendy2702W wendy2702

                        Hi,

                        Kann mir jemand sagen ob man diese Kombi mit dem Adapter auslesen und eventuell auch steuern kann?

                        Inneneinheit: Compress CS6800i AW
                        Außeneinheit: AW 7 OR-S

                        vowillV Offline
                        vowillV Offline
                        vowill
                        schrieb am zuletzt editiert von
                        #732

                        @wendy2702 Hallo wendy, ja, das sollte sehr gut gehen. Ich habe eine CS6800i mit der größeren Wärmepumpe AW12 OR, und das geht prima.

                        ioBroker im lxc auf NUC / Aktive Instanzen: 38

                        1 Antwort Letzte Antwort
                        0
                        • Stephan BuhreS Offline
                          Stephan BuhreS Offline
                          Stephan Buhre
                          schrieb am zuletzt editiert von
                          #733

                          Hallo,
                          ich habe eine Buderus GB192-50 mit dem KM200-Adapter. Daten kann ich über das API auslesen.
                          Die Anlage schaltet automatisch basierend auf der Außentemperatur zwischen Sommer und Winterbetrieb um.
                          Welcher Datenpunkt zeigt mir an, ob sie sich im Sommer- bzw. Winterbetrieb befindet?
                          Gruß
                          Stephan

                          T 1 Antwort Letzte Antwort
                          0
                          • wendy2702W wendy2702

                            Hi,

                            Kann mir jemand sagen ob man diese Kombi mit dem Adapter auslesen und eventuell auch steuern kann?

                            Inneneinheit: Compress CS6800i AW
                            Außeneinheit: AW 7 OR-S

                            Pedder007P Offline
                            Pedder007P Offline
                            Pedder007
                            schrieb am zuletzt editiert von
                            #734

                            @wendy2702 Hi, Bosch CS 7800i Compress (Geothermie) geht auch wunderbar :-)

                            Pedder
                            All @Proxmox/Bookworm auf HP Elitedesk 800 G4; Zigbee: ZigStar (LAN), ~110Devices
                            Unifi, Motioneye/3Reolinks, PiHole, Bosch 7800i via BBQKees/EMS-ESP, Fronius/BYD 11kWp via Modbus

                            1 Antwort Letzte Antwort
                            0
                            • kep42010K Offline
                              kep42010K Offline
                              kep42010
                              schrieb am zuletzt editiert von
                              #735

                              Hi,
                              ich bin noch totaler Anfänger mit dem EMS-ESP und habe so einige Schwierigkeiten. Hoffe ihr könnt mir ein paar grundsätzliche Infos geben.

                              Zur Situation:
                              Buderus WLW 186i mit MX400 und BBQKees Gateway S3. Das Gateway ist eingebunden und ich kann hier Werte lesen sowie schreiben (z.B. an der Heizkurve getestet). Auf dem Raspi mit ioBroker kann ich aber nur lesen (wenn ich hier Objekte editiere wird dies nicht übernommen). Ist es generell möglich Objekte hier direkt zu setzten oder muss ich mqtt nutzten?

                              Der nur Lese-Modus ist deaktiviert und der Zugriffstoken scheint zu passen, da ich Werte ja lesen kann.

                              Bin für alle Tipps danbar.

                              T 1 Antwort Letzte Antwort
                              0
                              • Stephan BuhreS Stephan Buhre

                                Hallo,
                                ich habe eine Buderus GB192-50 mit dem KM200-Adapter. Daten kann ich über das API auslesen.
                                Die Anlage schaltet automatisch basierend auf der Außentemperatur zwischen Sommer und Winterbetrieb um.
                                Welcher Datenpunkt zeigt mir an, ob sie sich im Sommer- bzw. Winterbetrieb befindet?
                                Gruß
                                Stephan

                                T Offline
                                T Offline
                                tp1de
                                schrieb am zuletzt editiert von
                                #736

                                @stephan-buhre Bei mir der Datenpunkt ems-esp.0.heatingCircuits.hc1.suWiSwitchMode ... analog für hc2 etc.
                                "states": {
                                "0": "off",
                                "1": "automatic",
                                "2": "forced"
                                },

                                "0" = Sommerbetrieb, "2"= Winterbetrieb beides permanent
                                Bei "1" automatische Umschaltung kannst du nicht erkennen, wann Sommer- oder Winterbetrieb

                                Stephan BuhreS 1 Antwort Letzte Antwort
                                0
                                • kep42010K kep42010

                                  Hi,
                                  ich bin noch totaler Anfänger mit dem EMS-ESP und habe so einige Schwierigkeiten. Hoffe ihr könnt mir ein paar grundsätzliche Infos geben.

                                  Zur Situation:
                                  Buderus WLW 186i mit MX400 und BBQKees Gateway S3. Das Gateway ist eingebunden und ich kann hier Werte lesen sowie schreiben (z.B. an der Heizkurve getestet). Auf dem Raspi mit ioBroker kann ich aber nur lesen (wenn ich hier Objekte editiere wird dies nicht übernommen). Ist es generell möglich Objekte hier direkt zu setzten oder muss ich mqtt nutzten?

                                  Der nur Lese-Modus ist deaktiviert und der Zugriffstoken scheint zu passen, da ich Werte ja lesen kann.

                                  Bin für alle Tipps danbar.

                                  T Offline
                                  T Offline
                                  tp1de
                                  schrieb am zuletzt editiert von
                                  #737

                                  @kep42010 Zum Lesen brauchst du den Token nicht - nur zum Schreiben.
                                  Zeigt die Logdatei denn Schreibfehler an?

                                  kep42010K 1 Antwort Letzte Antwort
                                  0
                                  • T tp1de

                                    @kep42010 Zum Lesen brauchst du den Token nicht - nur zum Schreiben.
                                    Zeigt die Logdatei denn Schreibfehler an?

                                    kep42010K Offline
                                    kep42010K Offline
                                    kep42010
                                    schrieb am zuletzt editiert von
                                    #738

                                    @tp1de Hi, nein es gibt keine Meldungen im Log. Tatsächlich kam gar keine Meldung als ich Werte setzen wollte.
                                    Ich glaub ich hab den Fehler nun aber gefunden. Ich habe immer den Haken bei "Bestätigt" gesetzt. Wenn ich das nicht mache schreibt er nun und protokolliert das jetzt auch.

                                    Danke für den Anstoß mit dem Log :+1:

                                    T 1 Antwort Letzte Antwort
                                    0
                                    • kep42010K kep42010

                                      @tp1de Hi, nein es gibt keine Meldungen im Log. Tatsächlich kam gar keine Meldung als ich Werte setzen wollte.
                                      Ich glaub ich hab den Fehler nun aber gefunden. Ich habe immer den Haken bei "Bestätigt" gesetzt. Wenn ich das nicht mache schreibt er nun und protokolliert das jetzt auch.

                                      Danke für den Anstoß mit dem Log :+1:

                                      T Offline
                                      T Offline
                                      tp1de
                                      schrieb am zuletzt editiert von
                                      #739

                                      @kep42010 Das ist eine generelle Regel.
                                      Bei allen ioBroker Adaptern werden Änderungen nur fortgeschrieben, wenn die "unbestätigt" sind.

                                      1 Antwort Letzte Antwort
                                      0
                                      • T tp1de

                                        @stephan-buhre Bei mir der Datenpunkt ems-esp.0.heatingCircuits.hc1.suWiSwitchMode ... analog für hc2 etc.
                                        "states": {
                                        "0": "off",
                                        "1": "automatic",
                                        "2": "forced"
                                        },

                                        "0" = Sommerbetrieb, "2"= Winterbetrieb beides permanent
                                        Bei "1" automatische Umschaltung kannst du nicht erkennen, wann Sommer- oder Winterbetrieb

                                        Stephan BuhreS Offline
                                        Stephan BuhreS Offline
                                        Stephan Buhre
                                        schrieb am zuletzt editiert von
                                        #740

                                        @tp1de das ist ja ärgerlich...

                                        1 Antwort Letzte Antwort
                                        0
                                        • D Online
                                          D Online
                                          docsnyder7
                                          schrieb am zuletzt editiert von
                                          #741

                                          Guten Tag... eine Frage zum Adapter, kann der auch ohne SQL oder sonstige DB´s genutzt werden... würde ersteinmal alles ohne irgendwelche Statistiken ans Laufen bekommen wollen.

                                          Merlin123M 1 Antwort Letzte Antwort
                                          0
                                          Antworten
                                          • In einem neuen Thema antworten
                                          Anmelden zum Antworten
                                          • Älteste zuerst
                                          • Neuste zuerst
                                          • Meiste Stimmen


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          967

                                          Online

                                          32.4k

                                          Benutzer

                                          81.5k

                                          Themen

                                          1.3m

                                          Beiträge
                                          Community
                                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                          ioBroker Community 2014-2025
                                          logo
                                          • Anmelden

                                          • Du hast noch kein Konto? Registrieren

                                          • Anmelden oder registrieren, um zu suchen
                                          • Erster Beitrag
                                            Letzter Beitrag
                                          0
                                          • Home
                                          • Aktuell
                                          • Tags
                                          • Ungelesen 0
                                          • Kategorien
                                          • Unreplied
                                          • Beliebt
                                          • GitHub
                                          • Docu
                                          • Hilfe