// Integrierte Heizungsthermostatsteuerung // Autor: Looxer01 ------------------------- // Version 0.7 12.04.2017 - Initialversion // Erweiterte Version 2.0 28.12.2017 // Autoren: Looxer01 - Apollon77 (Triggerkonzept) // // // Erweiterte Funktionen im Vergleich zu 1.0 //- ExcludeHMSensors eingebaut - Damit lassen sich einzelne Sensoren ausschliessen ohne das Gewerk zu Aendern //- wenn man "cron=0" setzt läuft das ganze Ding über Trigger. Vom Skript selbst erzeugte State-Changes werden aussortiert. Debug Log zeigt was passiert //- Es werden für die eingestellten Planzeiten Schedules gesetzt, sodass Änderungen immer zum korrekten Zeitpunkt kommlog("subscribeIdList "+ subscribeIdList);suben. Das geht auch crongesteuert //- Es gibt einen neuen Datenpunkt "Source_NextTemp" der die nächste Temperatur enthält vom nächsten Schedule-Punkt. Noch nicht vollkommen korrekt weil Feuertage/Urlaub blöd sind. Muss ich nochmal ran. Aber im Notfall würde mir das reichen. //- Es ist jetzt möglich eine manuell gesetzte Solltemperatur zum nächsten Schedulewechsel zurückzusetzen (statt Ablaufzeit in Minuten). Ablaufzeit in Minuten ist weiterhin möglich. Auch die Verhinderung von manuellen Temps ist möglich //- viele Optimierungen und Bugfixes //- Source_last_Program_Run Datenpunkt auch pro Raum angelegt, generelles Source_last_Program_Run wird immer geschrieben wenn etwas abgearbeitet wird //- Es wird ein neuer View zur Verfügung gestellt. Der bisherige View kann aber auch weiterhin genutzt werden. (Austausch des Datenpunktes Source_last_Program_Run auf Raumebenen ist empfehlenswert ) // // Das Programm dient zur Steuerung von Heizungsthermostaten. (siehe Funktionsliste in der Doku) // Es synchronisiert alle Thermostate eines Raumes - aehnlich zur Gruppenfunktion der Homematic // Direktverknüpfungen bzw Gruppen werden unterstützt. Letztendlich funktionieren die Thermostate aber auch ohne DV und Gruppen, so als wären sie mit DV bzw Gruppen gesteuert // Empfehlung: Modus des thermostates auf MANU setzen. "AUTO" ist zwar auchmoeglich, führt aber zur Erkennung einer manuellen Temperatur // // Ein Thermostat/Sensor darf nicht mehreren Raeumen zugeordnet sein // Alle Sensoren und Thermostate muessen je einem Gewerke zugeordnet sein. Dieses Gewerk wird vom Heizungsscript ausgewertet // Hinweis: bei aelteren ioBroker Installationen sind u.U. Raumdefinitionen in ioBroker inkonsistent was zu Fehlern im Programm fuehrt (siehe ioBroker Admin Tab: Aufzaehlungen) // Falls das auftritt muessen die enum.rooms mit den Raeumen der CCU verglichen worden. Falsche Definitionen muessen aus iobroker manuell geloescht werden - Restart Adapter REGA erforderlich // Falls es keine eigenen Definition in ioBroker gibt koennen auch einfach die enums gelöscht und durch REGA Neusynch neu angelegt werden // Fuer nicht HM-Geräte: Die angegebenen Räume muessen in enums (Aufzählungen) von ioBroker vorhanden sein -bzw. können dort angelegt werden // // Vorbereitungen // die relevanten Thermostate und Sensoren muessen dem angegebenen Gewerk zugeordnet sein // Checken, dass alle Raeume bei den Aufzaehlungen mit der Raumliste der CCU uebereinstimmen // Die Views muessen angelegt werden. Hierzu sind die folgenden Schritte notwendig // a. Ersetzen der folgenden Variablen aus dem mitgelieferten View. Das muss je Raum und Profil wiederholt werden // - Ersetze Schlafzimmer mit den eigenen jeweiligen Raumnamen entsprechend der eigenen Raumliste - Achtung Raumnamen duerfen keine Blanks enthalten - Blanks muessen mit "_" aufgefuellt werden // - Ersetze Profil-1 durch Profil-2 etc je anzulegendes Profil // b. Importieren der so geaenderten views in VIS (ueber VIS Funktionen) // c. Ordne die richtigen Datenpunkte fuer Solltemperatur und Isttemperatur zu // ACHTUNG: Im View muss das Raumprofil aktiviert werden. Es ist anfangs nicht aktiviert damit nicht alle Raeume mit dem Standardprofil beim ersten Programmlauf aktiviert sind. Daher manuelle notwendig // Import des Views geschicht über VIS - VIEWS - View imporiteren. Hier öffnet sich ein Fenster. Das Coding des Views muss dort hineinkopiert werden. // // Bitte die Dokumentation lesen - alle Konfigschritte und auch die Funktionsweise des Programmes sind dort erlaeutert // // ab hier ChangeLog // Aktuelle Version 2.0b01 (erste Beta) 28.12.2017 // Version 2.00b02 05.01.2018 - zweite Beta //.............................Technische Coding Aenderungen (ueberfluessige log eintragungen und doppel coding entfernt) //.............................Kein Trigger bei Aenderung von An/Abwesenheit und Feiertagen gefixt //.............................Bei Einstellung der Duration Manuelle Temp kleiner Null wurde bei einer Thermostataenderung am Thermostat keine Rückstellung auf schedule vorgenommen //.............................Delay Time (notwendig für alte Thermostate) nach Fensteröffnung wieder aktiviert - 2 Minuten Verzögerung nach Fensterschliessung //.............................Sensorstatusermittlung fuer HM-Geraete verallgemeinert (keine Speziallogik mehr notwendig. Konfig in der Sensortypetab reicht aus) / logging Eintraege fuer Sensor Aenderungen hinzugefuegt //.............................Bei Einschalten der Heizperiode wurden die Temperaturen nicht sofort auf die geplanten Temperaturen gesetzt // Version 2.00b03 02.04.2018 - dritte Beta //.............................Manuelle Temperaturen werden bei Scriptstart ignoriert/zurückgesetzt //.............................Thermostabtypetab Position 4 auf Position 8 (nach den Wandthermosteten) verschoben //.............................NoneHMTab - Fuellen der Position 12 in Controltab falsch (mit 0 ersetzt) //.............................Bei gleichen Zeiten im schedule von verschiedenen Räumen kam es dazu, dass nicht geschaltet wurde. Eine Zeitverzögerung eingebaut //.............................externe Dateiausgabe bei manuellen Aenderungen hinzugefügt (writelog) //.............................Fehler in Routine Sensor Change bei direktvernuepften Fenstersensoren beseitigt. //.............................Fehler bei den Subscriptions fuer Feiertage fuehrte zu Warnmeldungen, wenn kein Feiertagsadapter genutzt wurde // Version 2.00a01 08.10.2018 - erste Alpha //.............................Fehler bei nicht direkten Sensoren behoben: Temperatur wurde nicht abgesenkt //.............................Raum-Statusanzeigen (Abweichungen vom Heizplan) wurde überarbeitet - keine Datenstrukturanpassung nötig- zentrale Routine eingefügt //.............................Routine zur Überprüfung direktverknüpfter Sensoren nicht notwendig - entfernt //.............................Voreinstellungen Parameter für Verschlusssensoren angepasst //.............................Raumstatus (geoeffnet oder geschlossen ) Datenpunkt je Raum eingerichtet und Logik zum Raumstatus update fuer alle Sensoren des raumes abgebildet //.............................Sensor aufgenommen: HMW-IO-12-Sw14-DR', 'Schließerkontakt HMW' , wired // Version 2.00a02 14.10.2018 - zweite Alpha //.............................Fehler bei nicht direkten Sensoren behoben: Temperatur wurde nicht abgesenkt / Code wieder aktiviert // Version 2.00 26.10.2018 - erste Stable //.............................bei direkt verknuepften Sensoren wurde bei geoeffnetem Fenster ein moeglicher Schedule Wechsel nicht berüksichtig //Version 2.10 13.12.2018......Fehler behoben nach updae auf JS 4.0.1 !fs.existsSync funktioniert nicht mehr ausgetausch mit fs.readFile //.............................WT HmiP-BWTH hinzugefügt //.............................Subscription fuer ICAL Profile hinzugefügt //.............................Delay für HM Geräte gefixt und für Nicht-HM Geräte hinzugefügt //.............................ICAL Pfad umgestellt für ICAL Adapter Version 1.7.0 //.............................ICAL- Profile mit Selection Prio für die Profil-Selektion hizugefügt - unterstützt jetzt mehrere gleichzeitig aktive ICAL Events //.............................Subscription für ICAL-Raumprofile und Selektion hinzugefügt (analog zu den globalen Profilen) //.............................nach update auf JS 4.0.2 !fs.existsSync wieder aktiviert //.............................SoftBoost Funktion hinzugefügt (analog dem HardBoost der Thermostate) //.............................ICAL Events können jetzt in den Views aktiviert/deaktiviert werden //.............................Manuelle Aenderungen stabilisiert //Version 2.1.01 18.12.........Testraum erzeugte Fehler - Entfernt //.............................Synchen bei nicht direkt verknüpften Thermostaten wieder hergestellt (bei Absenktemperatur) //Version 2.2..................Funktion: AbsenkTemp als absolute Temperatur einstellbar //.............................Funktion: UserExit finalisiert //.............................Funktion: Manuelle Temperaturen und SoftBoost gehen nach Programmstop nicht mehr verloren //.............................Bug: Manuelle Temperaturen: Korrekturen im View mehrfach möglich //.............................Technisch: Trigger Konzept auf Thermostate erweitert (keine unnötige Programmausführungen mehr) //.............................Funktion: "Exclude Sensoren" kann jetzt auch fuer Thermostate verwendet werden //.............................Funktion: Heizungsgruppen können jetzt direkt erkannt und gesteuert werden //.............................Bug: Syncing von nicht direkt verknuepften Thermostaten funktioniert wieder //.............................Funktion: States werden generell noch nur upgedated, wenn tatsächlich Änderungen vorliegen. Vermeidet unnoetige Triggeraufrufe //.............................Bug: Fehler bei Determine Schedule wenn heute Feiertag ist beseitigt //.............................Funktion: Abwesenheit je Raum (Absenkung fuer einzelne Räume bei Abwesenheit zugeordneter Namen) //.............................Bug: Subscription für raumbezogene ICAL Events korrigiert //.............................Bug: Subsription global werden jetzt immer gemacht auch wenn ical ausgeschaltet ist, da im View eingeschaltet werden kann //.............................Funktion: IP Fenstersensor HmIP-SWDM-B2 hinzugefügt //.............................Technisch: Editor Fehlermeldungen beseitigt //.............................Bug: Warnmeldung bei Abspeicherung von NextSollTemp beseitigt //.............................Funktion: Daenpunkt kann jetzt für NoneHM Thermostate und NoneHM Sensoren ohne split in 3 Teilstrings eingegeben werden. (leerstring 2 und 3 sind ebenfalls zulaessig) //.............................Funktion: Thermostat HmIP-eTRV-B1 in Thermostattabelle aufgenommen //.............................Bug: Reihenfolge overrule Tab geändert. es wird zuerst Urlaub_Abwesend geprüft und dann erst Abwesend //.............................Technisch: Programmablauflogik aufgeräumt //.............................Bug: beim Synchronisieren der Thermostate wurde immer nur das auslösende Gerät gecheckt ob es sich im manuellen Modus befindet. jetzt werden alle Geräte des Raumes überprüft // //------------------------------------------------------------------------------ // Beginn Generelle Einstellungen // werden an dieser Stelle benoetigt - Einstellungen sind nur in Spezialfaellen notwendig //------------------------------------------------------------------------------ // Anpassung nur wenn unbedingt notwendig. Hier ist der Ansatz um z.B. Einliegerwohnungen separat zu steuern var JSPath = "javascript.0."; // JS- Pfad var path = JSPath + 'Heizung.Heizplan'; // Pfad fuer create states var Gparameterpath = path + ".GlobaleParameter"; // Pfad in die Globalen Parameter var ICALPath = "ical.0.events.0.now"; // Pfad zu den ICAL events zur Profilauswahl // ab ICAL Adapter version 1.7.0 // var ICALPath = "ical.0.events"; // Pfad zu den ICAL events zur Profilauswahl // bis ICAL Version kleiner als 1.7.0 //------------------------------------------------------------------------------ // Ende Generelle Einstellungen // Usereinstellungen sind Einstellungen, die ueblicherweise gemacht werden //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Beginn USER Einstellungen //------------------------------------------------------------------------------ // Gewerke - wichtige Einstellung, da nur die Geraete eingelesen werden, die im Gewerk vorhanden sind // Das Gewerk muss alle Thermostate bzw Sensoren enhalten var HeizungGewerk = "Heizung"; // diesem Gewerk muessen alle Thermostate zugeordnet sein. var SensorGewerk = "Verschluss"; // diesem Gewerk muessen alle Verschlusssensoren zugeordnet sein. // Alle x Minuten wird gecheckt ob die SollTemp angepasst werden muss - Empfehlung wenn cron dann 5 // Wenn Wert = 0 ist dann läuft das Skript über Events (empfohlener Weg) var cron = 0; // Raumliste - empfohlen zu benutzen fuer kleine Systeme // UseRoomList heisst, dass die nur hier gelisteten Raeume angelegt und abgearbeitet werden - somit werden nicht sofort alle Datenpunkte aller Räume angelegt (ca. 100 pro Raum und Profil) // dies dient hauptsaechlich zur Anlage der Daten - So koennen Raum fuer Raum alle States angelegt wrden // Das sollte genutzt werden mit langsamen Rechnern wie Raspi mit SD karte var UseRoomList = false; // Wenn testmodus werden nur die Angegebenen Raeume abgearbeitet var RoomList = []; RoomList[0] = ['Teresa']; // Liste der Raeume die gesteuert werden soll zum Testen RoomList[1] = ['Flur']; RoomList[2] = ['Jana']; RoomList[3] = ['Hobbyraum']; RoomList[4] = ['Elternbad']; RoomList[5] = ['Schlafzimmer']; RoomList[6] = ['Gaeste WC']; RoomList[7] = ['Kueche']; RoomList[8] = ['Wohnzimmer']; RoomList[9] = ['Raum9']; // Liste der Auszuschliessenden Homematic Sensoren // Falls ein Raum Sensoren hat die zu "Verschluss" gehören aber pot. nichts mit einem Öffnungszustand zu tun ChckAbsenkung // kann man diese ausschliessen var ExcludeHMSensors = []; //ExcludeHMSensors[0] = 'hm-rpc.1.000A97099CEAD0.1.SET_POINT_TEMPERATURE'; // Liste des STATE-Datenpunkts (z.B. hm-rpc.0.NEQXXXXX.1.STATE) von Sensoren die nicht beachtet werden sollen ExcludeHMSensors[1] = ''; // Anzahl der Profile. i.d.R. sollten maximal 3 Profile genuegen - Profile werden z.B. fuer Events aus ICAL verwendet var MaxProfile = 2; // Maximal genutzte Profile pro Raum (gering halten ) Zahl zwischen 1 und 9 // Das ist die Temperatur, die eingestellt wird, wenn erkannt wird, dass ein Verschluss eines Raumes geoeffnet ist (z.B bei nicht direktverknuepften Geraeten) var VerschlussAbsenkungsGrenze = 12; // Soft-Boost Temperatur zum kurzfristigen Aufheizen eines Raumes - Sollte nicht hoeher sein als die High-Temp der Thermostate var SoftBoostTemp = 30; // erweitertetes Logging im ioBroker log bei true var debug = false; // Logging in externe Datei - Achtung der Pfad muss fuer MS-Windows bzw IOS angepasst werden var LogFlag = true; // logging enabled var LogPath = "/opt/iobroker/iobroker-data/HeizungsthermostatLOG.csv"; // Pfad und Dateiname des externen Logs var OnlyChanges = false; // bei true wird nur geloggt wennn eine neue Solltemperatur geschrieben wird // ICAL Einstellungen // ICAL kann in den Views aktiviert werden - zuerst muss aber ICAL konfiguriert werden (Adapter ICAL Events) diehe dazu FAQs // die Events muessen entsprechend in ICAL angelegt werden, sonst gibt es Warnmeldungen im Log // Die Eventnamen koennen angepasst werden. Bitte die Logkik von ICAL unbeding beachten. (siehe Doku im Kapitel ICAL) var EventG_UrlaubAbwesend = "Urlaub_Abwesend"; // dieses Event muss in ICAL angelegt werden wenn ICAL genutzt wird var EventG_UrlaubAnwesend = "Urlaub_Anwesend"; // dieses Event muss in ICAL angelegt werden wenn ICAL genutzt wird var EventG_Party = "Party"; // dieses Event muss in ICAL angelegt werden wenn ICAL genutzt wird var EventG_Gaeste = "Gaeste"; // dieses Event muss in ICAL angelegt werden wenn ICAL genutzt wird var EventG_Abwesend = "Keiner_DA"; // dieses Event muss in ICAL angelegt werden wenn ICAL genutzt wird var EventG_Feiertag = "Feiertag"; // dieses Event muss in ICAL angelegt werden wenn ICAL genutzt wird // Die folgenden EVENT Texte muessen in ICAL angelegt werden. Sobald die Texte im google Kalender // aktiv sind wird das Event fuer die Heizungsthermostatsteuerung ausgewertet. // Achtung die Zeichen <> und der Text innerhalb dieser Klammer duerfen nicht geaendert werden - // siehe Dokumentation fuer mehr infos // folgende Beispiele, die in den ICAL Events des ICAL Adapters angelegt werden muessen // Globale Events // - Urlaub_Anwesend // - Party // Globale Profil Events (fuer alle Räume gültig) // - Profil_1 // - Profil_2 // Raumprofil Events (fuer einzelne Räume gültige Events) // -Schlafzimmer_1 // - Schlafzimmer_2 // - Kinderzimmer_1 var UseEventG_Profil = "Profil_"; // Events mit denen das Profil umgeschaltet werden kann - muss in ICAL angelegt werden wenn UseEventP_Profil = true ist var UseEventR_Profil = "_"; // Events mit denen das Raumprofil umgeschaltet werden kann - muss in ICAL angelegt werden wenn mit Eents gearbeitet wird // Integration zur Anwesenheitsermittlung - // Globale Anwesenheitserkennung ? (Absenkung für alle Raeume) var UseAnwesenheitserkennung = true; // wenn true, dann wird die o.g. Anwesenheitsvariable genutzt - Empfehlung erst im zweiten Schritt aktivieren var StateAnwesenheitFunction = JSPath + "Anwesenheitssteuerung.Userlist.JemandDa"; // Wenn UseAnwesenheitserkennung = true, dann muss der Pfad angepasst werden // Soll eine raumbezogene Anwesenheitssteuerung erfolgen ? (durch Nutzer und Raumzuordnung) - Dazu muss die globale Anwesenheitskernnung auf true stehen var UseRaumAnwesenheit = true; var RaumAnwTAB = []; // 0.Raum 1.Pfad RaumAnwTAB[0] = ['Wohnzimmer', 'tr-064.0.devices.Pascals-iPhone.active' ]; RaumAnwTAB[1] = ['Wohnzimmer', 'tr-064.0.devices.Janniks-Phone.active' ]; RaumAnwTAB[2] = ['Schlafzimmer', 'tr-064.0.devices.Pascals-iPhone.active' ]; RaumAnwTAB[3] = ['Kueche' , 'initial' ]; // Integration zum Feiertagskalender - var UseFeiertagskalender = true; // wenn der Kalender genutzt wird bitte auf true setzen - Empfehlung: Feiertagsadapter installieren und auf true setzen var StateFeiertagHeuteAdapter = "feiertage.0.heute.boolean"; // wenn UseFeiertagskalender, dann wird dieser Pfad verwendet var StateFeiertagMorgenAdapter = "feiertage.0.morgen.boolean"; // wenn UseFeiertagskalender, dann wird dieser Pfad verwendet wenn es darum geht den nächsten Schaltpunkt zu ermitteln wenn dieser am nächsten Tag liegt //------------------------------------------------------------------------------ // Ende USER Einstellungen // Usereinstellungen sind Einstellungen, die ueblicherweise gemacht werden //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Beginn Experteneinstellungen // Experteneinstellungen sollten nur geamcht werden, wenn die Logik des Programmes bekannt ist //------------------------------------------------------------------------------ // Pfad zum globalen Anwesenheitsflag der Hz-Steuerung - wird parallel zum Adapter gehalten var StateAnwesenheit = JSPath + "Heizung.Heizplan.GlobaleParameter.Anwesenheit"; // Pfad zum Feiertagskennzeichen der Hz-Steuerung - wird parallel zum Adapter gehalten var StateFeiertagHeute = JSPath +"Heizung.Heizplan.GlobaleParameter.Feiertag_Heute"; // die States sollten moeglichst so belassen werden - das Programm laesst aber Aenderungen zu var StatePartyjetzt = Gparameterpath + ".Partyjetzt"; // ID Party Jetzt flag var StateGaesteDa = Gparameterpath + ".GaesteDa"; // ID Gaeste da flag var StateUrlaubAnwesend = Gparameterpath + ".Urlaub_Anwesend"; // Wenn kein Arbeitstag, dann wird der Tag wie ein Sonntag behandelt var StateUrlaubAbwesenheit = Gparameterpath + ".Urlaub_Abwesend"; // Temperaturabsenkung wenn laengerer Urlaub eingetragen ist var StateHeizperiode = Gparameterpath + ".Heizperiode"; // Wenn Heizperiode false werden alle Ventile geschlossen // Die ThermostatTypeTab definiert die Thermostat Typen. // Achtung zentrale Steuerungen muessen immer zuerst eingetragen sein. // Steuerung zentral heisst, dass dieses Geraet evt abhaengige Geraete steuert, wenn false, dann werden abhaengige Geraete gleich behandelt // Wenn mit Direktverbindungen gearbeitet wird dann MUSS zentrale Steuerung auf true stehen var ThermostatTypeTab = []; // 0.frei 1.GeraeteType 2. Beschreibung, 3. Type 4.DP-SollTemp 5.nicht verwendet ID 6.DP MANU/AUTO Schaltung 7.Steuerung DV 8. IstTemp 9-Check-MANU-Mode 10-Ventilstellung wenn nicht Heizperiode 11. Delay nach Verschluss zu ThermostatTypeTab[0] = ['frei', 'HM-CC-VG-1' , 'Heizungsgruppe' ,'VD', '1.SET_TEMPERATURE' , 'frei', '1.MANU_MODE', true, '1.ACTUAL_TEMPERATURE', '1.CONTROL_MODE', 12, 0]; ThermostatTypeTab[1] = ['frei', 'HM-TC-IT-WM-W-EU', 'Wandthermostat (neu)' ,'WT', '2.SET_TEMPERATURE' , 'frei', '2.MANU_MODE', true, '1.TEMPERATURE', '2.CONTROL_MODE', 12, 0]; ThermostatTypeTab[2] = ['frei', 'HM-CC-TC' , 'Wandthermostat (alt)' ,'WT', '2.SETPOINT' , 'frei', false, false, '1.TEMPERATURE', false, 12, 0]; ThermostatTypeTab[3] = ['frei', 'HM-CC-RT-DN' , 'Heizkoerperthermostat(neu)' ,'HT', '4.SET_TEMPERATURE' , 'frei', '4.MANU_MODE', true, '4.ACTUAL_TEMPERATURE', '4.CONTROL_MODE', 12, 0]; ThermostatTypeTab[4] = ['frei', 'HmIP-eTRV' , 'Heizkoerperthermostat(HMIP)','IPHT', '1.SET_POINT_TEMPERATURE', 'frei', '1.CONTROL_MODE', false, '1.ACTUAL_TEMPERATURE', '1.CONTROL_MODE', 12, 0]; ThermostatTypeTab[5] = ['frei', 'HmIP-WTH' , 'Wandthermostat(HMIP)' ,'IPWT', '1.SET_POINT_TEMPERATURE', 'frei', '1.CONTROL_MODE', true, '1.ACTUAL_TEMPERATURE', '1.CONTROL_MODE', 12, 0]; ThermostatTypeTab[6] = ['frei', 'HmIP-WTH-2' , 'Wandthermostat(HMIP)' ,'IPWT', '1.SET_POINT_TEMPERATURE', 'frei', '1.CONTROL_MODE', false, '1.ACTUAL_TEMPERATURE', '1.CONTROL_MODE', 12, 0]; ThermostatTypeTab[7] = ['frei', 'HmIP-STH' , 'Wandthermostat(HMIP)' ,'IPWT', '1.SET_POINT_TEMPERATURE', 'frei', '1.CONTROL_MODE', true, '1.ACTUAL_TEMPERATURE', '1.CONTROL_MODE', 12, 0]; ThermostatTypeTab[8] = ['frei', 'HmIP-STHD' , 'Wandthermostat(HMIP)' ,'IPWT', '1.SET_POINT_TEMPERATURE', 'frei', '1.CONTROL_MODE', true, '1.ACTUAL_TEMPERATURE', '1.CONTROL_MODE', 12, 0]; ThermostatTypeTab[9] = ['frei', 'HmIP-eTRV-2' , 'Heizkoerperthermostat(HMIP)','IPHT', '1.SET_POINT_TEMPERATURE', 'frei', '1.CONTROL_MODE', false, '1.ACTUAL_TEMPERATURE', '1.CONTROL_MODE', 12, 0]; ThermostatTypeTab[10] = ['frei', 'HmIP-eTRV-B' , 'Heizkoerperthermostat(HMIP)','IPHT', '1.SET_POINT_TEMPERATURE', 'frei', '1.SET_POINT_MODE', true, '1.ACTuAL_TEMPERATURE', '1.SET_POINT_MODE', 12, 0]; ThermostatTypeTab[11] = ['frei', 'HmiP-BWTH' , 'Heizkoerperthermostat(HMIP)','IPWT', '1.SET_POINT_TEMPERATURE', 'frei', '1.CONTROL_MODE', false, '1.ACTUAL_TEMPERATURE', '1.CONTROL_MODE', 12, 0]; ThermostatTypeTab[12] = ['frei', 'HmIP-eTRV-B1' , 'Heizkoerperthermostat(HMIP)','IPHT', '1.SET_POINT_TEMPERATURE', 'frei', '1.SET_POINT_MODE', true, '1.ACTUAL_TEMPERATURE', '1.SET_POINT_MODE', 12, 0]; // Tabelle fuer Nicht HM Thermostate - Details finden sich in der Dokumentation // wurde zum Testen verwendet, da auch virutelle Thermostate verwaltet werden koennen. // Wenn nicht HM Geraete korrekt in ioBroker angebunden sind sollten diese auch ueber die Tabelle ThermostatTypeTab konfigurierbar sein //Spalte 1 = Raumname wie in der CCU hinterlegt //Spalte 2 = Erster Teil des Datenpunktpfades mit Instance wie z.B. "hm-rpc.0" //Spalte 3 = Zweiter Teil des Datenpunktpfades mit der ID des Geraetes z.B. "MEQ0183268" //Spalte 4 = Dritter Teil des Datenpunktpfades mit dem Datenpunkt der die Solltemperatur des Geraetes einstellt z.B. "4.SET_TEMPERATUR" //Spalte 5 = Delay nach schliessen des Raumes bis zum Start des nächsten Programmlaufes // var NoneHMTab = []; // 0 = Raum 1 = Datenpunkt bis vor Geraet 2=Datenpunkt Geraet 3=Datenpunkt SollTemp 4= Ventilstellung bei NichtHeizperiode 5. Delay nach Verschluss zu NoneHMTab[0] = ['initial', 'javascript.0.Heizung', 'zwave1', '4.SET_TEMPERATURE', 12, 0]; NoneHMTab[1] = ['initial', 'ZWAVE.0', 'zwa0183xxx', '4.SET_TEMPERATURE', 12, 0]; NoneHMTab[2] = ['initial', 'maxcube.0.devices', 'thermostat_197b0b', 'setpoint', 12, 0]; // Typen-Tabelle der Verschlusssensoren fuer Homematic Geräte // 6 = Verschlussstatus = false ist gechlossen var SensorTypeTab = []; // 0.frei 1.GeraeteType 2. Beschreibung, 3.Type 4.DP Status 5.nicht verwendet 6. Verschlussstatus 7. direktverknuepft SensorTypeTab[0] = ['frei', 'HM-Sec-SCo' , 'Fenstersensor (neu)' , 'HM', '1.STATE' , false, false, true ]; SensorTypeTab[1] = ['frei', 'HM-Sec-SC' , 'Fenstersensor (alt)' , 'HM', '1.STATE' , false, false, true ]; SensorTypeTab[2] = ['frei', 'HM-Sec-RHS' , 'Fenster-Drehgriffkontakt', 'HM', '1.STATE' , false, 0, true ]; SensorTypeTab[3] = ['frei', 'HM-Sec-SC-2', 'Fenstersensor-2 (alt)' , 'HM', '1.STATE' , false, false, true ]; SensorTypeTab[4] = ['frei', 'HMIP-SWDO' , 'Fenstersensor (HMIP )' , 'IPSE', '1.STATE' , false, 0, true ]; SensorTypeTab[5] = ['frei', 'HMW-Sen-SC-12-DR', 'Schließerkontakt HMW' , 'HM', '1.STATE' , false, false, false ]; SensorTypeTab[6] = ['frei', 'HMW-IO-12-Sw14-DR', 'Schließerkontakt HMW' , 'HM', '1.STATE' , false, false, false ]; // wired SensorTypeTab[7] = ['frei', 'HmIP-SWDO-I' , 'Fenstersensor (HMIP )' , 'IPSE', '1.STATE' , false, 0, true ]; // IP innenliegender Sensor SensorTypeTab[8] = ['frei', 'HmIP-SWDM' , 'Fenstersensor (HMIP )' , 'IPSE', '1.STATE' , false, 0, true ]; // IP SensorTypeTab[9] = ['frei', 'HmIP-SWDM-B2' , 'Fenstersensor (HMIP )' , 'IPSE', '1.STATE' , false, 0, true ]; // IP // Tabelle der Verschlusssensoren fuer NichtHomematic Geräte // 5 = wenn script die Absenktemperatur setzen soll, dann false var NoneHMSenorTab = []; // 0= Raum 1 = Datenpunkt vis vor Geraet 0.RPC-Pfad 2. Datenpunkt Geraet 3. Datenpunkt FensterstatusGeraeteType 4.Verschlussstatus bei geschlossen 5. TempAbsenkung automatisch NoneHMSenorTab[0] = ['initial', 'javascript.0.Heizung.Heizplan', 'Wohnzimmer' , 'TestZusaetzlichesFenster' , 'false', false]; NoneHMSenorTab[1] = ['initial', 'fhem.0', 'OG_DU_TF' , 'state' , 'closed', false]; NoneHMSenorTab[2] = ['initial', 'maxcube.0.devices.contact_0a9d75', '', '', 'opened', false]; NoneHMSenorTab[3] = ['initial', 'javascript.0.Heizung.zwave1.isOpen', '', '', 'false', false]; NoneHMSenorTab[4] = ['initial', 'javascript.0.Heizung.zwave1.4.Fenster', '', '', 'false', false]; // Mit der Tabelle OverruleTab kann die Logik der Temperaturanpassungen beeinflusst werden (Sobald eine Anpassung erfolgt wird der Vorgang Overrule abgebrochen) // Die Tabelle kann als Prioritätenliste verstanden werden, wenn mehrere Parameter gleichzeitig zutreffen sollten. // Die Logik wird fuer jeden Raum ausgefuehrt var OverruleTab = []; OverruleTab[0] = ["UrlaubAnwesend"]; // Urlaubsanwesenheit / beeinflusst nicht direkt die Solltemp - ist aber wichtig fuer die Schedule Findung OverruleTab[1] = ["Abwesenheit"]; // Bei Abwesenheit wird die Temperatur der entsprechend Eisntellung abgesenkt OverruleTab[2] = ["UrlaubAbwesend"]; // Urlaubsabwesenheit - OverruleTab[3] = ["Gaeste"]; // Temperatur Anhebung OverruleTab[4] = ["Party"]; // Partyabsenkung // Bei Verwendung des Widgets Select-value List werden die Temperaturen nicht als Grad gespeichert var VerwendungSelectValue = true; // Weitere Pfade fuer die globalen Parameter - Empfehlung ist keine Aenderung vorzunehmen var StatePP_PartyAbsenkung = "ProfilParameter_PartyAbsenkung"; var StatePP_GaesteAnhebung = "ProfilParameter_GaesteAnhebung"; var StatePP_AbwesenheitAbsenkung = "ProfilParameter_AbwesenheitAbsenkung"; var StatePP_UrlaubAbsenkung = "ProfilParameter_UrlaubAbsenkung"; var StatePP_UrlaubWieFeiertag = "ProfilParameter_UrlaubWieFeiertag"; var StatePP_MinimaleTemperatur = "ProfilParameter_MinimaleTemperatur"; //Abwesenheitsabsenkung also absolute Temperatur - Standard ist eine Relative Temperatur die abgesenkt wird abhängig von der SollTemp z.B. SollTemp = 20 Grad. AbsenkTemp = 1 Grad - neue SollTemp = 19 Grad // Achtung um die absolute Temp zu nutzen muss das Widget zur AbsenkTemperatur angepasst werden. Kopiere z.B. Widget für die MinimalTemperatur anstelle der Standard AbsenkTemp und nenne den Datenpunkt entsprechend um var AbsenkTempAbsolut = false; // Standard ist false - bei true muss das Widget "Absenktemperatur" neu erstellt werden als Kopie eines Absoluten Temperatur Widgets wie z.B. MinimalTemp = 0 // UserExit Einstellungen // UserExits koennen genutzt werden, um die manuelle Temperatur von selbstdefinierten Abhängigkeiten zu steuern // Beipiel Steuerung von ElektroKonvektoren abhängig vom Energieertrag einer PV // Es koennen beliebig viele Eintragungen gemacht werden // // Die Datenpunkte werden nicht angelegt sondern muessen separat angelegt wreden // UserEexitTab Tabellendefinition: // 0 = Datenpunkt = Pfad Datenpunkt aufgrund dessen eine Reaktion erfolgen soll und Definition bei welchem Ereignis eine Reaktion erfolgen soll // 1 = Name der Routine - Routine muss im Userexit definiert sein // 2 = Operand - zulaessige Operanden sind // beliebiege Aenderung = "any" // gleich = "val" // groesser = "valGt" // groesser gleich = "valGe" // kleiner = "valLt" // kleiner gleich = "valLe" // ungleich = "valNe" // 3 = Wert - Vergleichswert der die Routine auslöst (im Zusammenhang mit dem Operanden // Sobald eine Bedingung zutrifft wird der gelistete UserExit aufgerufen. Die zugehoerige Routine wird zu anfang des UserExits ermittelt und kann dann weiterverarbeitet werden // Rueckgabe des UserExits ist ein Raumname sowie eine manuelle Temperatur und Gueltigkeit in Minuten. Die Temperatur wird dann entsprechend mit der Gueltigkeit gesetzt // eine evt. vorher eingestellte manuelle Temperatur gelöscht - die neue manuelle Temp wird eingestellt. Ist die dauer auf 999 eingestellt gilt diese Temperatur bis zum nächsten UserExit aufruf // Die UserExitTab kann beliebig erweitert werden. Neue Routinen, die sich nicht in der Standardliste befinden muessen entsprechend programmiert werden var UserExitTab = []; // 0 = Datenpunkt 1= Raum 2= Routine 3=Operand 3= Vergleichswert UserExitTab[0] = ['initial', 'Testraum', 'TriggerHeating-any', 'any', 0 ]; UserExitTab[1] = ['initial', 'Wohnzimmer', 'TriggerHeating-val', 'val', 30 ]; UserExitTab[2] = ['initial', 'Wohnzimmer', 'TriggerHeating-valGt', 'valGt', 100 ]; UserExitTab[3] = ['initial', 'Wohnzimmer', 'TriggerHeating-valGe', 'valGe', 100 ]; UserExitTab[4] = ['initial', 'Wohnzimmer', 'TriggerHeating-valLt', 'valLt', 100 ]; UserExitTab[5] = ['initial', 'Wohnzimmer', 'TriggerHeating-valLe', 'valLe', 100 ]; UserExitTab[6] = ['initial', 'Wohnzimmer', 'TriggerHeating-valNe', 'valNe', 100 ]; // es sind 5 globale Tabellen vordefiniert. Werden diese im UserExit befuellt bleiben die Werte erhalten für den nächsten Aufruf var UserExitValueTab1 = []; var UserExitValueTab2 = []; var UserExitValueTab3 = []; var UserExitValueTab4 = []; var UserExitValueTab5 = []; //------------------------------------------------------------------------------ // Ende Experteneinstellungen //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Beginn Entwicklereisntellungen // Entwicklereinstellungen sollten in Absprache mit dem Enwickler gemacht werden //------------------------------------------------------------------------------ // Delay Eisntellungen für Thermostatsynching var ThermostChangeDelay = 2000; var SynchDelay = 500; // Debugging Level var trace = true; var ManDebug = false; if ( debug) { var trace = true; var ManDebug = true; } //------------------------------------------------------------------------------ // Ende Entwicklereisntellungen //------------------------------------------------------------------------------ // Kompatibilitätsvariablen - macht die user einstellungen abwärtskompatibel bis version 2.0 if (SoftBoostTemp === undefined) { // falls nicht definiert / abwärtskompatibilität var SoftBoostTemp = 30; } if (AbsenkTempAbsolut === undefined) { // falls nicht definiert / abwärtskompatibilität var AbsenkTempAbsolut = false; } if (UseRaumAnwesenheit === undefined) { // falls nicht definiert / abwärtskompatibilität var UseRaumAnwesenheit = false; } // Variablendefinition var ControlTab = []; // Zentrale Tabelle der Thermostate var SensorList = []; // Liste der Verschlusssensoren var DelayAfterClose = []; // Liste fuer Thermostate, die einen Delay nachdem die Verschluesse geschlossen werden, benötigen (z.B. alte HM Thermostate) var fs = require('fs'); // enable write fuer externes log var cronjob = "*/" + cron + " * * * *"; // CRON pattern aufgrund der Vorgabe in den Einstellungen var SubscribeThermBlock = []; // dieses Array dient dazu doppelte Ausführungen zu vermeiden, wenn die Solltemp geaendert wird, da Trigger auf den Solltemps liegen // Diese Variablen sind ein Findungsnachweis fuer die gesetzte Temperatur - Sie werden am Ende im Raum gespeichert und dienen auch zur ermittlung von manuell eingestellten Temperatur var Source_Profil; var Source_ICALEvent; var Source_ManualAdjustment; var Source_GlobalParameter; var Source_SchedulePoint; var Source_Timestamp; var Source_CurrentSollTemp = 0; var Source_NextSollTemp = 0; var Source_last_Program_Run; var Source_ManTempSet = 0; var TriggerChangeThermostat = false; // Object Deklarationen fuer Subscriptionsteuerungen (und flexiblen schedules) var ManTimeouts = {}; var NextSchedules = {}; var ownStateChanges = {}; var rooms = {}; // Object zur Verwaltung der Delays (Programmaufruf) nach triggern des Programmes / var roomUpdateDelay = {}; // globale Variable //------------------------------------------------------------------------------ // Ablauf der Routinen bei Programmstart //------------------------------------------------------------------------------ getDevices(); // Liste der Thermostate und Sensoren generieren for (var RelRoom in rooms){ log ("Checke ob Datenpunkte angelegt werden müssen Raum: " + RelRoom, "info"); CreateStates(RelRoom); // alle relevanten States anlegen } setTimeout(function() { // Rest Initialisierung leicht verzögern damit States angelegt sind vorher initializeData(); // Funktionsaufruf zur Einsteuerung der Subscriptions oder Schedule (Cron > 0) }, 2000); setTimeout(function() { // Abarbeitung der Räume leicht verzögern damit States angelegt sind vorher LoopRooms(); // Jetzt werden die Räume erstmalig abgearbeitet. Danach geht alles über subscriptions und schedules }, 2500); //----------------------------------------------------------------------------------------------------- //ab hier Haupt-Routinen // 1. GetDevices -- einlesen der Thermostate und Sensoren bei jedem Start des Scriptes // 2. initializeData -- Bucht subscriptions ein (bzw den schedule fuer Cron-Ablauf) // 3. TriggerUpdate -- wird von den Subscriptions aufgerufen und startet das Script bei Aenderungen // 4. TriggerThermostatUpdate -- wird von den Thermostattriggern aufgerufen bei Aenderungen am Thermostat // 5. Loop Rooms -- wird ausgeführt durch den schedule oder durch TriggerUpdate und bei scriptstart durch initializeData // 6. LoopDevices -- wird durch LoopRooms, TriggerUpdate, SensorChange oder ThermostatChange ausgeführt - ist eng verknüpft mit ManAdjustments // 7. ThermostatChange -- bei jeder manuellen Aenderung des Thermostats wird diese Routine aufgerufen. Ruft dann ManAdjustments auf // 8. SensorChange -- wird ausgeführt wenn ein Verschluss geoeffnet/geschlossen wird und // 9. ManAdjustments -- behandelt alle manuellen SollTemp Aenderungen und wird von LoopDevices oder TriggerThermostatUpdate aufgerufen //----------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------- // Funktion getDevices - Zentrale Funktionen zum Lesen von enum und objekt - Einlesen von Thermostaten und Sensoren //----------------------------------------------------------------------------------------------------- function getDevices() { // jetzt alle zugehoerigen Thermostate finden und in Crontab speichern var roomName; var roomNoSpace; var idExtract; var fullname; var devtype; var FinishRoom = false; var y = 0; var SelectorVerschluss; var SelectorThermostat; var hmprc; var devTypeThermTab; var sensortype; var StateDP; var x; // Auslesen aller Raeume var allRooms = getEnums('rooms'); // Lade alle Raeume // jetzt Thermostate einlesen // Lade alle Raeume for (var i in allRooms) { // loop ueber alle Raeume roomName = allRooms[i].name; if (!RoomListUsage(roomName)) continue; // wenn die Raumliste genutzt wird ist und der Raum in der Liste enthalten ist if (debug) { log("Gibt es ein zugeordnetes Gerät für den Raum " + roomName + " wird jetzt ueberprueft", "info"); } roomNoSpace = roomName.replace(/\s/g, "_"); for (x in ThermostatTypeTab){ // loop ueber die moeglichen Thermostattypen devTypeThermTab = ThermostatTypeTab[x][1].toUpperCase(); StateDP = ThermostatTypeTab[x][4]; $('channel[state.id=*.' + ThermostatTypeTab[x][4] + '] (rooms=' + roomName + ') (functions=' + HeizungGewerk + ') ').each(function (id, i) { if (ExcludeHMSensors.indexOf(id) !== -1) return; // ID steht auf der Exclude-Liste idExtract = id.substr(0,id.length - StateDP.length - 1); fullname = getObject(id).common.name; devtype = getObject(idExtract).native.TYPE.toUpperCase(); //if (devtype.includes(devTypeThermTab)) { // hier wirden nur ähnlichkeiten verglichen. Das könnte zu Fehlern führen. if(devtype ===devTypeThermTab) { SelectorThermostat = $('channel[state.id=' + id + '] '); SelectorThermostat.each(function (id, i) { on({id: id, change: 'any'}, function(obj) { // erstelle subscription //if (obj.state.ack) { if (obj.state.val !== obj.oldState.val) { if (ownStateChanges[id]) { ownStateChanges[id]--; if (ownStateChanges[obj.id] === 0) delete ownStateChanges[id]; if (debug) {log("Ignoriere Trigger nach Änderung für State " + id, "info");} return; } TriggerThermostatUpdate(id) } }); // endon }); // endeach // 0 = roomName 1 = id 2 = devtype 3 = FullName 4 ID Extract 5 = Type des Geraetes 6 = DP SollTemp 7 = Manu/Auto 8 = Steuerung zentral 9 = DP Ist-Temperatur 10 = DP MANU Check 11=Ventil Offen 12= Delay nach Verschluss zu ControlTab[y] = [roomNoSpace, id, devtype, fullname, idExtract, ThermostatTypeTab[x][3], ThermostatTypeTab[x][4], ThermostatTypeTab[x][6], ThermostatTypeTab[x][7], ThermostatTypeTab[x][8], ThermostatTypeTab[x][9], ThermostatTypeTab[x][10], ThermostatTypeTab[x][11]]; y++; rooms[roomName] = true; if (debug) {log("Routine getdevice fuer " + roomNoSpace,"info");} if (ThermostatTypeTab[x][7] === true) { // Das ist das zentrale Steuerthermostat FinishRoom = true; // gehe zum naechsten Raum } log("Routine GetDevices fuer HM Thermostate " + roomName +" - " + ThermostatTypeTab[x], "info"); } }); // end Channel loop if (FinishRoom) { FinishRoom = false; break; } // gehe zum naechsten Raum } // End ThermostatTypeTab } // End rooms - Homematic Geraete bzw voll eingebundene Geraete // Fuellen der Nicht-Homematic Geraete in die ControlTab y = ControlTab.length; var id; for (var b in NoneHMTab) { roomName = NoneHMTab[b][0]; if (roomName !== "initial" && RoomListUsage(roomName)) { id = ConstructID(NoneHMTab[x][1],NoneHMTab[x][2],NoneHMTab[x][3]); var NoneHMObj = getObject(id); if (NoneHMObj && NoneHMObj.common && NoneHMObj.common.name) { fullname = NoneHMObj.common.name; } else { fullname = "n/a"; } devtype = "NoneHM"; if (debug ) {log("Routine getdevice fuer " + roomName, "info");} roomNoSpace = roomName.replace(/\s/g, "_"); if(NoneHMTab[b][5] === undefined) { // für den Fall, dass in der Tabelle kein Wert eingetragen wurde - wird nur zur kompatibilität zu den alten Einstellungen gebraucht NoneHMTab[b][5] = 0; } // 0 = room 1 = id 2 = devtype 3 = FullName 4 ID Extract 5 = Type des Geraetes 6 = DP SollTemp 7 = Manu/Auto 8 = Steuerung zentral 9 = DP Ist-Temperatur 10 = DP MANU Check 11=Ventil Offen - 12= Delay nach Verschluss zu ControlTab[y] = [roomNoSpace, id, devtype, fullname, NoneHMTab[b][2], 'HT', NoneHMTab[b][3], false, false, false, false, NoneHMTab[b][4], NoneHMTab[b][5] ]; y++; // subscription für Aenderung auf Aenderung der Raumtemperatur des Thermostates zu reagieren on({id: id, change: 'ne'}, function(obj) { if (ownStateChanges[obj.id]) { ownStateChanges[obj.id]--; if (ownStateChanges[obj.id] === 0) delete ownStateChanges[obj.id]; if (debug) {log("Ignoriere Trigger nach Änderung für State " + obj.id, "info");} return; } TriggerThermostatUpdate(obj.id) }); // ende on id log("Routine GetDevices fuer NoneHM Thermostate " + roomName +" - " + NoneHMTab[b], "info"); rooms[roomName] = true; } // endif roomName war nicht initial } // endfor Nicht Homematic Geraete // jetzt die Control Tab Sortieren nach Raumnamen ControlTab.sort(SortControlTab); log("Liste der Thermostate in der Control Tabelle", "info"); for (var c in ControlTab) { log(ControlTab[c],"info"); } // Verschluss sensoren einlesen und subscription buchen // SensorTypeTab[5] = ['hm-rpc.1.', 'HMIP-SWDO' , 'Fenstersensor (HMIP )' , 'IPSE', '1.STATE' , 14, false, true]; y = 0; for (roomName in rooms) { // suche Sensoren in allen Räumen mit mind einem Kontroll-Gerät roomNoSpace = roomName.replace(/\s/g, "_"); for (x in SensorTypeTab){ sensortype = SensorTypeTab[x][1]; StateDP = SensorTypeTab[x][4]; $('channel[state.id=*.' + SensorTypeTab[x][4] + '] (rooms=' + roomName + ') (functions=' + SensorGewerk + ') ').each(function (id, i) { if (ExcludeHMSensors.indexOf(id) !== -1) return; // ID steht auf der Exclsude-Liste idExtract = id.substr(0, id.length - StateDP.length - 1); fullname = getObject(id).common.name; devtype = getObject(idExtract).native.TYPE; if (devtype === sensortype ) { SensorList[y] = [roomNoSpace, id, devtype, fullname, idExtract, SensorTypeTab[x][3], SensorTypeTab[x][4], SensorTypeTab[x][5], SensorTypeTab[x][6], SensorTypeTab[x][7] ]; SensorList[y][7] = getState(SensorList[y][1]).val; // Status des Sensors log("Routine GetDevices fuer HM Sensoren "+ roomName +" - " + SensorList[y], "info"); y++; SelectorVerschluss = $('channel[state.id='+id+'] '); SelectorVerschluss.on(function(obj) { // bei Zustandaenderung if (obj.state.ack && obj.state.val !== obj.oldState.val) { SensorChange(id); } }); // endon } }); // end Channel loop } // Endfor SensorTypeTab } // Endfor rooms Verschlusssensoren // Fuellen der Nicht-Homematic Sensoren in die SensorTab // Map-Dokumentation: // Sensortypetab = 0.RPC-Pfad 1.GeraeteType 2. Beschreibung, 3.Type 4.DP Status 5.Laenge ID 6. Verschlussstatus 7. direktverknuepft // NonHM-SensorTab 0= Raum 1 = Datenpunkt vis vor Geraet 0.RPC-Pfad 2. Datenpunkt Geraet 3. Datenpunkt FensterstatusGeraeteType 4.Verschlussstatus bei geschlossen 5. TempAbsenkung automatisch, // // Sensorlist SensorList[y] = [roomNoSpace, id, devtype, fullname, idExtract, SensorTypeTab[x][3],SensorTypeTab[x][4],SensorTypeTab[x][5],SensorTypeTab[x][6],SensorTypeTab[x][7] ]; // Füllen der sensorlist raum=0 komplette id=1 NoneHM=2 Name aus getobject(id)=3 NoneHMSenorTab(1)=4 "NoneHM"=5 NoneHMSenorTab(2)=6 laenge ID=7 verschlussstatus=8 direktverknüpft=9 , y = SensorList.length; // letzter Eintrag der Sensorlist for (x in NoneHMSenorTab) { roomName = NoneHMSenorTab[x][0]; if(roomName !== "initial" && RoomListUsage(roomName)) { // wenn die Raumliste genutzt wird ist und der Raum in der Liste enthalten ist. y++; roomNoSpace = roomName.replace(/\s/g, "_"); id = ConstructID(NoneHMSenorTab[x][1],NoneHMSenorTab[x][2],NoneHMSenorTab[x][3]); var NoneHMSensorObj = getObject(id); if (NoneHMSensorObj && NoneHMSensorObj.common && NoneHMSensorObj.common.name) { fullname = NoneHMSensorObj.common.name; } else { fullname = "n/a"; } devtype = "NoneHM"; if (debug ) {log("Routine getdevice fuer NoneHMSenorTab " + roomName,"info");} //Füllen: raum=0 komplette id=1 NoneHM=2 Name aus getobject(id)=3 Datenpunkt bis vor Geraet=4 "NoneHM"=5 Beschreibung=6 verschlussstatuss=7 frei=8, direktverknuepft=9 SensorList[y] = [roomNoSpace, id, devtype, fullname, NoneHMSenorTab[x][1], NoneHMSenorTab[x][3], fullname, getState(id).val, false, NoneHMSenorTab[x][5] ]; log("Routine GetDevices NoneHM Sensoren: " + roomNoSpace + " - " + SensorList[y], "info"); on({id: SensorList[y][1], change: 'ne'}, function(obj) { SensorChange(obj.id); }); // ende on id } // endif roomName war nicht initial } // endfor Nicht Homematic Geraete // Ausgabe der Devices - Thermostate und Sensoren // Raumstatus ermitteln über alle Sensoren für jeden Raum var Statuscheck = false; for (roomName in rooms) { // loop ueber all Raeume Statuscheck = false; roomName = roomName.replace(/\s/g, "_"); // Blanks durch unterstrich ersetzen for (var z in SensorList ) { // loop über all Sensoren des Raumes if (SensorList[z][0] === roomName) { if(SensorStatCalc(SensorList[z][1], SensorList[z][2] ) ) { Statuscheck = true; // wenn einer true dann alle true break; // nächster Raum } else { Statuscheck = false; } //SensorStatCalc - Ermittlung status des Sensors } // endif nur sensoren des Raumes } // endfor Sensorlist if(Statuscheck === false) { setOwnState(path + "." + roomName + ".RaumStatusVerschluss", false); // Raum ist geschlossen log ("Raum "+ roomName + " Status geschlossen") } else { setOwnState(path + "." + roomName + ".RaumStatusVerschluss", true); // Raum ist geöffnet log ("Raum "+ roomName + " Status geöffnet") } // endif Statuscheck } // endfor rooms log("Routine GetDevices Devices initialisiert","info"); } // ende Funktion //------------------------------------------------------------------------------ // Diese Funktion bucht alle Subscriptions ein - ausser Thermostate und Sensoren (wird in Getdevices gemacht) //------------------------------------------------------------------------------ function initializeData() { //----------------------------------------------------------------------------------------------------- // Job zur Ausfuehrung der Kernfunktion (Temperatur Setting) //----------------------------------------------------------------------------------------------------- if (cron > 0) { log("Heizungsscript verarbeitung benutzt Cron", "info"); schedule(cronjob, function() { LoopRooms(); // Ablauflogik entlang der gefundenen Thermostate fuer alle Raeume log("Heizungsscript verarbeitung Cron durchgelaufen", "info"); return; }); // Ende Job return; } //----------------------------------------------------------------------------------------------------- // oder es läuft alles über Events und Trigger //----------------------------------------------------------------------------------------------------- log("Heizungsscript verarbeitung benutzt Trigger und Events", "info"); var ProfilName; var subscribeIdList = [StateFeiertagHeuteAdapter,StateAnwesenheitFunction,StateHeizperiode]; if (UseFeiertagskalender ) { subscribeIdList.push(StateFeiertagHeuteAdapter); } if (UseFeiertagskalender === false) { subscribeIdList.push(ICALPath + "."+ EventG_Feiertag); } // ICAL globale Event Parameter subscribeIdList.push(ICALPath + "."+ EventG_Party); subscribeIdList.push(ICALPath + "."+ EventG_UrlaubAbwesend); subscribeIdList.push(ICALPath + "."+ EventG_UrlaubAnwesend); subscribeIdList.push(ICALPath + "."+ EventG_Gaeste); // ICAL globale Profil Subscriptions if (getState(Gparameterpath + ".ICAL-Events_Aktiv").val === true) { for (var i = 1; i <= MaxProfile; i++) { ProfilName = UseEventG_Profil.replace("", i.toString()); subscribeIdList.push(ICALPath + "." + ProfilName); } } // jetzt alles aus der subscribeIdList subscriben on({id: subscribeIdList, change: 'ne'}, function(state) { if (debug) { log("Trigger nach Änderung für State " + state.id, "info");} TriggerUpdate(undefined,false,state.id); }); // Auf Änderungen der Globalen Parameter im Heizungspfad reagieren on({id: new RegExp("^" + Gparameterpath .replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') + '\\.'), change: 'ne'}, function(state) { if (ownStateChanges[state.id]) { ownStateChanges[state.id]--; if (ownStateChanges[state.id] === 0) delete ownStateChanges[state.id]; if (debug) {log("Ignoriere Trigger nach Änderung für State " + state.id, "info");} return; } if (debug) {log("Trigger nach Änderung für State " + state.id, "info");} TriggerUpdate(undefined,false,state.id); }); // Pro Raum auf Änderungen registrieren im Raumpfad for (var roomName in rooms) { roomName = roomName.replace(/\s/g, "_"); on({id: new RegExp("^" + path.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') + '\\.' + roomName + '\\.'), change: 'ne'}, function(state) { var roomName = state.id.substring(path.length + 1); roomName = roomName.substring(0, roomName.indexOf('.')); if (ownStateChanges[state.id]) { ownStateChanges[state.id]--; if (ownStateChanges[state.id] === 0) delete ownStateChanges[state.id]; if (debug) {log("Ignoriere Trigger nach Änderung für State " + state.id, "info");} return; } if (debug) {log("Trigger nach Änderung für State " + state.id + " und Raum " + roomName, "info");} TriggerUpdate(roomName,state.id); }); } // endfor rooms // Auf Änderungen fuer raumbezogene An/Abwesenheiten reagieren if (UseRaumAnwesenheit) { // Soll die Raumanwesenheit genutzt werden ? const ONSub = []; for (let x = 0; x < RaumAnwTAB.length; x++) { if(RaumAnwTAB[x][0] !== "initial" && RaumAnwTAB[x][1] !== "initial" ) { // nur wenn nicht auf initial gesetzt im array const roomC = RaumAnwTAB[x][0].replace(/\s/g, "_") ONSub.push(on(RaumAnwTAB[x][1], (obj) => TriggerUpdate(roomC,false,obj.id))); //über Const da aus dem Pfad kein Raum ableitbar } } } // endif raumanwesenheiten // jetzt raumbezogene ICAL Profile subscriben const ONSub = []; for (roomName in rooms) { const roomC = roomName.replace(/\s/g, "_") for (var y = 1; y <= MaxProfile; y++) { ProfilName = UseEventR_Profil; ProfilName = UseEventR_Profil.replace("", roomName); ProfilName = ProfilName.replace("", y.toString()); ONSub.push(on(ICALPath + "."+ProfilName, (obj) => TriggerUpdate(roomC,false,obj.id))); } } // Subscription des User Exits anhand der User Exit Konfigurationstabelle aus den Einstellungen for (var x in UserExitTab ) { if (UserExitTab[x][0] === "initial" ) { continue; } var check = false; var subscribeObj = {id: UserExitTab[x][0]}; switch (UserExitTab[x][3]) { case "valNe": subscribeObj.change = "valNe" && UserExitTab[x][4]; check = true; break; case "valGt": subscribeObj.change = "valGt" && UserExitTab[x][4]; check = true; break; case "valGe": subscribeObj.change = "valGe" && UserExitTab[x][4]; check = true; break; case "valLt": subscribeObj.change = "valLt" && UserExitTab[x][4]; check = true; break; case "valLe": subscribeObj.change = "valLe" && UserExitTab[x][4]; check = true; break; case "val": subscribeObj.change = "val" && UserExitTab[x][4]; check = true; break; case "any": subscribeObj.change = "any"; check = true; break; } if (check) { log(" subscribeObj id = " + subscribeObj.id + " change = " + subscribeObj.change,"info" ); } else { log("User Exit - Fehler in Setup Tabelle UserExitTAB Spalte 2") } on(subscribeObj, function(obj) { UserExitPrep(obj.id, obj.state.val); }); // ende on id } // Endfor User Exit } //----------------------------------------------------------------------------------------------------- // Diese Funktion wird bei jeder registrierten Änderung mit der Ausnahme von Thermostaten und Sensoren aufgerufen und sorgt für // eine Abarbeitung des/der Räume mit kleiner Verzögerung falls mehrere Werte nacheinander geändert // wurden, sodass die Logik nur einmalig ausgeführt wird. // Parameter: Raumname wenn bekannt oder "undefined" und Flag für manuelle Änderungen //----------------------------------------------------------------------------------------------------- function TriggerUpdate(room, manChange, id) { if (trace) { log("Routine TriggerUpdate Trace: gestarted fuer Raum " + room + " und id " + id,"info"); } if (manChange === undefined) manChange = false; if (manChange && room && ManTimeouts[room]) { // Funktion wurde durch manuellen Timeout getriggert, zurücksetzen ManTimeouts[room] = null; } var delayRoom = room; if (!room) { // Kein Raum gesetzt delayRoom = 'all'; } else if (roomUpdateDelay.all) { // Raum gesetzt aber ein Timeout für alle räume läuft schon, also bleibt es dabei log("Routine TriggerUpdate: Sonderfall ein Timeout für alle Räume läuft schon - Raum war " + room + " ... reset to all", "info"); room = undefined; delayRoom = 'all'; } if (roomUpdateDelay[delayRoom]) { // Wenn für aktuellen Raum ein Timeout gesetzt ist dann beendedn clearTimeout(roomUpdateDelay[delayRoom]); roomUpdateDelay[delayRoom] = null; } if (!room) { // Falls kein Raum angegeben wurde for (var roomName in roomUpdateDelay) { // Alle Tneouts auch beenden, muss ja nicht mehrfach laufen if (roomUpdateDelay[roomName]) { clearTimeout(roomUpdateDelay[roomName]); roomUpdateDelay[roomName] = null; } } } var delayForUpdate = 1000; if (!room) { delayForUpdate = 5000; } roomUpdateDelay[delayRoom] = setTimeout(function () { roomUpdateDelay[delayRoom] = null; LoopRooms(room); // Ablauflogik entlang der gefundenen Thermostate fuer alle Raeume log("Heizungsscript verarbeitung Trigger für Raum " + delayRoom + " durchgelaufen", "info"); }, delayForUpdate); } //----------------------------------------------------------------------------------------------------- // Diese Funktion wird bei jeder registrierten Änderung von Thermostaten aufgerufen und sorgt für // eine Abarbeitung des/der Räume mit kleiner Verzögerung falls mehrere Werte nacheinander geändert // wurden, sodass die Logik nur einmalig ausgeführt wird. // Parameter: ID des Thermostat //----------------------------------------------------------------------------------------------------- function TriggerThermostatUpdate(id) { var room; for (var x in ControlTab ) { if (ControlTab[x][1] === id ) { room = ControlTab[x][0]; break; } } if (trace) { log("Routine TriggerThermostatUpdate Trace: gestarted fuer Raum " + room,"info"); } var delayRoom = room; if (!room) { // Kein Raum gesetzt log("Routine TriggerThermostatUpdate Trace: konnte keinen Raum zur id zuordnen - ID = " + id,"info"); return; } else if (roomUpdateDelay.all) { // Raum gesetzt aber ein Timeout für alle räume läuft schon, also bleibt es dabei log("Routine TriggerThermostatUpdate: Sonderfall ein Timeout für alle Räume läuft schon - Raum war " + room + " ... reset to all", "info"); room = undefined; delayRoom = 'all'; } if (roomUpdateDelay[delayRoom]) { // Wenn für aktuellen Raum ein Timeout gesetzt ist dann beendedn clearTimeout(roomUpdateDelay[delayRoom]); roomUpdateDelay[delayRoom] = null; } var delayForUpdate = 1000; roomUpdateDelay[delayRoom] = setTimeout(function () { roomUpdateDelay[delayRoom] = null; ThermostatChange(id); }, delayForUpdate); } //----------------------------------------------------------------------------------------------------- // Funktion LoopRooms - Abarbeiten der Raeume //----------------------------------------------------------------------------------------------------- function LoopRooms(room) { var roomName; var globalChange = false; if (trace) { log("Routine LoopRooms Trace: gestarted fuer Raum " + room,"info"); } // Feiertagsflag synchronisieren if (UseFeiertagskalender) { // Feiertagskalender Adapter aktiv ? if (getState(StateFeiertagHeuteAdapter).val !== getState(StateFeiertagHeute).val ) { // Feiertagsflag aktualisieren setState(StateFeiertagHeute, getState(StateFeiertagHeuteAdapter).val); globalChange = true; } } // Anwesenheitsflag synchronisieren if (UseAnwesenheitserkennung) { if (getState(StateAnwesenheitFunction).val !== getState(StateAnwesenheit).val ) { // Anwesenheit synchen mit setState(StateAnwesenheit, getState(StateAnwesenheitFunction).val); globalChange = true; } } Source_last_Program_Run = formatDate(new Date(),"DD/MM/JJJJ SS:mm:ss"); setOwnState(Gparameterpath + ".Source_last_Program_Run", Source_last_Program_Run); SetEventGlobalParameter(); // checken of ICAL events als global parameter vorliegen for (roomName in rooms) { // loop ueber all Raeume roomName = roomName.replace(/\s/g, "_"); // Blanks durch unterstrich ersetzen if (room && room !== roomName) continue; // Wenn für einen Raum getriggert müssen wir nur den Raum abarbeiten if (getState(path + "." + roomName + ".AktivesRaumProfil").val > 0) { if (debug) { log("Routine LoopRooms Starte Abarbeitung fuer Raum " + roomName, "info"); } ClearSources(); LoopDevices(roomName); if (debug) { log("Routine LoopRooms Ende Abarbeitung fuer Raum " + roomName, "info"); } } } // Endfor rooms if (cron === 0) { setTimeout(function() { if (Object.keys(ownStateChanges).length > 0) { if (debug) { log("Restliche Triggered States resetten: " + JSON.stringify(ownStateChanges), "info"); } } ownStateChanges = {}; }, 10000); } } // endfunction //----------------------------------------------------------------------------------------------------- // Funktion LoopDevices - Abarbeiten der Thermostate // Der eigentliche Loop findet in der Routine SyncThermostat statt // die übergebene ID ist das führende Thermostat mit dem alle anderen synchronisiert werden //----------------------------------------------------------------------------------------------------- function LoopDevices(roomName) { var ScheduledSollTemp; var ManAdj; var ManAdjTimeStamp = getState(path + "." + roomName +".Source_TimeStamp").val; var id; var Sensor; Source_CurrentSollTemp = 0; Source_NextSollTemp = 0; Source_SchedulePoint = ""; Source_GlobalParameter = ""; if(debug) { log("loop Devices gestarted fuer Raum "+roomName, "info"); } Source_ManTempSet = null; // Variable zurücksetzen - wird gebraucht fuer das synchen von Thermostaten if (trace) { log("Routine LoopDevices Trace: gestarted fuer Raum " + roomName,"info"); } for (var x in ControlTab) { // finden des ersten ControlTab Eintrages if (ControlTab[x][0] === roomName) { id = ControlTab[x][1]; break; } // endif finde ersten controltab Eintrag } // End for // Handling einer evt manuellen Temperatur im Regler oder im view eingestellt ManAdj = ManAdjustments(roomName, id); // Finden ob es man adjustments gibt // Nachbehandlung if (ManAdj === false ) { // es gibt keine manuelle Anpassung if(debug) { log( "Routine Loop Devices: es soll eine Temp-Findung durchgeführt werden ","info"); } SaveStatus("Heizplan", roomName, false); // Status Meldung evt zurücksetzen var ScheduledTemp = ExecuteTempDetermination(roomName, id); if (trace) { log("Routine LoopDevices Trace: Temp nach Temperaturfindung wird jetzt gesynched fuer Raum " + roomName + " mit Temp "+ScheduledTemp, "info"); } SyncThermostat(roomName, ScheduledTemp, id); // jetzt Temp synchen writelog(roomName, id, "Temperatur wird nach Schedule eingestellt"); } // endif keine manuellen Adjustments else { // es liegt eine manuelle Korrektur vor if(debug) { log( "Routine Loop Devices: es soll keine Temp-Findung durchgeführt werden ","info"); } SaveStatus("Manuell", roomName,false); // Meldung ausgeben dass eine manuelle Temp vorliegt writelog(roomName, id, ""); if (Source_ManTempSet === null) { // keine manuelle Temp gesetzt - Das wäre ein Problem an dieser Stelle log("Routine LoopDevices: Source_ManTempSet ist NULL") } if (trace) { log("Routine LoopDevices Trace: Temp nach Erkennung manueller Temp wird jetzt gesynced fuer Raum " + roomName + " mit Temp "+Source_ManTempSet, "info"); } SyncThermostat(roomName, Source_ManTempSet, id); // jetzt Temp synchen } // endif Manuelle Anpassung erkannt } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion ThermostatChange erkennt die Veraenderung der Solltemperatur durch subsription //----------------------------------------------------------------------------------------------------- function ThermostatChange(id) { var ManAdj = false; var room; var ActTime = formatDate(new Date(),"YYYY/MM/DD SS:mm:ss"); // Formatierte Aktuelle Zeit var ActTimeMilliSek = new Date(ActTime).getTime(); // Aktuelle Zeit in Millisekunden seit 1970 TriggerChangeThermostat = false; // wird für die routine ManAdjustments verwendet // Raum finden for (var x in ControlTab ) { if (ControlTab[x][1] === id ) { room = ControlTab[x][0]; break; } } if (trace) { log("Routine ThermostatChange Trace: gestarted fuer Raum " + room+ " und id " + id ,"info"); } Source_CurrentSollTemp = 0; // Ruecksetzen der current SollTemp fuer nachfolgende Programme // wenn der Raum vor < 1 Sekunde bereits ein update hatte wird hier abgebrochen var LastRoomUpdate = ActTimeMilliSek - LastRoomUpdateTime(room, "find"); // Differenzzeit aus dieser Zeit und der letzten updatezeit des Raumes (Vermeidung von eigenen Triggern wenn die Solltemp geaendert wird) if ( LastRoomUpdate <= ThermostChangeDelay) { // der Raum wurde vor weniger als x Millisekunden bereits upgedated log("Routine ThermostatChange: Der Raum wurde vor weniger als " + ThermostChangeDelay + " Millikekunden bereits upgedated Aenderung wird ignoriert " + LastRoomUpdate, "info"); return; } writelog(room, id, "Am Thermostat wurde eine neue Temperatur festgestellt " + getState(id).val); log("Routine ThermostatChange: " + id + " Raum " + room + " Manuelle Solltemperatur-Aenderung erkannt auf " + getState(id).val , "info"); Source_ManTempSet = getState(id).val; if (Source_ManTempSet !== VerschlussAbsenkungsGrenze) { writelog(room, id, "Routine ThermostatChange: Thermostat " + id + " Raum " + room + " Thermostat Solltemperatur-Aenderung erkannt auf " + Source_ManTempSet) ; setOwnState(path + "." + room + ".RaumStatusVerschluss", false); // Raum ist geschlossen TriggerChangeThermostat = true; // wird für die routine ManAdjustments verwendet ManAdj = ManAdjustments(room, id); TriggerChangeThermostat = false; // wird für die routine ManAdjustments verwendet } else { ManAdj = true; // relevant fuer Syncing SaveStatus("Fenster", room,false); // "Verschluss offen TemperaturAbsenkung gesetzt"; setOwnState(path + "." + room + ".RaumStatusVerschluss", true); // Raum ist geoeffnet writelog(room, id, "Routine ThermostatChange: Temperaturabsenkung erkannt" + id + " Raum " + room + " auf: " + Source_ManTempSet ) ; } if (Source_ManTempSet === null) { log("Routine ThermostatChange: ThermostatChange in Temperatur NewThermostatTemp hat keinen Wert - ?","info"); return; } if (debug) { log("Routine ThermostatChange: Starte Sync fuer Manuelle Temperatur fuer " + id + " Temperatur = " + Source_ManTempSet, "info"); log("ControlTab " + ControlTab[x], "info"); } if ( ManAdj) { // Sync nur notwendig wenn es eine manuelle Aenderung gab SyncThermostat(room, Source_ManTempSet, id); // jetzt manuelle Temp synchen } } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion SensorChange erkennt die Verschlussstellung eines Sensors und stellt die Temperatur entsprechend ein //----------------------------------------------------------------------------------------------------- function SensorChange(id) { var tabNo; var delay = 0; var IDThermostat; tabNo = SensorFind(id) ; if (tabNo === 999) { log("Routine SensorChange: Sensor " + id + " nicht in Sensorliste gefunden","info"); return; // Sensor nicht in Sensorlist gefunden } // Ermittlung des Status des Sensors und update in Tabelle zur späteren Raumstatusermittlung SensorList[tabNo][7] = SensorStatCalc(id, SensorList[tabNo][2]); // id des Sensors und device type - Der Status des Sensors wird ermittelt und in die sensorliste eingetragen // zugeordneter Raum steht in Sensorlist var room = SensorList[tabNo][0].toString(); if (trace) { log("Routine SensorChange Trace: gestarted fuer Raum " + room+ " und id " + id ,"info"); } // Wenn keine Heizperiode if (getState(StateHeizperiode).val === false) { return; // keine Heizperiode } if (getState(path + "." + room + ".Source_Profil").val === 0 ) { return; // es ist noch kein Profil dem Raum zugeordnet } if (debug) { log("Routine SensorChange: Fenster " + id + " status geaendert fuer Raum " + room + " " + SensorList[tabNo][1] + " " + SensorList[tabNo][0] + " " + SensorList[tabNo][7], "info" ); log("Routine SensorChange: Sensor ist direktverknuepft ? " + SensorList[tabNo][9] + " fuer Raum "+ room,"info"); log("Routine SensorChange: Sensor status ist ? " + SensorList[tabNo][7] + " fuer Raum "+ room,"info"); } // delay in Minuten ermitteln und erste Thermostat ID ermitteln for (var x in ControlTab ) { if (ControlTab[x][0] === room ) { delay = ControlTab[x][12]; IDThermostat = ControlTab[x][1]; break; } } // Fenster wurde geoeffnet if (VerschlussRaumStatus(room) === true ) { // Mindestens ein Fenster ist geoeffnet setOwnState(path + "." + room + ".RaumStatusVerschluss", true); // Raum ist geoeffnet SaveStatus("Fenster", room,false); // "Verschluss offen TemperaturAbsenkung gesetzt"; if (SensorList[tabNo][9] === false ) { // Sensor ist nicht direktverknuepft if (debug) { log("Routine SensorChange : Raum " + room + " nicht direkt verknuepfter Sensor - Verschluss offen es Temp wird abgesenkt", "info"); } SetTemp(room, VerschlussAbsenkungsGrenze, IDThermostat, false); writelog(room, id, "Routine SensorChange: Sensor nicht Direktverknuepft Sensor auf geoeffnet - ID - Temp automatisch auf Absenkung" + id + " Raum " + room +" auf "+ SensorList[tabNo][7]) ; } // endif Sensor ist nicht direktverknüpft if (SensorList[tabNo][9] === true) { // Sensor ist direktverknuepft writelog(room, id, "Routine SensorChange: Sensor Direktverknuepft Sensor auf geoeffnet - ID - Absenktemp gesetzt" + id + " Raum " + room +" auf "+ SensorList[tabNo][7]) ; if (debug) { log("Routine SensorChange : Raum " + room + " direkt verknuepfter Sensor - Verschluss offen es Temp wird vom Thermostat abgesenkt", "info"); } } } // Fenster wurde geschlossen if (VerschlussRaumStatus(room) === false ) { // Raum ist geschlossen setOwnState(path + "." + room + ".RaumStatusVerschluss", false); // Raum ist geschlossen SaveStatus("Heizplan", room,false); // Meldung loeschen if (SensorList[tabNo][9] === false ) { // Sensor ist nicht direktverknuepft writelog(room, id, "Routine SensorChange: Sensor nicht Direktverknuepft Sensor auf geschlossen - ID - Temp wird neu gesetzt" + id + " Raum " + room +" auf "+ SensorList[tabNo][7]) ; if(debug) { log("Routine SensorChange: Delay Time nach Schlessen des nicht direkt verknüpften Sensors gesetzt","info"); } } else { writelog(room, id, "Routine SensorChange: Sensor Direktverknuepft Sensor auf geoeffnet - ID - Temp wird neu gesetzt" + id + " Raum " + room +" auf "+ SensorList[tabNo][7]) ; if (debug) { log("Routine SensorChange : Raum " + room + " nicht verknuepfter Sensor - Verschluss geschlossen - Thermostat passt die Temp an", "info"); } } SetRoomClosed(room,delay); // zeitstempel setzen wenn Fenster geschlossen wurde - delay wenn erforderlich } LoopDevices(room); // jetzt wieder normale Temp ermitteln } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion SensorStatCalc Setzt den Sensorsatus um in true oder false fuer Geraete die mehr Status zur Verfuegung stellen // ID ist der vollständige Pfad zum Datenpunkt des Sensors // DEVType ist bei HM Geräten die Geräte bezeichnung also z.B. HM-CC-DN etc //----------------------------------------------------------------------------------------------------- function SensorStatCalc(id, devtype) { var SensorStatus = getState(id).val; if (SensorStatus === true || SensorStatus === false ) { if ( debug ) { log("Routine SensorStatCalc: Sensorstatus ist "+ SensorStatus + " fuer devtype = " + devtype + " und id "+ id); } return SensorStatus; } // handelt es sich um einen HM Sensor mit anderen Status als true oder false ? if (devtype !== "NoneHM") { for (var x in SensorTypeTab ) { if (devtype == SensorTypeTab[x][1]) { if (SensorStatus == SensorTypeTab[x][6] ) { if (debug ) {log("Routine SensorStatCalc: Sensorstatus ist geschlossen fuer devtype = " + devtype + " und id "+ id); } return false; } else { if (debug ) {log("Routine SensorStatCalc: Sensorstatus ist geoeffnet fuer devtype = " + devtype + " und id "+ id); } if (debug ) {log("Routine SensorStatCalc: verglichen wurden SensorStatus " + SensorStatus + " und Eintrag SensorTypeTab " + SensorTypeTab[x][6]); } return true; } } } } // HM check // jetzt checken ob der NoneHM Sensor geschlossen oder geöffnet ist if (devtype === "NoneHM") { for (var x in NoneHMSenorTab ) { if (id === NoneHMSenorTab[x][1] + "." + NoneHMSenorTab[x][2] + "." + NoneHMSenorTab[x][3]) { if (SensorStatus == NoneHMSenorTab[x][4] ) { if (debug ) { log("Routine SensorStatCalc: Sensorstatus ist geschlossen fuer devtype = " + devtype + " und id "+ id); } return false; } else { if (debug ) { log("Routine SensorStatCalc: Sensorstatus auf geoeffnet fuer devtype = " + devtype + " und id "+ id); } return true; } } } } // Ende NoneHM check log("Routine SensorStatCalc: Sensorstatus fuer " + id + " und devtype " + devtype + " Logik nicht implementiert - Status war " + SensorStatus , "info"); return false; // fall back wenn keine Bedingung zutrifft = nicht implementierte Logik } // endfunction //----------------------------------------------------------------------------------------------------- // Funktion ManAdjustments checkt ob eineThermostat/Raum Temperatur manuell angpasst wurde //----------------------------------------------------------------------------------------------------- function ManAdjustments(room, id) { var ActiveRoomProfile = ActiveProfile(room); // Ermittlung des aktiven Raumprofilsfunction ManAdjustments(room, id) { var ViewManDuration = getState(path + "." + room +".View_Manual_Temp_Duration").val; // Dauer der manuellen Aenderung var ViewManValidity = getState(path + "." + room +".View_ManTemp_Validity").val; // Errechnete Gueltigkeit als Ende Zeit var ViewManValue = Calculate_SollTemp(getState(path + "." + room +".View_Manually_Adjusted").val, "SetTemp"); // Check fuer eine manuelle Temp Aenderung aus dem view var SourceManValue = Calculate_SollTemp(getState(path + "." + room +".Source_Manually_Adjusted").val, "SetTemp"); // Check fuer eine manuelle Temp Aenderung aus dem view var ManAdjTimeStamp = getState(path + "." + room +".Source_TimeStamp").val; // Zeitstempel fall eine manuelle korrektur schon aktiv ist var last_Soll_Temp = getState(path + "." + room +".Source_Last_Temp").val; // Letzte gespeicherte Soll Temperatur var currentSollTemp = getState(id).val; // Ist die Geraete SollTemp ungleich der zuletzt gespeicherten Soll Temp ? // Hilfsrechnung um mehrere Abfragen von overrule zu vermeiden var NewCurrSollTemp = SelectSwitchTime (room, ActiveRoomProfile, "CurrSollTemp"); // Ist die Geraete SollTemp ungleich der zuletzt gespeicherten Soll Temp ? vor overrule var NewCurrTimeSlot = SelectSwitchTime (room, ActiveRoomProfile, "CurrSlot"); // ermittellt den aktuellen Timeslot für zu erwartende Solltemperatur - vor overrule var NextTimeSlot = SelectSwitchTime (room, ActiveRoomProfile, "NextSlot"); // ermittellt den nächsten Timeslot für die nächste zu erwartende Solltemperatur - vor overrule var NextSollTemp = SelectSwitchTime (room, ActiveRoomProfile, "NextSollTemp"); // ermittellt die nächste zu erwartende Solltemperatur - vor overrule var OverruleSoll = OverruleSollTemp (room,ActiveRoomProfile,20,id) - 20; // Trick um den Deltawert aus overrule zu erhalten - der kann positiv oder negativ sein - vor overrule // Hilfsrechnung ende ende NewCurrSollTemp = NewCurrSollTemp + OverruleSoll; // Ist die Geraete SollTemp ungleich der zuletzt gespeicherten Soll Temp ? nach overule NewCurrTimeSlot = NewCurrTimeSlot + OverruleSoll; // ermittellt den aktuellen Timeslot für zu erwartende Solltemperatur nach overule NextTimeSlot = NextTimeSlot + OverruleSoll; // ermittellt den nächsten Timeslot für die nächste zu erwartende Solltemperatur nach overule NextSollTemp = NextSollTemp + OverruleSoll; // ermittellt die nächste zu erwartende Solltemperatur nach overule var ActTime = formatDate(new Date(),"YYYY/MM/DD SS:mm:ss"); // Formatierte Aktuelle Zeit var ActTimeMilliSek = new Date(ActTime).getTime(); // Aktuelle Zeit in Millisekunden seit 1970 var bisTime = getState(path + "." + room + ".View_ManTemp_Validity").val; // gespeicherte Bistime var bisSetTimeMilliSek = 0; var lastSetTimeMilliSek = 0; var tillSetTimeMilliSek = 0; ; // Restdauer der manuellen Temperatur var ChckAbsenkung = false; var SoftBoostSchalter = getState(path + "." + room + ".SoftBoostActive").val; // SoftBoost Switch var SoftBoostDauer = getState(path + "." + room + ".SoftBoostDuration").val; // SoftBoost Dauer in Minuten var SoftBoostGueltigkeit = getState(path + "." + room + ".SoftBoostValidity").val; // SoftBoost Gueltigkeit bis - oder init var SoftBoostTime = getState(path + "." + room + ".SoftBoostValidity").val; // Zeitstempel bei SoftBoost Aktiv // Zeitberechnungen fuer manuelle Gültigkeiten lastSetTimeMilliSek = ActTimeMilliSek; bisSetTimeMilliSek = lastSetTimeMilliSek + ViewManDuration * 60 * 1000; if (trace) { log("Routine ManAdjustments Trace: estarted fuer Raum " + room+ " und id " + id ,"info"); } if (ManDebug) { log("Routine Manadj: errechnete SollTemp ist NewCurrSollTemp " + NewCurrSollTemp + " fuer Raum " + room, "info"); log("Routine Manadj: errechneter TimeSlot ist NewCurrTimeSlot " + NewCurrTimeSlot, "info"); log("Routine Manadj: momentane SollTemp ist currentSollTemp " + currentSollTemp + " fuer ID " + id, "info"); log("Routine Manadj: errechnete Next SollTemp ist NextSollTemp " + NextSollTemp, "info"); log("Routine Manadj: errechneter Next Timeslot ist NextTimeSlot " + NextTimeSlot, "info"); log("Routine Manadj: ViewManValue ist " + ViewManValue, "info"); log("Routine Manadj: SourceManValue ist " + SourceManValue, "info"); log("Routine Manadj: ViewManValidity ist " + ViewManValidity, "info"); log("Routine Manadj: last_Soll_Temp ist " + last_Soll_Temp, "info"); log("Routine Manadj: last_Soll_Temp ist " + last_Soll_Temp, "info"); log("Routine Manadj: bisTime ist " + bisTime+ " fuer Raum " + room, "info"); } Source_NextSollTemp = NextSollTemp; // Zeistempel für diesen Programmlauf setzen Source_last_Program_Run = formatDate(new Date(),"DD/MM/JJJJ SS:mm:ss"); setOwnState(path + "." + room + ".Source_last_Program_Run", Source_last_Program_Run); // Vermeidung von mehrfach Ausführungen - Tabelleneintrag vornehmen mit aktueller Zeit der Ausführung LastRoomUpdateTime(room, "push"); // die aktuelle Zeit wird in das Array eingetragen um doppelte Ausführungen zu vermeiden, falls die SollTemp geaendert wird. // Erster Abschnitt der manuellen Tempermittlung ermittelt Bedingungen, die nicht zu einer manuellen Temperatur führen // Pruefen ob ein FEnster geoeffnet ist if (VerschlussRaumStatus(room) === true ) { // Mindestens ein Fenster ist geoeffnet // setOwnState(path + "." + room + ".RaumStatusVerschluss", true); // Raum ist geoeffnet SaveStatus("Fenster", room,false); // "Verschluss offen TemperaturAbsenkung gesetzt"; if (debug) { log("Routine ManAdjustments : Raum " + room + " ist geoeffnet ", "info"); } Source_ManTempSet = VerschlussAbsenkungsGrenze; // if (!Check_SensorDV(room) ) { // einer der geoeffneten Sensoren ist nicht direktverknüpft // SetTemp(room, VerschlussAbsenkungsGrenze, id, false); // } // writelog(room, id, "Routine ManualAdjustments: Raum Geoeffnet - Temp wird auf Absenkung gesetzt" + id + " Raum " + room) ; // if (debug) { log("Routine SensorChange : Raum " + room + " Verschluss offen es Temp wird vom Thermostat abgesenkt", "info"); } return true; } // entspricht die aktuelle Temp des Thermostates der Absenktemperatur bei Fenster offen ? if (currentSollTemp === VerschlussAbsenkungsGrenze) { if (ManDebug) { log("Tempabsenkung erkannt fuer Raum "+ room,"info"); } ChckAbsenkung = true; } // Allgemeine Bedingungen bei denen zunächst nur prinzipiell ermittelt wird ob evt eine manuelle Temp vorliegt var manuellRelevant = false; if (ManAdjTimeStamp !== "init") manuellRelevant = true; if (last_Soll_Temp !== currentSollTemp) manuellRelevant = true; if (ViewManValue > 0) { if (last_Soll_Temp !== ViewManValue) manuellRelevant = true; if (ViewManValue !== currentSollTemp) manuellRelevant = true; } if (debug) { log("Routine ManAdjustments: Manuell Relevant?: " + manuellRelevant, "info"); } if (ManAdjTimeStamp !== "init") manuellRelevant = true; if (last_Soll_Temp !== currentSollTemp) manuellRelevant = true; if (ViewManValue > 0) { if (last_Soll_Temp !== ViewManValue) manuellRelevant = true; if (ViewManValue !== currentSollTemp) manuellRelevant = true; } if (debug) { log("Routine ManAdjustments: Manuell Relevant?: " + manuellRelevant, "info"); } if (ManTimeouts[room]) { clearTimeout(ManTimeouts[room]); ManTimeouts[room] = null; } // Delaybearbeitung - Nach schliessen eines Fensters wird abgewartet bis der Delay beendet ist (Einstellung in der Thermostattabelle) var DelayTime = CheckDelay(room); // gibt es einen delay ? 0 = kein Delay - Delays werden in der ThermostatTypeTab eingetragen und bei schliessen eines Raumes aktiviert if (DelayTime > 0 ) { // es gibt einen aktiven Delay des Raumes var RestDelay = DelayTime - ActTimeMilliSek; ManTimeouts[room] = setTimeout(TriggerUpdate, RestDelay, room, true,id); // Wiederherstellen des Delays mit der Restzeit - geht irgendwie verloren if (ManDebug) { log("Routine ManAdjustments: Warten bis Ablauf DelayTime - geplant fuer Raum "+ room + " um " + formatDate(DelayTime,"YYYY/MM/DD SS:mm:ss"),"info"); } return true; // mache nichts weiter und warte auf Ablauf des Delays } //....Ende Delaybearbeitung // SoftBoost Bearbeitung //SoftBoost läuft aber Raum wurde geoeffnet if ( ChckAbsenkung === true ) { if (SoftBoostSchalter === true || SoftBoostGueltigkeit !== "init" ) { // SoftBoost läuft if (ManDebug) { log ("Boost läuft und Absenkung erkannt ","info"); } // jetzt wieder den timeout fuer den Softboost setzen, der wurde nämlich durch die gesetzte Absenkung gelöscht tillSetTimeMilliSek = new Date(SoftBoostTime).getTime() - new Date().getTime(); if (tillSetTimeMilliSek > 0) { // SoftBoost Time noch gueltig if (ManDebug) { log("Routine ManAdjustments: Restlaufzeit der SoftBoost Zeit "+ tillSetTimeMilliSek,"info"); } ManTimeouts[room] = setTimeout(TriggerUpdate, tillSetTimeMilliSek, room, true,id); } else { setOwnState(path + "." + room + ".SoftBoostValidity", "init"); // Initilize setOwnState(path + "." + room + ".SoftBoostActive", false); // Initilize } return true; // zurück und keine neue Tempfindung } } // Ende Raum geoeffnet // SoftBoost neu gestartet if (SoftBoostGueltigkeit === "init" && SoftBoostSchalter) { // SoftBoost wurde gerade gestartet if(!ChckAbsenkung) { // nur wenn keine TempAbsenkung bisSetTimeMilliSek = ActTimeMilliSek + SoftBoostDauer * 60 * 1000; // Bis Zeit der Gueltigkeit der manuellen Aenderung in Millisekunden setOwnState(path + "." + room + ".SoftBoostValidity", formatDate(bisSetTimeMilliSek, "YYYY/MM/DD SS:mm:ss")); // Zeitstempel mit der aktuellen Zeit versehen SetTemp(room, SoftBoostTemp, id, false); // setze Temp auf BoostTemp ManTimeouts[room] = setTimeout(TriggerUpdate, SoftBoostDauer * 60 * 1000, room, true,id); if (debug) { log("Routine ManAdjustments: SoftBoost neu gesetzt für " + room + " ist " + formatDate(bisSetTimeMilliSek+1000, "YYYY/MM/DD SS:mm:ss")); } return true; // zurück und keine neue Tempfindung } } // SoftBoost abgelaufen ? lastSetTimeMilliSek = new Date(SoftBoostGueltigkeit).getTime(); // gespeicherte Von-Zeit wenn SoftBoost gestartet wurde in Millisekunden seit 1970 if (SoftBoostSchalter && SoftBoostGueltigkeit !== "init") { // SoftBoost läuft if (lastSetTimeMilliSek <= ActTimeMilliSek) { // die SoftBoost Zeit ist abgelaufen if (debug) { log("Routine ManAdjustments: SoftBoost abgelaufen","info"); } setOwnState(path + "." + room + ".SoftBoostValidity", "init"); // Initilize setOwnState(path + "." + room + ".SoftBoostActive", false); // Initilize SoftBoostSchalter = false; if (ManDebug) { log("SoftBoost Bedingung für reaktiverung einer manuellen Temp erfüllt - Raum "+ room,"info"); } if (ManTempReset(room,bisTime,ViewManValue,id) === true ) { // reaktivierung manuelle aenderung und Zeittrigger setzen return true; // zurück und keine neue Tempfindung } else { return false; // zurück und neue Tempfindung } } // endif lastsettimemillisek } // endif softboostschalter // SoftBoost manuell beendet if (SoftBoostSchalter === false && SoftBoostGueltigkeit !== "init") { // SoftBoost läuft wurde aber beendet setOwnState(path + "." + room + ".SoftBoostValidity", "init"); // Initilize if (debug) { log("Routine ManAdjustments: SoftBoost manuell beendet","info"); } if (ManTempReset(room,bisTime,ViewManValue,id) === true ) { // reaktivierung manuelle aenderung und Zeittrigger setzen return true; // zurück und keine neue Tempfindung } else { return false; // zurück und neue Tempfindung } } if (SoftBoostSchalter) { // SoftBoost läuft noch - abwarten log("Softboost fuer Raum " + room + " läuft noch - abwarten", "info"); if ( ViewManValue > 0 && ManAdjTimeStamp === "init") { // neue manuelle Temp während SoftBoost if (ManDebug) { log("Softboost fuer Raum " + room + " läuft noch - Berechnung der Gültigkeit der neuen manuellen Temp","info"); } setOwnState(path + "." + room + ".Source_TimeStamp", ActTime); // Zeitstempel mit der aktuellen Zeit versehen setOwnState(path + "." + room + ".View_ManTemp_Validity", formatDate(bisSetTimeMilliSek, "YYYY/MM/DD SS:mm:ss")); // Zeitstempel fuer die manuelle Gueltigkeit // jetzt wieder den timeout fuer den Softboost setzen, der wurde nämlich durch die gesetzte manuelle Temp gelöscht tillSetTimeMilliSek = new Date(SoftBoostTime).getTime() - new Date().getTime(); if (tillSetTimeMilliSek > 0) { // SoftBoost Time noch gueltig if (ManDebug) { log("Routine ManAdjustments: Restlaufzeit der SoftBoost Zeit "+ tillSetTimeMilliSek,"info"); } ManTimeouts[room] = setTimeout(TriggerUpdate, tillSetTimeMilliSek, room, true,id); } else { setOwnState(path + "." + room + ".SoftBoostValidity", "init"); // Initilize setOwnState(path + "." + room + ".SoftBoostActive", false); // Initilize } } return true; // zurück und keine neue Tempfindung } // Ende SoftBoost // Wenn im View die Gültigkeit kleiner Null ist wird jegliche manuelle Temp zurückgesetzt if (ViewManDuration < 0 ) { // die Dauer der manuellen Aenderung ist auf kleiner null gesetzt . das heisst, dass keine manuellen Aenderungen zugelassen sind if (ManDebug) { log("View kleiner Null - wird zurückgesetzt ","info"); } Source_ManualAdjustment = "Manuelle Temperatur Erkennung ist ausgeschaltet"; if (ManAdjTimeStamp !== "init" || ViewManValidity !== "init" || ViewManValue > 0) { InitilizeManChange(room); // Manuelle Aenderungen zuruecksetzen Source_ManualAdjustment = "Manuelle Temperatur Erkennung initialisiert und ausgeschaltet"; } if (debug) { log("Routine ManAdjustments: " + Source_ManualAdjustment, "info"); } return false; } // jetzt prüfen ob evt eine manuelle Temp gesetzt werden soll die der neuen SollTemp entspricht - Wenn dies der Fall ist, dann manuelle Temp ignorieren if (ViewManValue === NewCurrSollTemp ) { // Die aktuelle Solltemperatur des Thermostates ist gleich der Solltemperatur, die gleich gesetzt werden soll oder die view temp ist auf null gesetzt if (ManDebug) { log(" manuelle Temp = aktueller geplanter Temp wird zurückgesetzt","info"); } InitilizeManChange(room); // Manuelle Aenderungen zuruecksetzen if (debug) {log("Manuelle Temperatur entspricht der errechneten SollTemp - Manuelle Temp wird zurückgesetzt ", "info"); } writelog(room, id, "Manuelle Temperatur entspricht der errechneten SollTemp - Manuelle Temp wird zurückgesetzt "); return false; // gehe weiter mit der normalen Temp Determination } // Ende manuelle Temp = errechnete SollTemp lastSetTimeMilliSek = ActTimeMilliSek; bisSetTimeMilliSek = lastSetTimeMilliSek + ViewManDuration * 60 * 1000; // Duration 999 = Sonderfall = Undendliche Dauer der Manuellen Temp if (ViewManDuration == 999) { // Duration 999 = Sonderfall = Undendlich bisSetTimeMilliSek = ActTimeMilliSek + 315360000000; // + 10 jahre } // Wenn es bereits eine Manuelle Temp gibt, dann wird jetzt Die Biszeit in Millisek berechnet if (ManAdjTimeStamp !== "init") { // eine manuelle Temperatur wurde bereits gespeichert lastSetTimeMilliSek = new Date(ManAdjTimeStamp).getTime(); // gespeicherte Von-Zeit der manuellen Aenderung in Millisekunden seit 1970 bisSetTimeMilliSek = lastSetTimeMilliSek + ViewManDuration * 60 * 1000; // Bis Zeit der Gueltigkeit der manuellen Aenderung in Millisekunen } // wenn die manuelle Aenderung bei einem Slotwechsel zurückgesetzt werden soll wird das Ende des Slotwechsels berechnet if (ViewManDuration == 0 && ManAdjTimeStamp === "init" && manuellRelevant) { // "==="" ergibt Probleme bei ViewManDuration Aenderungen im view - nummber- string problem bisSetTimeMilliSek = SelectSwitchTime (room, ActiveRoomProfile, "CurrSlotEnde"); bisSetTimeMilliSek = bisSetTimeMilliSek ; // bis time entspricht genau dem slotwechsel if (debug) { log("Routine ManAdjustments: Die manuelle gesetzte Temperatur wird zurückgesetzt um " + formatDate(bisSetTimeMilliSek, "YYYY/MM/DD SS:mm:ss", "info")); } } // Ermittlung der restlichen Zeit bis zum Ablauf der manuellen Temperatur tillSetTimeMilliSek = bisSetTimeMilliSek - new Date().getTime(); //tilSetTimeMilliSek ist die Restdauer der manuellen Temperatur // 0. Fall Eine manuelle Tempertur war eingestellt und muss wieder aktiviert werden+ if (ManAdjTimeStamp !== "init" && NewCurrSollTemp !== ViewManValue && ViewManValue > 0 && ChckAbsenkung !== true ) { // Manuelle Temp ist eingestellt . Die gegenwärtige Temp ist aber anders if (ManDebug) { log("Fall 0 : Bedingung fuer Reaktivierung der manuellen Temp erfüllt - Raum "+ room ,"info"); } if (ManDebug) { log("Gueltigkeit "+ ManAdjTimeStamp +" NewCurrSollTemp "+ NewCurrSollTemp + " ViewManValue "+ ViewManValue + " Raum " + room,"info"); } if( currentSollTemp !== ViewManValue && TriggerChangeThermostat ) { // es scheint, dass die Temperatur am Thermostat sich geändert hat setOwnState(path + "." + room + ".View_Manually_Adjusted", Calculate_SelectValueWert(currentSollTemp, "SetTemp")); if (ManTempReset(room,bisTime,currentSollTemp,id) === true ) { // reaktivierung manuelle aenderung und Zeittrigger setzen return true; // zurück und keine neue Tempfindung } else { return false; // zurück und neue Tempfindung } } if (SourceManValue !== ViewManValue && ViewManValue > 0 && !TriggerChangeThermostat ) { // Im View wurde eine manuelle Temperatur eingetragen - die kommt nicht vom Thermostat if (ManDebug) { log(" Fall 0 - Korrektur der manuellen Temp im View trifft zu", "info"); } Source_ManualAdjustment = "Manuelle Temperatur wurde im View auf neu gesetzt "; SetTemp(room, ViewManValue, id, false); Source_ManTempSet = ViewManValue; setOwnState(path + "." + room + ".Source_Manually_Adjusted", Calculate_SelectValueWert(ViewManValue, "SetTemp")) ; setOwnState(path + "." + room + ".View_Manually_Adjusted", Calculate_SelectValueWert(ViewManValue,"SetTemp")) ; if (tillSetTimeMilliSek > 0) { ManTimeouts[room] = setTimeout(TriggerUpdate, tillSetTimeMilliSek + 1000, room, true,id); if (debug) {log("Routine ManAdjustments: Timeout für Delaytime gesetzt für " + room + " ist " + tillSetTimeMilliSek);} } writelog(room, id, "0. Fall im View wurde eine Temperatur gesetzt die von der vorher gesetzten Temperatur abweicht " + ViewManValue); return true; } if (ManTempReset(room,bisTime,ViewManValue,id) === true ) { // reaktivierung manuelle aenderung und Zeittrigger setzen return true; // zurück und keine neue Tempfindung } else { return false; // zurück und neue Tempfindung } } // Es liegt eine manuelle Temperatur vor - entweder ist sie gerade erst festgestellt oder sie gab es schon und deren weiteren Behandlung wird überprüft und ausgeführt // 1. Fall eine manuelle Korrektur am Thermostat ist erkannt - Sie wurde gerade erst eingestellt if (currentSollTemp !== last_Soll_Temp && ManAdjTimeStamp === "init" && ChckAbsenkung !== true) { // Temperturdifferenz erkannt und Zeitstempel noch nicht gesetzt if (ManDebug) { log(" Fall 1 trifft zu", "info"); } Source_ManualAdjustment = "Manuelle Temperatur Verstellung am Thermostat auf " + currentSollTemp + " " + "erkannt"; SetTemp(room, currentSollTemp, id, false); Source_ManTempSet = currentSollTemp; setOwnState(path + "." + room + ".Source_TimeStamp", ActTime); // Zeitstempel mit der aktuellen Zeit versehen setOwnState(path + "." + room + ".View_ManTemp_Validity", formatDate(bisSetTimeMilliSek, "YYYY/MM/DD SS:mm:ss")); // Zeitstempel mit der aktuellen Zeit versehen setOwnState(path + "." + room + ".Source_Manually_Adjusted", Calculate_SelectValueWert(currentSollTemp, "SetTemp")); setOwnState(path + "." + room + ".View_Manually_Adjusted", Calculate_SelectValueWert(currentSollTemp, "SetTemp")); if (debug) { log("1.Fall " + Source_ManualAdjustment, "info"); } if (tillSetTimeMilliSek > 0) { ManTimeouts[room] = setTimeout(TriggerUpdate, tillSetTimeMilliSek + 1000, room, true,id); if (debug) {log("Routine ManAdjustments: Timeout für Delaytime gesetzt für " + room + " ist " + tillSetTimeMilliSek);} } writelog(room, id, "1. Fall - manuelle Temp erkannt auf " + currentSollTemp); return true; } // 2. Fall eine Temperatur wurde im View eingegeben - eine vorherige Manuelle Temp gibt es nicht if (ManAdjTimeStamp === "init" && ViewManValue > 0) { // Im View wurde eine manuelle Temperatur eingetragen if (ManDebug) { log(" Fall 2 trifft zu", "info"); } Source_ManualAdjustment = "Manuelle Temperatur Verstellung im View auf " + ViewManValue + " " + "erkannt"; SetTemp(room, ViewManValue, id, false); Source_ManTempSet = ViewManValue; setOwnState(path + "." + room + ".Source_TimeStamp", ActTime); // Zeitstempel mit der aktuellen Zeit versehen setOwnState(path + "." + room + ".View_ManTemp_Validity", formatDate(bisSetTimeMilliSek, "YYYY/MM/DD SS:mm:ss")); // Zeitstempel mit der aktuellen Zeit versehen setOwnState(path + "." + room + ".Source_Manually_Adjusted", Calculate_SelectValueWert(ViewManValue, "SetTemp")); if (debug) { log("2.Fall " + Source_ManualAdjustment, "info"); } if (tillSetTimeMilliSek > 0) { ManTimeouts[room] = setTimeout(TriggerUpdate, tillSetTimeMilliSek + 1000, room, true,id); if (debug) {log("Routine ManAdjustments: Timeout für Delaytime gesetzt für " + room + " ist " + formatDate(tillSetTimeMilliSek, "YYYY/MM/DD SS:mm:ss"));} } writelog(room, id, "2. Fall eine Temperatur wurde im View eingegeben " + ViewManValue); return true; } // 3. Fall im View wurde eine Temperatur gesetzt die von der vorher gesetzten Temperatur abweicht - overrule durch view if (ManAdjTimeStamp !== "init" && SourceManValue !== ViewManValue && ViewManValue > 0) { // Im View wurde eine manuelle Temperatur eingetragen if (ManDebug) { log(" Fall 3 trifft zu", "info"); } Source_ManualAdjustment = "Manuelle Temperatur wurde im View auf neu gesetzt "; SetTemp(room, ViewManValue, id, false); Source_ManTempSet = ViewManValue; setOwnState(path + "." + room + ".Source_Manually_Adjusted", Calculate_SelectValueWert(ViewManValue, "SetTemp")) ; setOwnState(path + "." + room + ".View_Manually_Adjusted", Calculate_SelectValueWert(ViewManValue,"SetTemp")) ; if (debug) { log("3.Fall " + Source_ManualAdjustment, "info"); } if (tillSetTimeMilliSek > 0) { ManTimeouts[room] = setTimeout(TriggerUpdate, tillSetTimeMilliSek + 1000, room, true,id); if (debug) {log("Routine ManAdjustments: Timeout für Delaytime gesetzt für " + room + " ist " + tillSetTimeMilliSek);} } writelog(room, id, "3. Fall im View wurde eine Temperatur gesetzt die von der vorher gesetzten Temperatur abweicht " + ViewManValue); return true; } // 4. Fall im View wurde die Temperatur auf 0 gesetzt - das fuehrt zum reset der manuellen Temperatur if (ManAdjTimeStamp !== "init" && ViewManValue === 0 && SourceManValue > 0) { // Im View wurde eine manuelle Temperatur eingetragen if (ManDebug) { log(" Fall 4 trifft zu", "info"); } Source_ManualAdjustment = "4. Fall - Manuelle Temperatur wurde im View auf Null gesetzt - Loeschen der manuellen Temperatur"; InitilizeManChange(room); // Manuelle Aenderungen zuruecksetzen if (debug) { log("4.Fall " + Source_ManualAdjustment, "info"); } writelog(room, id, "4. Fall im View wurde die Temperatur auf 0 gesetzt "); return false; } /* ubgedeckt durch Fall 0 // 5. Fall Die Manuelle Temperatur wurde am Thermostat veraendert if (currentSollTemp !== SourceManValue && ManAdjTimeStamp !== "init" && ChckAbsenkung !== true) { if( currentSollTemp === NewCurrSollTemp ) { // Die aktuelle Solltemperatur des Thermostates ist gleich der Solltemperatur, die gleich gesetzt werden soll oder die view temp ist auf null gesetzt if (ManDebug) { log(" Fall 5 trifft zu", "info"); } if (ManAdjTimeStamp !== "init" || ViewManValidity !== "init" || ViewManValue > 0 || ViewManValue > 0) { InitilizeManChange(room); // Manuelle Aenderungen zuruecksetzen Source_ManualAdjustment = "5. Fall - Manuelle Temperatur zurückgesetzt"; if (debug) { log("Routine ManAdjustments: " + Source_ManualAdjustment, "info"); } } if (debug) { log("keine ManAdjustments festgestellt fuer Raum "+room, "info"); } writelog(room, id, "5. Fall Die Manuelle Temperatur wurde am Thermostat veraendert " + ViewManValue); return true; // zurück } Source_ManualAdjustment = "Manuelle Temperatur Verstellung im Termostat auf " + currentSollTemp + " " + "erkannt"; setOwnState(path + "." + room + ".Source_Manually_Adjusted", Calculate_SelectValueWert(currentSollTemp, "SetTemp")); setOwnState(path + "." + room + ".View_Manually_Adjusted", Calculate_SelectValueWert(currentSollTemp, "SetTemp")); Source_ManTempSet = currentSollTemp; if (debug) { log(Source_ManualAdjustment, "info"); } if (tillSetTimeMilliSek > 0) { ManTimeouts[room] = setTimeout(TriggerUpdate, tillSetTimeMilliSek + 1000, room, true,id); if (debug) {log("Routine ManAdjustments: Timeout für Delaytime gesetzt für " + room + " ist " + tillSetTimeMilliSek);} } return true; } */ // 6. Fall - die Manuell gesetzte Temperatur wird auf abgelaufen ueberprueft if (currentSollTemp !== last_Soll_Temp && ManAdjTimeStamp !== "init" ) { // Die manuelle Temperatur ist - pruefen ob abgelauen if (ManDebug) { log(" Fall 6 trifft zu", "info"); } // wenn abgelaufen, dann die manuelle Temperatur loeschen if (bisSetTimeMilliSek <= ActTimeMilliSek) { // die manuelle Zeit ist abgelaufen InitilizeManChange(room); // Manuelle Aenderungen zuruecksetzen Source_ManualAdjustment = "6a. Fall - Manuelle Temperatur abgelaufen um: " + ViewManValidity + " - zurueck zum Schedule"; if (debug) { log(Source_ManualAdjustment, "info"); } writelog(room, id, "6a Fall. Manuelle Temperatur abgelaufen um " + ViewManValidity); return false; } else { // Temperatur noch nicht abgelaufen if (ChckAbsenkung !== true ) { // wenn keine Temperaturabsenkung vorliegt Source_ManualAdjustment = "6b. Fall - Manuelle Temperatur noch aktuell - warten bis " + ViewManValidity + " - Temperatur ist " + currentSollTemp; Source_ManTempSet = currentSollTemp; if (debug) { log(Source_ManualAdjustment,"info"); } if (tillSetTimeMilliSek > 0) { ManTimeouts[room] = setTimeout(TriggerUpdate, tillSetTimeMilliSek + 1000, room, true,id); if (debug) {log("Routine ManAdjustments: Timeout für Delaytime gesetzt für " + room + " ist " + tillSetTimeMilliSek);} } writelog(room, id, "6b Fall. Manuelle Temperatur noch aktuell bis " + ViewManValidity); } else { SetTemp(room, SourceManValue, id, false); // alte manuelle Temperatur wieder einstellen Source_ManTempSet = SourceManValue; if (tillSetTimeMilliSek > 0) { ManTimeouts[room] = setTimeout(TriggerUpdate, tillSetTimeMilliSek + 1000, room, true,id); if (debug) {log("Routine ManAdjustments: Timeout für Delaytime gesetzt für " + room + " ist " + tillSetTimeMilliSek);} } writelog(room, id, "6c Fall. Manuelle Temperatur wieder eingestellt " + SourceManValue); return true; } } // endif Die manuelle Zeit ist abgelaufen } // check ob die manuelle Zeit abgelaufen ist // 7. Fall - die Manuell gesetzte Temperatur ist abgelaufen- der schedule hat gewechselt und entspricht der vorherigen manuellen Temperatur (Ausnahmesituation) if (ManAdjTimeStamp !== "init" ) { // Die manuelle Temperatur ist - pruefen ob abgelauen if (bisSetTimeMilliSek + cron * 60 * 1000 < ActTimeMilliSek) { if (ManDebug) { log(" Fall 7 trifft zu", "info"); } InitilizeManChange(room); // Manuelle Aenderungen zuruecksetzen Source_ManualAdjustment = "7. Fall - Manuelle Temperatur abgelaufen um: " + ViewManValidity + " - zurueck zum Schedule"; if (debug) { log(Source_ManualAdjustment, "info"); } writelog(room, id, "7. Fall - die Manuell gesetzte Temperatur ist abgelaufen"); return false; } } if (ManDebug) { log("nichts in den manuellen checks traf zu. zurück und neue Tempfindung Raum "+ room, "info"); } return false; } // Endfunction //----------------------------------------------------------------------------------------------------- //ab hier weitere wichtige Funktionen die von den Hauptfunktionen aufgerufen werden // 1. Overrule -- Behandelt globale Parameter und ggf davon abhaengige Temperaturanpassungen // 2 ExecuteTempDetermination -- Hauptroutine zur Findung und speichern der Solltemp abhängig vom schedule // 3. SelectSwitchTime -- Ermittlung die gültige Zeit des schedules (nicht Planungstag) // 4. ActiveProfile -- Bestimmt das aktive Raumprofil // 5. SetEventGlobalParameter -- ermittelt aufugrund von ICAL ggf gueltige Events // 6. SetTemp -- Nur hier wird die Solltemperatur fuer die Thermostate gesetzt // 7. DetermineSchedule -- ist input fuer ExecuteTempDetermination und ermittelt den richtigen Planungstag (1 bis 8 ) SelectSwitchTime ermittelt die Zeit // 8. SyncThermostat -- Synchronisiert andere im Raum befindliche Thermostate wenn kein anderes zentrales Thermostat vorhanden ist // 9.CreateStates -- legt alle relevanten States fuer Raeume und profile an //----------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------- // Funktion ManTempReset - Reset Manuelle Aenderung und starte timer neu // wird genutzt um evt nicht abgelaufene Temperaturen wieder neu zu schedulen //----------------------------------------------------------------------------------------------------- function ManTempReset (room,bisTime,ViewManValue,id ) { var bisSetTimeMilliSek = new Date(bisTime).getTime(); var tillSetTimeMilliSek = bisSetTimeMilliSek - new Date().getTime(); if (tillSetTimeMilliSek > 0) { // manuelle Temp gueltig if (debug) { log("Routine ManTempReset: Restlaufzeit einer ggf man Aenderung "+ tillSetTimeMilliSek,"info"); } SetTemp(room, ViewManValue, id, false); Source_ManTempSet = ViewManValue; ManTimeouts[room] = setTimeout(TriggerUpdate, tillSetTimeMilliSek, room, true,id); return true // manuelle Aenderung eingestellt - keine neue Tempfindung } else { // manuelle Temp abgelaufen if (debug) { log("Routine ManTempReset: Manuelle Temp abgelaufen zurück zum Schedule","info"); } InitilizeManChange(room); // Manuelle Aenderungen zuruecksetzen Source_ManualAdjustment = "Manuelle Temperatur abgelaufen fuer Raum " + room + " - zurueck zum Schedule"; if (debug) { log(Source_ManualAdjustment, "info"); } writelog(room, id, "Manuelle Temperatur abgelaufen -Raum " + room ); return false; // neue Tempfindung } } // Endfunction //----------------------------------------------------------------------------------------------------- // Funktion OverruleSollTemp arbeitet nach der vorgegebenen Reihenfolge die globalen und Profilparameter ab // Ubersteuert die gefundene SollTemp aus dem schedule //----------------------------------------------------------------------------------------------------- function OverruleSollTemp (room,Profil,SollTempSched,id) { if (getState(StateHeizperiode).val === false ) { // keine Heizperiode SaveStatus("HeizperiodeAus", room,false); return SollTempSched; // also keine Veraenderung der SollTemp } for (var x in OverruleTab) { if (OverruleTab[x][0] === "UrlaubAnwesend") { // Wenn Urlaub dann check wie ein Feiertag eingestellt ist (Urlaub wie Feiertag) if (getState(StateUrlaubAnwesend).val) { if (getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_UrlaubWieFeiertag).val) { SaveStatus("UrlaubAnwesend", room,false); } // endif ist der Urlaub wie ein Feiertag ? } // endif Urlaubanwesend ist true } if (OverruleTab[x][0] === "UrlaubAbwesend" && getState(StateUrlaubAbwesenheit).val) { // Wenn Urlaub dann Absenkung bis Mindestemperatur if (Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_UrlaubAbsenkung).val, "CorrectTemp") !== 0) { // Absenkung geplant ? SollTempSched = SollTempSched - Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_UrlaubAbsenkung).val, "CorrectTemp"); if (SollTempSched < Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_MinimaleTemperatur).val,"SetTemp")) { // Minimaltemp zieht SollTempSched = Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_MinimaleTemperatur).val,"SetTemp"); SaveStatus("UrlaubAbwesend", room, true); // mit Mindesttemperatur return SollTempSched; } SaveStatus("UrlaubAbwesend", room,false); return SollTempSched; } } if (OverruleTab[x][0] === "Gaeste") { // Wenn Gaeste dann Anhebung um Profilparameter if (getState(StateGaesteDa).val && Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_GaesteAnhebung).val, "CorrectTemp") !== 0) { // Gaeste Anhebung muess ungleich 0 sein // nur wenn einen Anhebung geplant ist SollTempSched = SollTempSched + Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_GaesteAnhebung).val, "CorrectTemp"); if (SollTempSched < Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_MinimaleTemperatur).val, "SetTemp")) { SollTempSched = Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_MinimaleTemperatur).val, "SetTemp"); SaveStatus("Gaeste", room, true); // mit Mindesttemperatur return SollTempSched; } SaveStatus("Gaeste", room,false); return SollTempSched; } } if (OverruleTab[x][0] === "Abwesenheit") { // Wenn Abwesenheit dann Absenkung um Profilparameter if (AbsenkTempAbsolut === false ) { // relative Absenktemperatur ist eingestellt if (PresenceCheck(room) === false && Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_AbwesenheitAbsenkung).val, "CorrectTemp") !== 0) { // Absenkung muss ungleich 0 sein // nur wenn einen Anhebung geplant ist if(debug) { log("Routine Overrule Profil ist " + Profil + " Absenkung ist " + Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_AbwesenheitAbsenkung).val, "CorrectTemp") + " Raum ist " + room) } SollTempSched = SollTempSched - Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_AbwesenheitAbsenkung).val, "CorrectTemp"); if(SollTempSched < Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_MinimaleTemperatur).val, "SetTemp")) { // Mindesttemp erreicht SollTempSched = Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_MinimaleTemperatur).val, "SetTemp"); return SollTempSched; } return SollTempSched; } // endif Absenkung relevant und AbsenkTemp ist ungleich NULL } // endif relative AbsenkTemp if (AbsenkTempAbsolut === true ) { // relative Absenktemperatur ist eingestellt{ // absolute Absenktemperatur ist eingestellt if (PresenceCheck(room) === false && Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_AbwesenheitAbsenkung).val, "FullTemp") !== 0) { // Absenkung muss ungleich 0 sein if(debug) { log("Routine Overrule Profil ist " + Profil + " Absenkung ist " + Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_AbwesenheitAbsenkung).val, "SetTemp") + " Raum ist " + room) } SollTempSched = Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_AbwesenheitAbsenkung).val, "FullTemp"); return SollTempSched; } // endif Absenkung relevant und AbsenkTemp ist ungleich NULL } // endif absolute AbsenkTemp } // Endif Abwesenheitscheck if (OverruleTab[x][0] === "Party") { // Wenn Party dann Absenkung um Profilparameter if (getState(StatePartyjetzt).val && Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_PartyAbsenkung).val, "CorrectTemp") !== 0) { // Gaeste Anhebung muess ungleich 0 sein // nur wenn einen Anhebung geplant ist SollTempSched = SollTempSched - Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_PartyAbsenkung).val, "CorrectTemp"); if (SollTempSched < Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_MinimaleTemperatur).val, "SetTemp")) { SollTempSched = Calculate_SollTemp(getState(path + "." + room + "." + "Profil-" + Profil + "." + StatePP_MinimaleTemperatur).val, "SetTemp"); SaveStatus("Party", room, true); // mit Mindesttemperatur return SollTempSched; } SaveStatus("Party", room,false); return SollTempSched; } } } return SollTempSched; } // Ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion PresenceCheck checkt ob eine globale oder raumbezogenen Anwesenheit vorliegt // stellt An/Abwesenheit fest //----------------------------------------------------------------------------------------------------- function PresenceCheck(room) { var chckroom; var anwesend = true; // globale Anwesenheitserkennung aktiv `? if ( UseAnwesenheitserkennung) { // Anwesenheitscheck eingeschaltet if (getState(StateAnwesenheit).val === false) { // globale Anwesenheit auf false, dann ist keiner da SaveStatus("Abwesend", room,false); return false; } // endif checke flag } else { // globale anwesenheit nicht aktiv also zurück return true; } // hier geht es nur weiter wenn die globale Anwesenheit aktiviert und auf true steht if ( UseRaumAnwesenheit === false ) { // keine Anwesenheitsprüfung return true; } // die Raumanwesenheit ist aktiviert - also loop durch dir Räume for (var x in RaumAnwTAB ) { chckroom = RaumAnwTAB[x][0] chckroom = chckroom.replace(/\s/g, "_"); // Alle Leerzeichen aus der Raumbezeichnung entfernen if(chckroom !== "initial" && RaumAnwTAB[x][1] !== "initial" ) { // nur wenn nicht als initial gekennzeichnet if(chckroom === room) { // Raum in Tabelle gefunden if (getState(RaumAnwTAB[x][1]).val === true ) { // checke den Pfad aus der Raumtabelle - muss boolean sein true = anwesend return true ; // im Raum ist mindestens einer anwesend - es geht also zurück mit Raumanwesenheit } anwesend = false; // found wird false wenn es zwar konfigurierte Räume gibt aber keiner steht auf anwesend } } // endif check kein initial eintrag } // endfor loop ueber RaumAnwTAB // so ab hier kann es nur sein, dass entweder kein Eintrag vorhanden war oder niemand im Raum ist if(anwesend === false) { // Da nicht mindestens ein Wert auf true stand muss der rückgabewert false (abwesend sein) log("keiner da in Raum "+ room) SaveStatus("RaumAbwesend", room,false); return false; // im Raum sind alle abgemeldet } // es war kein Eintrag in der Raumtabelle - also mit true zurück da die globale anwesenheit zieht return true ; } // ende funktion PresenceCheck //----------------------------------------------------------------------------------------------------- // Funktion ExecuteTempDetermination checkt ob eineThermostat/Raum Temperatur manuell angpasst wurde //----------------------------------------------------------------------------------------------------- function ExecuteTempDetermination(roomName, id) { var ActiveRoomProfile; var ScheduledSollTemp; // Findung des aktuellen RaumProfiles ActiveRoomProfile = ActiveProfile(roomName); // Ermittlung des aktiven Raumprofils //die geplante Soll Temperatur aus dem Raumschedule aus dem aktuellen Profil ermitteln ScheduledSollTemp = SelectSwitchTime(roomName, ActiveRoomProfile, "CurrSollTemp",id); // Ermittlung der geplanten Solltemperatur if (debug) { log("Routine ExecuteTempDetermination: raum " + roomName + "Solltemp nach Switchtime: " + ScheduledSollTemp + " Findung " + Source_SchedulePoint, "info"); } // Schauen ob die ermittelte Temperatur angepasst werden muss z.B. party Gaeste etc ScheduledSollTemp = OverruleSollTemp(roomName, ActiveRoomProfile, ScheduledSollTemp, id); //Global und Profilparameter koennen den schedule uebersteuern if (debug) { Source_GlobalParameter = getState(path + "." + roomName + ".Source_Global_Parameter").val; log("Routine ExecuteTempDetermination: raum " + roomName + "Solltemp nach overrule: " + ScheduledSollTemp+ " Findung " + Source_GlobalParameter, "info"); } // jetzt die Temperatur dem Thermostat uebermitteln SetTemp(roomName, ScheduledSollTemp, id, true); // jetzt die Temperatur schalten if (NextSchedules[roomName]) { if (debug) { log("Schedule gelöscht für " + roomName, "info"); } clearSchedule(NextSchedules[roomName]); NextSchedules[roomName] = null; } // jetzt Delay-Sekunden ermitteln, um die Schedules nicht gleichzeitig auszuführen var delaysek=0; var z = 1; for (var roomCheck in rooms) { // loop ueber all Raeume roomCheck = roomCheck.replace(/\s/g, "_"); // Blanks durch unterstrich ersetzen if ( roomCheck === roomName) { delaysek = z * 2; // es werden alle 2 Sekunden ein schedule geplant also 2,4,6.... if (delaysek > 58) { // jetzt sind 58 Sekunden erreicht delaysek = (delaysek - 59) * 2 - 1 ; // also mit 1,3,5... Sekunden weiter planen } if ( z > 59) { // mehr als 59 Räume mit Thermostaten? wohl kaum delaysek = 0; } break; } z = z + 1 }//endfor roomcheck // jetzt die Cron Pattern bestimmen und einplanen var schedArr = Source_SchedulePoint.split("_"); // [0]=Mo, [1]=00:00:00 schedArr[1] = schedArr[1].substr(0,6) + delaysek; var nextSchedule = parseInt(schedArr[1].substr(6, 2), 10) + " " + parseInt(schedArr[1].substr(3, 2), 10) + " " + parseInt(schedArr[1].substr(0, 2), 10) + " * * *"; if (debug) { log("Setze Schedule für nächste Planzeit " + nextSchedule + " für Raum " + roomName, "info"); } NextSchedules[roomName] = schedule(nextSchedule, function() { if (debug) { log("Schedule Triggered für nächste Planzeit für Raum " + roomName, "info"); } TriggerUpdate(roomName,false,id); }); // Ende Funktion NextSchedules return ScheduledSollTemp } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion SelectSwitchTime Finde Schaltzeit im schedule und geplante Solltemperatur // Find nächst Schaltzeit // Finde nächste Solltemperatur //----------------------------------------------------------------------------------------------------- // Funktion = // NextSlot = finde nächste Schaltzeit // CurrSlot = finde aktuellen ZeitSlot // CurrSollTemp = finde aktuelle Solltemperatur des Slots // NextSollTemp = finde Solltemperatur des nächsten Slots // CurrSlotEnde = finde aktuellen Slot und sende die Zeit in Millisekunden für das Slotende zurück // function SelectSwitchTime(room, RaumProfil, Funktion,id) { if (Funktion !== "CurrSlot" && Funktion !== "NextSlot" && Funktion !== "CurrSollTemp" && Funktion !== "NextSollTemp" && Funktion !== "CurrSlotEnde") { log("Funktion unbekannt in fuction SelectSwitchTime Fumktion war " + Funktion, "info"); return 0 ; // Funktion unbekannt } // zuerst wird geprueft ob die Heizuperiode ausgeschaltet ist, In diesem Fall braucht kein Schedule ermittelt werden und die Sommer SollTemp wird zurueckgegeben if (getState(StateHeizperiode).val === false ) { // keine Heizperiode if ( Funktion === "CurrSollTemp" || Funktion === "NextSollTemp" ) { // nur fuer Temperaturermittlung. Der Slot muss trotzdem gefunden werden for(var x in ControlTab) { if ( id === ControlTab[x][1]) { // ID in der ControlTab gefunden return ControlTab[x][11]; // Die Solltemp für die Sommerperiode wird zurückgegeben (kommt aus der ThermostatTab) } } // endfor controltab } // endif Funktion } // endif keine Heizperiode // Folgende Verarbeitung findet den Scheduled Slot room = room.replace(/\s/g, "_"); // Alle Leerzeichen aus der Raumbezeichnung entfernen var currTime = formatDate(new Date(),"SS:mm:ss"); var TemperatureScheduledTemp = 0; var TemperatureScheduledSlot; var NextTemperatureScheduledTemp = 0; var NextTemperatureScheduledSlot; var TimeBisScheduled = " "; var TimeFrom = "00:00:00"; var y = 1; var NextSchedulePointer = 0; //var d = new Date(); var heute = currentDate(); var MilliSekHeute = 0; var MilliSekSlot; var weekday = new Date().getDay(); weekday = DetermineSchedule(room, weekday, RaumProfil); // tatsächlicher weekday wird uebersteuert, wenn ein "WieVortag" vorkommt for (var i = 1; i <= 6; i++) { // es gibt 6 schedules pro Tag TimeBisScheduled = getState(path + "." + room + ".Profil-" + RaumProfil + "." + Wochentag(weekday) + "_" + i + "_" + "bis").val; if (isTimeInRange(TimeFrom, TimeBisScheduled)) { Source_SchedulePoint = Wochentag(weekday) + "_" + TimeBisScheduled; if (Funktion === "CurrSollTemp" ) { TemperatureScheduledTemp = getState(path + "." + room + ".Profil-" + RaumProfil + "." + Wochentag(weekday) + "_" + i + "_" + "Temp").val; TemperatureScheduledTemp = Calculate_SollTemp(TemperatureScheduledTemp, "SetTemp"); if(debug) { log("Routine SelectSwitchTime - Aktuelle Solltemperatur ist " + TemperatureScheduledTemp + " fuer Raum " + room + " Raumprofil ist " + RaumProfil, "info"); } return TemperatureScheduledTemp; //exakte Schaltzeit gefunden - also raus jetzt } // endif currsolltemp if (Funktion === "CurrSlot" || Funktion === "CurrSlotEnde") { TemperatureScheduledSlot = Wochentag(weekday) + "_" + TimeBisScheduled; if( Funktion === "CurrSlot") { // errechne curr slot ende in Zeit WW_hh:mm:ss if(debug) { log("Routine SelectSwitchTime - Aktueller Zeitslot ist " + TemperatureScheduledSlot + " fuer Raum" + room + " Raumprofil ist " + RaumProfil, "info"); } return TemperatureScheduledSlot; // exakte Schaltzeit gefunden - also raus jetzt } if (Funktion === "CurrSlotEnde") { // errechne currslot ende in millisekunde var schedArr = TemperatureScheduledSlot.split("_"); MilliSekSlot = addTime(schedArr[1]).getTime(); if (schedArr[1] === "00:00:00") { MilliSekSlot += 24 * 60 * 60 * 1000; } //MilliSekSlot = parseInt(TemperatureScheduledSlot.substr(3, 2), 10) * 1000 * 60 * 60; // Stunden //MilliSekSlot = MilliSekSlot + parseInt(TemperatureScheduledSlot.substr(6, 2), 10) * 1000 * 60; // Minuten addieren //MilliSekSlot = MilliSekSlot + parseInt(TemperatureScheduledSlot.substr(9, 2), 10) * 1000; // Sekunden addieren //MilliSekSlot = MilliSekSlot + heute.getTime(); return MilliSekSlot; } break; } // endif Currslot if (Funktion === "NextSollTemp" || Funktion === "NextSlot" ) { NextSchedulePointer = i; // merken der slotnummer break; // schleife verlassen } } // endif timeInRange } // endfor if (NextSchedulePointer < 6 ) { // Wenn noch mindestens ein Slot am Selben Tag zur Verfügung steht NextSchedulePointer = NextSchedulePointer + 1; } if (TimeBisScheduled === "00:00:00") { // Ende der Planungszeit des Tages erreicht Tag muss gewechselt werden if (weekday < 6) { // Nicht Samstag und Nicht Feiertag weekday = weekday + 1; NextSchedulePointer = 1; } if (weekday === 6) { // 6 = Samstag weekday = 0; // 0 = Sonntag NextSchedulePointer = 1; } if (weekday === 7) { // 7 = Feiertag if (new Date().getDay() === 6) { // 1 = Morgen ist Sonntag weekday = 0; // also auf Sonntag gehen NextSchedulePointer = 1; } else { weekday = new Date().getDay() + 1; NextSchedulePointer = 1; } } } if (NextSchedulePointer >= 6) { weekday = weekday + 1; // der letzte Slot war erreicht also Tageswechsel NextSchedulePointer = 1; // next slot ist der erste if(weekday > 7 ) { // log("Achtung FEHLER - Weekday ist "+ weekday+ " fuer Raum "+ room ) weekday = 7; } } // endif weekday if (NextSchedulePointer === 1 ) { // Es liegt ein Tageswechsel vor weekday = DetermineSchedule(room, weekday, RaumProfil,true); // weekday wird uebersteuert, wenn ein "WieVortag" vorkommt } else { weekday = DetermineSchedule(room, weekday, RaumProfil,false); // weekday wird uebersteuert, wenn ein "WieVortag" vorkommt } TimeBisScheduled = getState(path + "." + room + ".Profil-" + RaumProfil + "." + Wochentag(weekday) + "_" + NextSchedulePointer + "_" + "bis").val; // die BIS-Zeit lesen if (Funktion === "NextSollTemp") { NextTemperatureScheduledTemp = getState(path + "." + room + ".Profil-" + RaumProfil + "." + Wochentag(weekday) + "_" + NextSchedulePointer + "_" + "Temp").val; NextTemperatureScheduledTemp = Calculate_SollTemp(NextTemperatureScheduledTemp, "SetTemp"); if(debug) { log("Routine SelectSwitchTime - Nächste Solltemperatur ist " + NextTemperatureScheduledTemp + " fuer Raum" + room + " Raumprofil ist "+ RaumProfil, "info"); } return NextTemperatureScheduledTemp; //Next Schaltzeit gefunden - also raus jetzt } // endif next Solltemp if (Funktion === "NextSlot") { NextTemperatureScheduledSlot = Wochentag(weekday) + "_" + TimeBisScheduled; if(debug) { log("Routine SelectSwitchTime - Nächster Zeitslot ist " + NextTemperatureScheduledSlot + " fuer Raum" + room + " Raumprofil ist "+ RaumProfil , "info"); } return NextTemperatureScheduledSlot; //Next Schaltzeit gefunden - also raus jetzt } // endif next slot return 0; // da ist was schiefgelaufen. es haette einen schedule geben sollen } // ende der Function //----------------------------------------------------------------------------------------------------- // Funktion um den richtigen Planungstag zu finden bei Verwendung von WieVortag im schedule //----------------------------------------------------------------------------------------------------- function DetermineSchedule(room,weekday,Profil,FeiertagMorgenChck) { if (FeiertagMorgenChck === undefined) { FeiertagMorgenChck = false; } var currentday = weekday; var prePath = path + "." + room + ".Profil-" + Profil + "."; var StateUrlaubWieFeiertag = prePath + "ProfilParameter_UrlaubWieFeiertag"; // Profilparameter Urlaub = Feiertag gesetzt ? if (getState(StateUrlaubAnwesend).val && getState(StateUrlaubWieFeiertag).val ) { // Heute ist ein Urlaubstag und soll wie ein Feiertag behandelt werden weekday = 7; // Urlaub ist wie Feiertag also den Feiertagsschedule setzen } if(UseFeiertagskalender) { if (!FeiertagMorgenChck && getState(StateFeiertagHeute).val ) { // Heute ist Feiertag weekday = 7; // Urlaub ist wie Feiertag also den Feiertagsschedule setzen } if (FeiertagMorgenChck && getState(StateFeiertagMorgenAdapter).val ) { // Heute ist Feiertag weekday = 7; // Urlaub ist wie Feiertag also den Feiertagsschedule setzen } } if (weekday === 1) { // der schedule fuer Montag hat nie einen "wie Vortag" return weekday; } // log("FeiertagMorgenChck " + FeiertagMorgenChck + "getState(StateFeiertagHeute).val " + getState(StateFeiertagHeute).val+ " getState(StateFeiertagMorgenAdapter).val " + getState(StateFeiertagMorgenAdapter).val) // log("weekday = "+ weekday + "Wochentag(weekday) "+ Wochentag(weekday)) var TempWieVortag = getState(prePath + Wochentag(weekday) + "_" + "wieVortag").val; // Wenn es keinen "WieVortag gib, dann kann der Tag genommen werden" if (TempWieVortag === false) { return weekday; } if (weekday === 0 ) { // Sonntag TempWieVortag = getState(prePath + Wochentag(0) + "_" + "wieVortag").val; if (TempWieVortag === false) { if (debug) {log("Routine DetermineSchedule: Zu planedner Tag ist = " + Wochentag(weekday) + " Tag fuer den Schedule ist = " + Wochentag(0),"info"); } return(0); // Sonntagschedule hat keinen "WieVortag"-tick } else { weekday = 6; // Sonntagschedule hat einen "WieVortag"-tick also ist geht es jetzt mit Samstag weiter } } // Erstmal bei Feiertag schauen ob der Sonntag-Schedule der richtige ist if (weekday === 7 ) { // Feiertag TempWieVortag = getState(prePath + Wochentag(0)+ "_" + "wieVortag").val; // Hat der folgende Sonntag einen WieVortag Tick ? if (TempWieVortag === false) { // hater er nicht if (debug) { log("Routine DetermineSchedule: zu planender Tag ist = " + Wochentag(weekday) + " Tag fuer den Schedule ist = " + Wochentag(0),"info"); } return(0); // Sonntagschedule hat keinen "WieVortag"-tick } else { weekday = 6; // Sonntagschedule hat einen "WieVortag"-tick also ist geht es jetzt mit Samstag weiter } } // da Feiertag und Sonntag schon klar sind koennen die Tage von Samstag bis Montag (6-1) abgearbeitet werden for (var i = weekday; i > 0; i--) { TempWieVortag = getState(prePath + Wochentag(i)+ "_" + "wieVortag").val; if (TempWieVortag === false) { if (debug) { log("Routine DetermineSchedule: zu planender Tag ist = " + Wochentag(currentday) + " Tag fuer den Schedule ist = " + Wochentag(i),"info"); } return i; // Sonntagschedule ist richtig } } } // ende Funktion //----------------------------------------------------------------------------------------------------- //Globale Parameter aus ICAL Feiertagskalender und Anwesenheitserkennung setzen // Globale Parameter aus ICAL: Urlaub Anwesend, Urlaub Abwesend, Party, Gaeste, Feiertage // Wenn events genutzt werden, dann koennen globale Parameter nicht mehr manuell eingestellt werden //----------------------------------------------------------------------------------------------------- function SetEventGlobalParameter() { if (getState(Gparameterpath + ".ICAL-Events_Aktiv").val === false) { return; // global parameter events sind ausgeschaltet } if (getState(ICALPath + "."+ EventG_Party).val) { setState(StatePartyjetzt, true); Source_ICALEvent = EventG_Party; return; } else { setState(StatePartyjetzt, false); } if (getState(ICALPath + "."+ EventG_UrlaubAbwesend).val) { setState(StateUrlaubAbwesenheit, true); Source_ICALEvent = EventG_UrlaubAbwesend; return; } else { setState(StateUrlaubAbwesenheit, false); } if (getState(ICALPath + "."+ EventG_UrlaubAnwesend).val) { setState(StateUrlaubAnwesend, true); Source_ICALEvent = EventG_UrlaubAnwesend; } else { setState(StateUrlaubAnwesend, false); } if (getState(ICALPath + "."+ EventG_Gaeste).val) { setState(StateGaesteDa, true); Source_ICALEvent = EventG_Gaeste; return; } else { setState(StateGaesteDa, false); } if (UseFeiertagskalender === false) { // Wenn der Feiertagsadapter genutzt wird werden Events fuer Feiertage nicht genutzt if (getState(ICALPath + "."+ EventG_Feiertag).val) { setState(StateFeiertagHeute, true); Source_ICALEvent = EventG_Feiertag; return; } else { setState(StateFeiertagHeute, false); } // endif check ICAL } // endif feiertagskalender nicht aktiv } // Ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion Finde Aktives Raumprofil //----------------------------------------------------------------------------------------------------- function ActiveProfile (room,Profil) { if(Profil === undefined) { Profil = 1; } var pathprofile = path + "." + room; var ProfilName; if (MaxProfile === 1) { Profil = 1; Source_Profil = 1; return Profil; } // jetzt ICAL Profil auswählen wenn ICAL für Raumprofile aktiviert ist var ICALProfilSelect = getState(Gparameterpath+".ICAL-Events_Aktiv").val if (ICALProfilSelect === true ) { // Events aktiv ? // Erst Raumprofil checken - prio1 for (var i = MaxProfile; i > 0; i--) { ProfilName = UseEventR_Profil; ProfilName = UseEventR_Profil.replace("", room); ProfilName = ProfilName.replace("", i.toString()); const PN = ICALPath + "." + ProfilName; if(getObject(PN)) { // Existiert der DP ? - Lange Liste Warnung im log vermeiden, falls nicht vorhanden if (getState(ICALPath + "." + ProfilName).val) { setOwnState(pathprofile + ".AktivesEventProfil", i); // Eventprofil update setOwnState(path + "." + room + ".AktivesRaumProfil", i); // Aktives Profil Update Source_Profil = i; Source_ICALEvent = ProfilName; return i; } } } // ende for i // Globales Profil ist prio2 for (var i = MaxProfile; i > 0; i--) { ProfilName = UseEventG_Profil; ProfilName = UseEventG_Profil.replace("", i.toString()); const PN = ICALPath + "." + ProfilName; if(getObject(PN)) { // Existiert der DP ? - Lange Liste Warnung im log vermeiden, falls nicht vorhanden if (getState(ICALPath + "." + ProfilName).val) { setOwnState(pathprofile+".AktivesEventProfil", i); // Eventprofil update setOwnState(path + "." + room + ".AktivesRaumProfil", i); // Aktives Profil Update Source_Profil = i; Source_ICALEvent = ProfilName; return i; } } } // ende for i } // ende if ICAL ProfilSelect if (getState(pathprofile + ".AktivesEventProfil").val !== 0 && ICALProfilSelect === true) { // falls vorher ein Eventprofil aktiv war jetzt deaktivieren setOwnState(pathprofile + ".AktivesEventProfil", 0); setOwnState(path + "." + room + ".AktivesRaumProfil", 1); // Aktives Profil Update return 1; } // Wenn kein anderes Profil vorliegt dann gilt das manuell eingstellte Profil Profil = getState(pathprofile + ".AktivesRaumProfil").val; if(Profil > MaxProfile || Profil === 0 ) { Profil = 1; setOwnState(path + "." + room + ".AktivesRaumProfil", 1); // Aktives Profil Update } return Profil; } //----------------------------------------------------------------------------------------------------- // Funktion SetTemp zum setzen einer neuen Solltemperatur //----------------------------------------------------------------------------------------------------- function SetTemp(room, SollTemp, id, AdjustLastTemp) { SetDetData(room); // Speichere Findungsdaten if (getState(id).val !== SollTemp) { // ist die SET-Temperature unterschiedlich zur errechneten Solltemperatur ? if (trace) { log("Routine SetTemp Trace: temp update gestarted fuer Raum " + room+ " und id " + id ,"info"); } setOwnState(id, SollTemp); if (debug) { log ("Routine SetTemp: ID updated " + id+" Raum " + room + " SollTemp = " + SollTemp, "info"); } Source_CurrentSollTemp = SollTemp; } if (AdjustLastTemp && getState(path + "." + room + ".Source_Last_Temp").val !== SollTemp) { // LastSollTemp synhronisieren setOwnState(path + "." + room + ".Source_Last_Temp", SollTemp); if (debug) {log(' Setze ' + room + ".Source_Last_Temp zu " + SollTemp);} } // endif } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion SyncThermostat - bei manuellen Aenderungen werden alle Thermostate des Raumes gesynched //----------------------------------------------------------------------------------------------------- function SyncThermostat(room, SollTemp, Source_id) { var id; var delay = SynchDelay; var idExtract; var ThermoType; var ThermMode; for (var x in ControlTab) { if (ControlTab[x][0] !== room) { // nur fuer relevante Raeume continue; } // bei jedem Sync wird der Modus (Auto/Manu) ueberprüft für alle Thermostate des Raumes idExtract = ControlTab[x][4]; ThermoType = ControlTab[x][5]; // Setzen des Thermostates in den manuellen Modus - wenn moeglich / eingestellt if (ControlTab[x][7] !== false && getState(path + "." + room + "." + "RaumParameter_ManuellModeForce").val === true ) { // Geraet laesst sich auf MANU Schalten - im raumparameter steht auch, dass geschaltet werden soll ThermMode = getState(idExtract + "." + ControlTab[x][10]).val; // Feststellen ob das Thermostat auf Auto oder MANU steht // Check CC und DN Thermostate (nicht IP) if ((ThermoType === "WT" || ThermoType === "HT" ) && (ThermMode === 0 || ThermMode === 2)) { // Pruefen ob der manuelle Modus oder Party Mode eingeschaltet ist fuer CC and DN Thermostate log("Routine LoopDevices: Geraet " + idExtract +" Raum: " + room + " in den Manuellen Modus gesetzt ", "info"); setOwnState(idExtract + "." + ControlTab[x][7], getState(id).val); // setzen auf manuell - native boost und party werden nicht beachtet // setOwnState(idExtract + "." + ControlTab[x][7], 1); // setzen auf manuell - native boost und party werden nicht beachtet writelog(room, id, "Thermostat in den manuellen Modus versetzt"); } // endif automode war eingeschaltet // check IP Thermostate if ((ThermoType === "IPHT" || ThermoType === "IPWT" || ThermoType === "VD" ) && (ThermMode === 0 || ThermMode === 2)) { // Pruefen ob der manuelle Modus oder Party Mode eingeschaltet ist fuer CC and DN Thermostate log("Routine LoopDevices: Geraet " + idExtract +" Raum: " + room + " in den Manuellen Modus gesetzt ", "info"); log("Thermotype ist " + ThermoType + " ThermMode ist "+ ThermMode, "info"); setOwnState(idExtract + "." + ControlTab[x][7], 1); // setzen auf manuell - native boost und party werden nicht beachtet writelog(room, id, "Thermostat in den manuellen Modus versetzt"); } // endif automode war eingeschaltet } // endif pruefe ob das Geraet in den manuellen Modus geschickt werden kann und soll if (Check_ThermDV(room) ) { // nicht relevant für DV Thermosate continue; // nur für nicht DV Thermostate } id = ControlTab[x][1]; if (ControlTab[x][0] === room && Source_id !== id ) { // alle Nicht DV Thermostate des Raumes werden gesynct if (debug) {log("Routine SyncThermostat: Temperatur " + SollTemp + " wird synchronisiert fuer id " + id + " auf Temp " + SollTemp ); } setTimeout(function() { // Rest Initialisierung bei Bedarf leicht verzögern SetTemp(room, SollTemp, id, false); }, delay); delay = delay + SynchDelay // delay bei jeder ID um den Wert von SynchDelay erhöht } // endif setze Manuelle Temperatur } // endfor } // Ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion CreateStates zum Anlegen der Datenpunkte //----------------------------------------------------------------------------------------------------- function CreateStates(room) { room = room.replace(/\s/g, "_"); // Alle Leerzeichen aus der Raumbezeichnung entfernen // Globale Parameter createState(StateAnwesenheit, false, {read: true, write: true, type: 'boolean', name: 'Anwesenheitsflag', desc: 'Anwesenheitsflag'}); createState(StateFeiertagHeute, false, {read: true, write: true, type: 'boolean', name: 'Feiertag Heute', desc: 'Feiertag Heute- zur Temperaturanpassung (wie Sonntag)'}); createState(StatePartyjetzt, false, {read: true, write: true, type: 'boolean', name: 'Party Jetzt', desc: 'Party Jetzt- zur Temperaturanpassung'}); createState(StateGaesteDa, false, {read: true, write: true, type: 'boolean', name: 'Gaeste da', desc: 'Gaeste da - zur Temperaturanpassung'}); createState(StateUrlaubAnwesend, false, {read: true, write: true, type: 'boolean', name: 'Urlaub Anwesend', desc: 'Urlaub Heute- zur Temperaturanpassung (wie Sonntag)'}); createState(StateUrlaubAbwesenheit, false, {read: true, write: true, type: 'boolean', name: 'Urlaub Abwesend', desc: 'Urlaub und nicht zu Hause)'}); createState(StateHeizperiode, true, {read: true, write: true, type: 'boolean', name: 'Wenn Heizperiode dann Aktivierung der Heizplaene', desc: 'Ausserhalb der Heizperiode werden Ventile geschlossen'}); State = Gparameterpath + ".Source_last_Program_Run"; createState(State, "init", {read: true, write: false, type: 'string', name: 'Datum/Zeit des letzten Programmlaufes' , desc: 'Datum/Zeit des letzten Programmlaufes'}); State = Gparameterpath + ".ICAL-Events_Aktiv"; createState(State, false, {read: true, write: false, type: 'boolean', name: 'Global ICAL Event aktiv' , desc: 'Uebersteuert Skripteinstellung'}); // Anlegen der raumbezogenen/profilbezogenen Datenpunkte var State; var RoomPath = path + "." + room + "."; var Profilpath; var Vorgabewert1 = Calculate_SelectValueWert(1, "CorrectTemp"); var Vorgabewert2 = Calculate_SelectValueWert(2, "CorrectTemp"); var Vorgabewert17 = Calculate_SelectValueWert(17, "SetTemp"); var bisTime; var SollTemp; var StateBis; var StateTemp; var StateWieVortag; State = RoomPath + "Source_last_Program_Run"; createState(State, "init", {read: true, write: false, type: 'string', name: 'Datum/Zeit des letzten Programmlaufes für diesen Raum' , desc: 'Datum/Zeit des letzten Programmlaufes für diesen Raum'}); for (var y = 1; y <= MaxProfile; y++) { // es werden alle Daten je profil angelegt Profilpath = "Profil-" + y +"."; for (var i = 1; i <= 6; i++) { // es werden 6 BIS Zeiten angelegt for (var x = 0; x <= 7; x++) { // es wird ein Plan je Wochentag und ein Feiertag angelegt if (i <= 6) { // die 6. Zeit hat keine BIS Zeit - nur Temperatur erforderlich StateBis = RoomPath + Profilpath + Wochentag(x) + "_" + i +"_" + "bis"; } StateTemp = RoomPath + Profilpath + Wochentag(x) + "_"+ i +"_" + "Temp"; if (i===1) { bisTime = "06:00:00"; SollTemp = Calculate_SelectValueWert(17,"SetTemp");} if (i===2) { bisTime = "08:00:00"; SollTemp = Calculate_SelectValueWert(21,"SetTemp");} if (i===3) { bisTime = "16:00:00"; SollTemp = Calculate_SelectValueWert(18,"SetTemp");} if (i===4) { bisTime = "21:00:00"; SollTemp = Calculate_SelectValueWert(21,"SetTemp");} if (i===5) { bisTime = "23:30:00"; SollTemp = Calculate_SelectValueWert(19,"SetTemp");} if (i===6) { bisTime = "00:00:00"; SollTemp = Calculate_SelectValueWert(17,"SetTemp");} createState(StateBis ,bisTime , {read: true, write: true, type: 'string', name: 'Zeit ' + i + ' von' , desc: 'Zeit von'}); createState(StateTemp ,SollTemp , {read: true, write: true, type: 'number', name: 'Solltemperatur '+ i , desc: 'Solltemperatur'}); StateWieVortag = RoomPath + Profilpath + Wochentag(x)+ "_" + "wieVortag"; createState(StateWieVortag, false, {read: true, write: true, type: 'boolean', name: 'Wie Vortag ' , desc: 'Wie Vortag'}); } // endfor y 6 Zeiten bzw Temperatur je schedule } // endfor i 6 BIS Zeiten State = RoomPath + Profilpath + StatePP_PartyAbsenkung; createState(State, Vorgabewert2 , {read: true, write: true, type: 'number', name: 'Absenkung bei Party in Grad Celsius' , desc: 'Absenkung bei Party - negativer Wert = Anhebung'}); State = RoomPath + Profilpath + StatePP_GaesteAnhebung; createState(State, Vorgabewert1 , {read: true, write: true, type: 'number', name: 'Anhebung, wenn Gaeste anwesend' , desc: 'Anhebung bei Gaesten - negativer Wert = Absenkung'}); State = RoomPath + Profilpath + StatePP_AbwesenheitAbsenkung; createState(State, Vorgabewert1 , {read: true, write: true, type: 'number', name: 'Absenkung bei Abwesenheit in Grad Celsius' , desc: 'Absenkung bei Abwesenheit - negativer Wert = Anhebung'}); State = RoomPath + Profilpath + StatePP_UrlaubAbsenkung; createState(State, Vorgabewert2 , {read: true, write: true, type: 'number', name: 'Absenkung bei Urlaubs-Abwesenheit in Grad Celsius' , desc: 'Absenkung bei Urlaubs-Abwesenheit - negativer Wert = Anhebung'}); State = RoomPath + Profilpath + StatePP_UrlaubWieFeiertag; createState(State, true, {read: true, write: true, type: 'boolean', name: 'bei Anwesenheit wg Urlaub Temperaturen laut Feiertagsplan' , desc: 'Temperaturen laut Feiertagsplan'}); State = RoomPath + Profilpath + "ProfilParameter_MinimaleTemperatur"; createState(State, Vorgabewert17 , {read: true, write: true, type: 'number', name: 'Minimale Temperatur fuer Absenkung' , desc: 'Minimale Temperatur fuer Absenkung'}); } // endfor y // Daten je Profil // RaumParameter State = RoomPath + "AktivesRaumProfil"; createState(State, 1, {read: true, write: true, type: 'number', name: 'Aktives EventProfil 1-9' , desc: 'Fuer jeden Raum koennen max 9 Profile verwendet werden'}); State = RoomPath + "AktivesEventProfil"; createState(State, 0, {read: true, write: true, type: 'number', name: 'Aktives EventProfil 1-9' , desc: 'Fuer jeden Raum koennen max 9 Profile verwendet werden'}); State = RoomPath + "Source_Profil"; createState(State, 0, {read: true, write: true, type: 'number', name: 'ermitteltes Profil fuer die letzte RaumTemperatur' , desc: 'Fuer jeden Raum koennen max 9 Profile verwendet werden'}); State = RoomPath + "Source_ICALEvent"; createState(State, "init", {read: true, write: true, type: 'string', name: 'ermitteltes ICAL Event fuer die Profilfindung / Parameterermittlung' , desc: 'Profil oder Parameteranpassung'}); State = RoomPath + "Source_Schedule"; createState(State, "init", {read: true, write: true, type: 'string', name: 'der Schedulepunkt fuer die Solltemperatur' , desc: 'Der Schedule ist die taegliche Temperaturplanung'}); State = RoomPath + "Source_NextTemp"; createState(State, 0, {read: true, write: false, type: 'number', name: 'nächste geplante Temperatur' , desc: 'nächste geplante Temperatur'}); State = RoomPath + "Source_Global_Parameter"; createState(State, "init", {read: true, write: true, type: 'string', name: 'Globaler Parameter, der die Temperaturfindung beeinflusst' , desc: 'Global Parameter wie Party, Urlaub, Gaeste etc'}); State = RoomPath + "Source_Manually_Adjusted"; createState(State, 0, {read: true, write: true, type: 'number', name: 'Manuell eingestellte Temperatur' , desc: 'Solltemperatur der manuellen Verstellung'}); State = RoomPath + "Source_Last_Temp"; createState(State, 0, {read: true, write: true, type: 'number', name: 'letzte eingestellte Temperatur' , desc: 'Letzte Solltemperatur'}); State = RoomPath + "Source_TimeStamp"; createState(State, "init", {read: true, write: true, type: 'string', name: 'Datum und Zeit der letzten Tempanpassung' , desc: 'Zeitstempel'}); State = RoomPath + "View_Manually_Adjusted"; createState(State, 0, {read: true, write: true, type: 'number', name: 'Im View manuell eingestellte Temperatur - 0=reset' , desc: 'Im View manuell eingestellte Temperatur - 0=reset'}); State = RoomPath + "View_Manual_Temp_Duration"; createState(State, 120, {read: true, write: true, type: 'number', name: 'Gueltigkeit in Minuten fuer manuelle Temperatur Aenderung' , desc: 'Zeitgueltigkeit in Minuten'}); State = RoomPath + "View_ManTemp_Validity"; createState(State, "init", {read: true, write: true, type: 'string', name: 'Datum und Zeit der Gueltigkeit (bis) zur Rueckkehr zum Plan' , desc: 'Zeitgueltigkeit bis (Datum / Zeit'}); State = RoomPath + "RaumParameter_ManuellModeForce"; createState(State, true, {read: true, write: true, type: 'boolean', name: 'Immer Umschaltung auf manuell (nur neue Thermsotate)' , desc: 'Immer Umschaltung auf manuell (nur neue Thermsotate'}); State = RoomPath + "RaumStatusVerschluss"; createState(State, true, {read: true, write: true, type: 'boolean', name: 'Verschlussssstatus des Raumes)' , desc: 'Verschlussstatus true=geoeffnet - false = geschlossen'}); State = RoomPath + "ICALProfilSelect"; createState(State, true, {read: true, write: true, type: 'boolean', name: 'ICAL Auto-Select aktivieren/deaktivieren' , desc: 'True = ICAL selektiert Profile / false es kann manuell ein Profil gewaehlt werden'}); State = RoomPath + "SoftBoostActive"; createState(State, false, {read: true, write: true, type: 'boolean', name: 'Soft-Boost aktiviertn' , desc: 'True = Soft-Boost Aktiv / false Soft-Boost deaktiviert'}); State = RoomPath + "SoftBoostDuration"; createState(State, 60, {read: true, write: true, type: 'number', name: 'Dauer in Minuten fuer wenn Soft-Boost aktiviert wird' , desc: 'Im View manuell eingestellte Dauer von SoftBoost'}); State = RoomPath + "SoftBoostValidity"; createState(State, "init", {read: true, write: true, type: 'string', name: 'Datum und Zeit der Gueltigkeit (bis) Soft-Boost beendet wird imd Rueckkehr zum Plan' , desc: 'Zeitgueltigkeit bis (Datum / Zeit'}); } // ende Funktion //----------------------------------------------------------------------------------------------------- //ab hier Nebenfunktionen //----------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------- // Funktion SetRoomClosed setzt einen Zeitstempel, wenn der letzte Sensor geschlossen meldet //----------------------------------------------------------------------------------------------------- function SetRoomClosed (room, delay) { if (delay === 0 ) { // delay in Minuten aus ThermTabType Tabelle return; } if ( trace ) { log("SetRoomClosed gestartet fuer Raum "+ room); } var DelayDuration = delay*60000; var DelayTime = new Date().getTime()+DelayDuration; // DelayTime = jetzt plus Anzahl Millisekunden (umgerechnet von Minuten) aus der TermostatTypeTab Einstellungen for (var x in DelayAfterClose) { if (DelayAfterClose[x][0] === room) { if(DelayAfterClose[x][1] < DelayTime) { DelayAfterClose[x][1] = new Date().getTime() + DelayDuration; // Zeitstempel setzen und die gewünschte anzahl Minuten hinzurechnen aus ThermTabType Tabelle ManTimeouts[room] = setTimeout(TriggerUpdate, DelayDuration, room, true,undefined); log("Routine SetRoomClosed: Zeitstempel war gesetzt für geschlossenen Raum " + room + "neuer Zeitstempel = " + formatDate(DelayAfterClose[x][1], "YYYY/MM/DD SS:mm:ss"),"info"); return; } else { return; // wert ist noch gültig } // endif DelayAfterClose } // endif room gefunden } // endfor // Eintrag gibt es noch nicht also hinzufügen DelayAfterClose.push([room, DelayTime]); ManTimeouts[room] = setTimeout(TriggerUpdate, DelayDuration, room, false,undefined); if (debug) { log("Routine SetRoomClosed: Zeitstempel gesetzt für geschlossenen Raum " + room + " Zeitstempel = " + formatDate(DelayTime, "YYYY/MM/DD SS:mm:ss")); } } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion CheckDelay checked ob ein Delay erforderlich ist für eine evt Anpassung der SollTemp //----------------------------------------------------------------------------------------------------- function CheckDelay (room) { var ActTime = formatDate(new Date(),"YYYY/MM/DD SS:mm:ss"); // Formatierte Aktuelle Zeit var ActTimeMilliSek = new Date(ActTime).getTime(); // Aktuelle Zeit in Millisekunden seit 1970 for (var x in DelayAfterClose ) { if (DelayAfterClose[x][0] === room) { if(ActTimeMilliSek > DelayAfterClose[x][1] && DelayAfterClose[x][1] > 0) { // DelayTime abgelaufen log("Routine CheckDelay: delay für Raum " + room + " abgelaufen", "info" ); DelayAfterClose[x][1] = 0; return 0; } if (debug) { log("Routine CheckDelay: delay für Raum " + room + " ist vorhanden bis " + DelayAfterClose[x][1] + " millisekunden ", "info" ); } return DelayAfterClose[x][1]; // melde die Ablaufzeit } // endif room gefunden } // endfor return 0; // kein Delay } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion Check_SensorDV checkt ob eine Direktvernuepfung von Sensor zu Thermostat vorliegt im Raum //----------------------------------------------------------------------------------------------------- function Check_SensorDV(room) { if(room !== undefined) { // raum wurde übergeben also checken ob der Sensor direktvernüpft ist - gilt nur für offene Räume for (var i in SensorList) { if (SensorList[i][0] === room && SensorList[i][7] && SensorList[i][9] ) { //Sensor für Raum ist offen und DV return true } } return false; } // endif suche in sensorlist nach raum } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion Check_ThermDV checkt ob eine Direktvernuepfung von Thermostat zu Thermostat (Gruppen) vorliegt im Raum //----------------------------------------------------------------------------------------------------- function Check_ThermDV(room) { for (var i in ControlTab ) { if (ControlTab[i][0] === room && ControlTab[i][7] === true) { // Thermostat ist direktverknuepft return true; } } return false; } //ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion InitilizeManChange - Ruecksetzen der manuellen Daten - Manuelle Temperatur //----------------------------------------------------------------------------------------------------- function InitilizeManChange(room) { setOwnState(path + "." + room + ".Source_TimeStamp", "init"); setOwnState(path + "." + room + ".View_ManTemp_Validity", "init"); setOwnState(path + "." + room + ".Source_Manually_Adjusted", 0) ; setOwnState(path + "." + room + ".View_Manually_Adjusted", 0) ; } // endfunction //----------------------------------------------------------------------------------------------------- // Funktion SortControlTab - Sortieren der ControlTab nach Raumnamen //----------------------------------------------------------------------------------------------------- function SortControlTab(a,b) { a = a[0]; b = b[0]; return a == b ? 0 : (a < b ? -1 : 1); } // endfunction //----------------------------------------------------------------------------------------------------- // Funktion check Room in roomlist - //----------------------------------------------------------------------------------------------------- function ChckRoom(room) { var z; for (z in ControlTab) { if (ControlTab[z][0] === room) { if (debug) {log("Routine ChckRoom - Dem Raum " + room + " ist ein Gerät zugeordnet ", "info"); } return true; } } for (z in NoneHMTab) { // auch nicht HM checken if (NoneHMTab[z][0] === room) { if (debug) {log("Routine ChckRoom - Dem Raum " + room + " ist ein Gerät zugeordnet ", "info"); } return true; } } for (z in NoneHMSenorTab) { // auch nicht HM checken if (NoneHMSenorTab[z][0] === room) { if (debug) {log("Routine ChckRoom - Dem Raum " + room + " ist ein Gerät zugeordnet ", "info"); } return true; } } return false; } // ende funktion //----------------------------------------------------------------------------------------------------- // Funktion SensorFind Sucht den Sensor aus der SensorTabelle //----------------------------------------------------------------------------------------------------- function SensorFind(id) { // tabtype entweder Sensortab oder controltab for (var i in SensorList) { if (debug) { log("Routine SensorFind ID = " + SensorList[i][1] + " Raum = " + SensorList[i][0], "info"); } if (SensorList[i][1] === id) { return i; } } return 999; } // Ende Funktion //----------------------------------------------------------------------------------------------------- //Function VerschlussRaumStatus // ermittelt ob irgend ein Verschluss auf offen steht //----------------------------------------------------------------------------------------------------- function VerschlussRaumStatus(room) { var count = 0; for (var x in SensorList) { if (SensorList[x][0].toString() === room) { count = count + 1; if (SensorList[x][7] === true) { return true; // mindestens ein Sensor steht auf offen } } } return false; // alle Sensoren auf geschlossen } // Ende Funktion //----------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------- // Funktion SaveStatus - Speichert den Raumstatus // Type = Type der Meldung: Heizplan, Fenster, HeizperiodeAus, Abwesend, Party, Gaeste, Urlaub Anwesend, Urlaub Abwesend, // room = auf den Raum bezogen // MindestTempflag = Mindesttemperatur gesetzt - true/false //----------------------------------------------------------------------------------------------------- function SaveStatus(Type,room, MindestTemp) { if(Type === "Heizplan") { Source_GlobalParameter = " " // es wird nach Heizplan geheizt - keine Meldung if (getState(path + "." + room + ".Source_Global_Parameter").val !== Source_GlobalParameter) { setOwnState(path + "." + room + ".Source_Global_Parameter", Source_GlobalParameter); if (debug) {log(' Setze ' + room + ".Source_Global_Parameter zu " + Source_GlobalParameter);} } // Endif update notwendig } // Ende Heizplan if(Type === "Fenster") { Source_GlobalParameter = "Absenkung - Verschluss geoeffnet"; if (getState(path + "." + room + ".Source_Global_Parameter").val !== Source_GlobalParameter) { setOwnState(path + "." + room + ".Source_Global_Parameter", Source_GlobalParameter); if (debug) {log(' Setze ' + room + ".Source_Global_Parameter zu " + Source_GlobalParameter);} } // Endif update notwendig } // Endif Fenster if(Type === "HeizperiodeAus") { Source_GlobalParameter = "keine Heizperiode - Heizplan wird nicht ausgeführt"; if (getState(path + "." + room + ".Source_Global_Parameter").val !== Source_GlobalParameter) { setOwnState(path + "." + room + ".Source_Global_Parameter", Source_GlobalParameter); if (debug) {log(' Setze ' + room + ".Source_Global_Parameter zu " + Source_GlobalParameter);} } // Endif update notwendig } // Ende Heizperiode if(Type === "Abwesend") { if(MindestTemp === true) { Source_GlobalParameter = "Absenkung Abwesenheit auf MindestTemperatur"; } else { Source_GlobalParameter = "Absenkung Abwesenheit"; } if (getState(path + "." + room + ".Source_Global_Parameter").val !== Source_GlobalParameter) { setOwnState(path + "." + room + ".Source_Global_Parameter", Source_GlobalParameter); if (debug) {log(' Setze ' + room + ".Source_Global_Parameter zu " + Source_GlobalParameter);} } // Endif update notwendig } // Ende Abwesend if(Type === "RaumAbwesend") { if(MindestTemp === true) { Source_GlobalParameter = "Absenkung Raum-Abwesenheit auf MindestTemperatur"; } else { Source_GlobalParameter = "Absenkung Raum-Abwesenheit"; } if (getState(path + "." + room + ".Source_Global_Parameter").val !== Source_GlobalParameter) { setOwnState(path + "." + room + ".Source_Global_Parameter", Source_GlobalParameter); if (debug) {log(' Setze ' + room + ".Source_Global_Parameter zu " + Source_GlobalParameter);} } // Endif update notwendig } // Ende RaumAbwesend if(Type === "Party") { if(MindestTemp === true) { Source_GlobalParameter = "Temperaturanpassung auf MindestTemperatur- Partyflag gesetzt"; } else { Source_GlobalParameter = "Temperaturanpassung - Partyflag gesetzt"; } if (getState(path + "." + room + ".Source_Global_Parameter").val !== Source_GlobalParameter) { setOwnState(path + "." + room + ".Source_Global_Parameter", Source_GlobalParameter); if (debug) {log(' Setze ' + room + ".Source_Global_Parameter zu " + Source_GlobalParameter);} } // Endif update notwendig } // Ende Party if(Type === "Gaeste") { if(MindestTemp === true) { Source_GlobalParameter = "Temperaturanpassung auf MindestTemperatur- Gaeste-Flag gesetzt"; } else { Source_GlobalParameter = "Temperaturanpassung - Gaeste-Flag gesetzt"; } if (getState(path + "." + room + ".Source_Global_Parameter").val !== Source_GlobalParameter) { setOwnState(path + "." + room + ".Source_Global_Parameter", Source_GlobalParameter); if (debug) {log(' Setze ' + room + ".Source_Global_Parameter zu " + Source_GlobalParameter);} } // Endif update notwendig } // Ende Gaeste if(Type === "UrlaubAnwesend") { Source_GlobalParameter = "Urlaub anwesend - Plannung ist Feiertag"; if (getState(path + "." + room + ".Source_Global_Parameter").val !== Source_GlobalParameter) { setOwnState(path + "." + room + ".Source_Global_Parameter", Source_GlobalParameter); if (debug) {log(' Setze ' + room + ".Source_Global_Parameter zu " + Source_GlobalParameter);} } // Endif update notwendig } // Ende Urlaub anwesend if(Type === "UrlaubAbwesend") { if(MindestTemp === true) { Source_GlobalParameter = "Temperaturanpassung auf MindestTemperatur- Urlaub Abwesend"; } else { Source_GlobalParameter = "Temperaturanpassung - Urlaub Abwesend"; } if (getState(path + "." + room + ".Source_Global_Parameter").val !== Source_GlobalParameter) { setOwnState(path + "." + room + ".Source_Global_Parameter", Source_GlobalParameter); if (debug) {log(' Setze ' + room + ".Source_Global_Parameter zu " + Source_GlobalParameter);} } // Endif update notwendig } // Ende Urlaub Abesend if(Type === "Manuell") { Source_GlobalParameter = "Manuelle Temperaturanpassung"; if (getState(path + "." + room + ".Source_Global_Parameter").val !== Source_GlobalParameter) { setOwnState(path + "." + room + ".Source_Global_Parameter", Source_GlobalParameter); if (debug) {log(' Setze ' + room + ".Source_Global_Parameter zu " + Source_GlobalParameter);} } // Endif update notwendig } // Ende Manuell } // Ende Function SaveStatus //----------------------------------------------------------------------------------------------------- // Funktion SetDetData - Schreibt die Findungsdaten in die source states //----------------------------------------------------------------------------------------------------- function SetDetData (room) { if (typeof(Source_ICALEvent) === "undefined" ) { Source_ICALEvent = ""; } if (typeof(Source_Profil) === "undefined" ) { Source_Profil = 99; } if (typeof(Source_SchedulePoint) === "undefined" ) { Source_SchedulePoint = ""; } if (getState(path + "." + room + ".Source_Profil").val !== Number(Source_Profil)) { setOwnState(path + "." + room + ".Source_Profil", Source_Profil); if (debug) {log(' Setze ' + room + ".Source_Profil zu " + Source_Profil);} } if (getState(path + "." + room + ".Source_ICALEvent").val !== Source_ICALEvent) { setOwnState(path + "." + room + ".Source_ICALEvent", Source_ICALEvent); if (debug) {log(' Setze ' + room + ".Source_ICALEvent zu " + Source_ICALEvent);} } //if (getState(path + "." + room + ".Source_ICALEvent").val !== Source_ICALEvent) { // setOwnState(path + "." + room + ".Source_ICALEvent", Source_ICALEvent); //} if (getState(path + "." + room + ".Source_Schedule").val !== Source_SchedulePoint) { setOwnState(path + "." + room + ".Source_Schedule", Source_SchedulePoint); if (debug) {log(' Setze ' + room + ".Source_Schedule zu " + Source_SchedulePoint);} } if (getState(path + "." + room + ".Source_NextTemp").val !== Source_NextSollTemp) { // if(Source_NextSollTemp !== undefined ) { setOwnState(path + "." + room + ".Source_NextTemp", Source_NextSollTemp); if (debug) {log(' Setze ' + room + ".Source_NextTemp zu " + Source_NextSollTemp);} // } } } // Ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion Calculate SollTemperatur Urechnung des Select Value Wertes in Gradzahlen //----------------------------------------------------------------------------------------------------- function Calculate_SollTemp(SollTemp, Calc_type) { var MinVal; var StepVal; var MaxVal ; if (VerwendungSelectValue !== true) { return SollTemp; } if (SollTemp === 0) { return SollTemp; } // Alle Temperaturen von 12 Grad bis 30 Grad if (Calc_type === "SetTemp") { MinVal = 12; // Liste faengt mit 12 an StepVal = 0.5; // Schrittwert ist 0.5 SollTemp = MinVal + ( SollTemp *StepVal); // Errechnung der SollTemp } // eingeschränkter Range von Temperaturen von - 5 bis plus 5 Grad if (Calc_type === "CorrectTemp") { MinVal = 0; // Liste faengt mit 0 an MaxVal = 5; // Der Maxvalue ist Positiv und Negativ StepVal = 0.5; SollTemp = SollTemp * StepVal; // Errechnung der SollTemp fuer positive Werte if (SollTemp > (MaxVal - MinVal)) { SollTemp = (SollTemp * -1 +StepVal) + MaxVal; // fuer Negative Werte } } // FullTemp = alle Temperaturen von 0 Grad nächste 10 Grad bis 30 grad if (Calc_type === "FullTemp") { MinVal = 10; // Liste faengt mit 10 an StepVal = 0.5; // Schrittwert ist 0.5 SollTemp = MinVal + ( SollTemp *StepVal) - 0.5 ; // Errechnung der SollTemp - Liste fängt mit 0 an was als 0,5 abgezogen wird } //log("aus Calc_SollTemp Ausstieg "+ SollTemp) return SollTemp; } // ende Funktion //----------------------------------------------------------------------------------------------------- // Function LastRoomUpdateTime // Findet die letzte Updatezeit eines Raumes oder speichert die letzte update Zeit // Funktion find und push //----------------------------------------------------------------------------------------------------- function LastRoomUpdateTime (room, Funktion) { // Funktion = "find" gibt die letzte updatezeit in Millisekunden zurück // Funktion = "push" fügt den Raum und aktuelle Zeit hinzu var ActTime = formatDate(new Date(),"YYYY/MM/DD SS:mm:ss"); // Formatierte Aktuelle Zeit var ActTimeMilliSek = new Date(ActTime).getTime(); // Aktuelle Zeit in Millisekunden seit 1970 var y = SubscribeThermBlock.length; for (var x in SubscribeThermBlock) { // log("routine LastRoomUpdateTime fuer Raum "+ room + " gespeicherte Zeit = "+ SubscribeThermBlock [x][1] + " aktuelle Zeit "+ formatDate(new Date(), "TT.MM.JJJJ"); ) if (SubscribeThermBlock[x][0] === room && Funktion == "find") { return SubscribeThermBlock [x][1]; } if (SubscribeThermBlock[x][0] === room && Funktion == "push") { SubscribeThermBlock[x][1] = ActTimeMilliSek; return ActTimeMilliSek; } } // wenn es den Raum noch nicht gab if ( Funktion == "find") { return 0; } if ( Funktion == "push") { SubscribeThermBlock[y] = [room, ActTimeMilliSek]; return ActTimeMilliSek; } } // ende der Function LastRoomUpdateTime //----------------------------------------------------------------------------------------------------- // Funktion Wochentag zur ermittlung des Tages //----------------------------------------------------------------------------------------------------- function Wochentag(tag) { if (tag === 0) { tag = "So"; return tag; } if (tag === 1) { tag = "Mo"; return tag; } if (tag === 2) { tag = "Di"; return tag; } if (tag === 3) { tag = "Mi"; return tag; } if (tag === 4) { tag = "Do"; return tag; } if (tag === 5) { tag = "Fr"; return tag; } if (tag === 6) { tag = "Sa"; return tag; } if (tag === 7) { tag = "Feiertag"; return tag; } } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion Calculate SelectValueList Wert Urechnung des Sollwertes in SelectValue List Wert //----------------------------------------------------------------------------------------------------- function Calculate_SelectValueWert(SelValWert, Calc_type) { var MinVal; var StepVal; var MaxVal; if (VerwendungSelectValue !== true) { return SelValWert; } if (Calc_type === "SetTemp") { MinVal = 12; // Liste faengt mit 12 an StepVal = 0.5; // Schrittwert ist 0.5 SelValWert = (SelValWert- MinVal ) / StepVal; // Errechnung des Select Value Wertes } if (Calc_type === "CorrectTemp") { MinVal = 0; // Liste faengt mit 0 an StepVal = 0.5; // Schrittwert ist 0.5 MaxVal = 5; // Der Maxvalue ist Positiv und Negativ if(SelValWert > (MaxVal/StepVal) ) { SelValWert = (SelValWert * (1 - StepVal) * -1) + 10; // Negative Werte } else { SelValWert = (SelValWert / StepVal); // Errechnung des Select Value Wertes positive Werte } } if (Calc_type === "FullTemp") { MinVal = 10; // Liste faengt mit 10 an StepVal = 0.5; // Schrittwert ist 0.5 SelValWert = (SelValWert- MinVal ) / StepVal + 0.5 ; // Errechnung des Select Value Wertes + 1 für den NullWert zu Beginn } // log ("SelValWert = " + SelValWert,"info"); return SelValWert; } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion ConstructID setzt den pfad für NoneHM Sensoren und Thermostate aus den TABs zusammen //----------------------------------------------------------------------------------------------------- function ConstructID (part1,part2,part3) { var FullID; if (part1 !== '' && part1 !== 'initial') { FullID = part1; } if ( part2 !== '' && part2 !== "initial" ) { if (FullID !== '' ) { FullID = FullID + '.'; } FullID = FullID + part2; } if ( part3 !== '' && part3 !== "initial" ) { if (FullID !== '' ) { FullID = FullID + '.'; } FullID = FullID + part3; } log("Routine ConstructID - zusammengebaute ID ist "+ FullID,"info" ); return FullID; } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion Setzt den Findungsnachweis zurueck //----------------------------------------------------------------------------------------------------- function ClearSources () { Source_Profil = 99; Source_ICALEvent = ""; Source_ManualAdjustment = ""; Source_Timestamp = ""; Source_SchedulePoint = ""; } // ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion listed die sources im log //----------------------------------------------------------------------------------------------------- function LogSources() { if (debug !== true) { return; } log("Source_Profil " + Source_Profil, "info"); log("Source_ICALEvent " + Source_ICALEvent, "info"); log("Source_ManualAdjustment " + Source_ManualAdjustment, "info"); log("Source_Timestamp " + Source_Timestamp, "info"); log("Source_SchedulePoint " + Source_SchedulePoint, "info"); log("Source_last_Program_Run " + Source_last_Program_Run, "info"); } // Ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion schreibt einen Logeintrag in das Filesystem //----------------------------------------------------------------------------------------------------- function writelog(room,id, Text) { if (typeof(Source_CurrentSollTemp) === "undefined") { Source_CurrentSollTemp = 0; } if (typeof(Source_ManualAdjustment) === "undefined") { Source_ManualAdjustment = 0; } if (OnlyChanges && Source_CurrentSollTemp === 0) { return; } // jetzt evt Kommawerte fuer Excel aufbereiten - Excel mit Komma =, und iobroker = . var Form_CurrentSollTemp = Source_CurrentSollTemp.toString(); Form_CurrentSollTemp = Form_CurrentSollTemp.replace(".", ","); var Form_Source_ManualAdjustment = Source_ManualAdjustment.toString(); Form_Source_ManualAdjustment = Form_Source_ManualAdjustment.replace(".", ","); if (LogFlag === true) { Source_GlobalParameter = getState(path + "." + room + ".Source_Global_Parameter").val; var logdate = formatDate(new Date(), "TT.MM.JJJJ"); var logtime = formatDate(new Date(), "SS:mm:ss"); if (!fs.existsSync(LogPath)) { log("Routine writelog: Logfile nicht gefunden - wird angelegt", "info"); var headerLine= "Datum;Uhrzeit;Raum;Geraete-ID;SollTemp gesetzt;Profil;Global-Parameter;Event;Manuelle Temp;Schedule-Point;Bemerkung"; fs.appendFileSync(LogPath, headerLine + "\n"); // Fuege Satz in Datei ein } fs.appendFileSync(LogPath, logdate + ";" + logtime + ";" + room + ";" + id + ";" + Form_CurrentSollTemp + ";" + Source_Profil + ";" + Source_GlobalParameter + ";" + Source_ICALEvent + ";" + Form_Source_ManualAdjustment + ";" + Source_SchedulePoint + ";" + Text + "\n"); // Fuege Satz in Datei ein } // Ende check on logflag } // Ende Funktion //----------------------------------------------------------------------------------------------------- // Funktion RoomListUsage - Ist der UseRoomList eingeschaltet und befindet sich der Raum in der Liste //----------------------------------------------------------------------------------------------------- function RoomListUsage (Room) { if (UseRoomList === false) { return true; } for (var i in RoomList ) { if (RoomList[i][0] === Room) { return true; } } // endfor return false; } // End Function //----------------------------------------------------------------------------------------------------- // 3 Funktionen zum Zeitrange check zur Pruefung ob die Schaltungszeiten erreicht sind // Autor ist Beatz - uebernommen aus: // viewtopic.php?f=21&t=1072&p=11167&hilit=isTimeInRange&sid=4dca8ea2c7f9337cdc73a1a9e4824a40#p11167 //----------------------------------------------------------------------------------------------------- function isTimeInRange(strLower, strUpper) { if (strLower === null || strUpper === null) { return; } // var now = new Date(); var lower = addTime(strLower); var upper = addTime(strUpper); var inRange = false; if (upper > lower) { // opens and closes in same day inRange = (now >= lower && now <= upper) ? true : false; } else { // closes in the following day inRange = (now >= upper && now <= lower) ? false : true; } return inRange; } function currentDate() { //var d = new Date(); //return new Date(d.getFullYear(), d.getMonth(), d.getDate()); var heute = new Date(); heute.setHours(0); heute.setMinutes(0); heute.setSeconds(0); heute.setMilliseconds(0); return heute; } function addTime(strTime) { var time = strTime.split(':'); var d = currentDate(); d.setHours(parseInt(time[0], 10)); d.setMinutes(parseInt(time[1], 10)); d.setSeconds(parseInt(time[2], 10)); return d; } // Ende Funktion //----------------------------------------------------------------------------------------------------- // Wird verwendet um State-Werte zu setzen, merkt sich wenn ein eigener State geändert wurde, // um das später ignorieren zu können //----------------------------------------------------------------------------------------------------- function setOwnState(stateId, val) { if(getState (stateId).val !== val) { // if (stateId.indexOf(path) !== -1 && cron === 0) { // erweitert auch auf Datenpunkte if (cron === 0) { if (! ownStateChanges[stateId]) { ownStateChanges[stateId] = 1 // if(trace) {log("Routine setOwnState fuer id "+ stateId + " Zaehler ist 1","info"); } } else { ownStateChanges[stateId]++; // if(trace) {log("Routine setOwnState fuer id "+ stateId + " Zaehler ist "+ ownStateChanges[stateId],"info"); } } } setState(stateId, val); } else { // log ("Routine setOwnState für id "+ stateId + "und Wert "+ val + " war bereits identisch im datenpunkt - kein update", "info") return; } } //----------------------------------------------------------------------------------------------------- // Funktion Update Manual Temperatures for special usage (user exit) - //----------------------------------------------------------------------------------------------------- function UpdateManTemp(room, value) { if(value === undefined ) { return false; } var ViewManDuration = getState(path + "." + room +".View_Manual_Temp_Duration").val; // Dauer der manuellen Aenderung if (ViewManDuration < 0 ) { // keine manuellen Temps erlaubt return false; } for (var x in ControlTab) { // ermittle die Thermostat-ID if (ControlTab[x][0] === room) { var ThermostatID = ControlTab[x][1]; break; } } // endfor Source_ManualAdjustment = "User Exit Temperatur Anpassung " + value; log(" update values - room "+ room + " value "+ value + " id "+ ThermostatID ) Source_NextSollTemp = value; SetTemp(room, value, ThermostatID, true); return true; } // endfunction //----------------------------------------------------------------------------------------------------- // Function UserExitPrep - hat nur die Aufgabe die Routine fuer den Userexit zu ermitteln. //----------------------------------------------------------------------------------------------------- function UserExitPrep (id,value) { if (debug) {log("Routine UserExitPrep aufgerufen " + id + " " + value, "info"); } for (var x in UserExitTab) { if (UserExitTab[x][0] === id ) { UserExit(id, value, x); // uebergabe id, wert und Routine zum eigentlichen UserExit return; // eine ID darf nur einmal aufgerufen werden } // endif } // endfor } // Endfunction //----------------------------------------------------------------------------------------------------- // Funktion UserExit- // Es stehen 5 globale Tabellen zur Verfügung Die Werte dieser Tabellen gehen nicht verloren für den nächsten aufruf // UserExitValueTab1 // UserExitValueTab2 // UserExitValueTab3 // UserExitValueTab4 // UserExitValueTab5 // Rückgabe: // USerExitCallBack(Raum,Solltemperatur,Gueltigkeit) // Die Idee des UserExits ist dass eigenes Coding eingefügt werden kann und bei update des Programmes lediglich durch cut & past wieder hergestellt werden kann. // Zusätzlich sind auch Manipulationen der SollTemp möglich ohne zusätzliches Coding // Aenderungen sollten nur in den Routinen erfolgen //----------------------------------------------------------------------------------------------------- function UserExit (id, value, TabNo) { // id = ausloesender Datenpunkt // value = Inhalt des ausloesenden Datenpunktes // TabNo = uebergabe Tabelle aus der Konfiguration - werden weiter unten in Variable umgesetzt - bitte nicht ändern var UEDatenpunkt = UserExitTab[TabNo][0]; // auslösender Datenpunkt var room = UserExitTab[TabNo][1].replace(/\s/g, "_"); // Raumname mit Unterstrichen falls Leerzeichen verwendet wurden var routine = UserExitTab[TabNo][2]; // Routine aus der Konfiguration var operand = UserExitTab[TabNo][3]; // Compare ist der Wert der mit den Vergleichsoperaturen verglichen wurde (aus der Konfiguration) var vergleichswert = UserExitTab[TabNo][4]; // Vergleichswert ist der Wert der mit dem der Operand den auslösenden Datenpunkt vergleicht (aus der Konfiguration) // Routines to adjust - Die Routinenamen können in der Konfiguration angepasst werden. // Die Routinen können angepasst werden. Wenn ein Wert an das Heizungsscript übergeben werden soll dann wird das mit if (UpdateManTemp(room, value) === true) {.... } gemacht if (routine === "TriggerHeating-any") { log("Routine UserExit TriggerHeating-any aufgerufen " + id + " " + value + " " + routine); if (UpdateManTemp(room, value) === true) { // hier wird die manuelle temperatur eingestellt log("update raum "+ room + " auf Temperatur "+ value + " erfolgreich", "info") }else { log("update raum "+ room + " auf Temperatur "+ value + " nicht erfolgreich", "info") } } if (routine === "TriggerHeating-val") { log("Routine UserExit TriggerHeating-val aufgerufen " + id + " " + value + " " + routine); } if (routine === "TriggerHeating-valGt") { log("Routine UserExit TriggerHeating-valGt aufgerufen " + id + " " + value + " " + routine); } if (routine === "TriggerHeating-valGe") { log("Routine UserExit TriggerHeating-valGe aufgerufen " + id + " " + value + " " + routine); } if (routine === "TriggerHeating-valLt") { log("Routine UserExit TriggerHeating-valLt aufgerufen " + id + " " + value + " " + routine); } if (routine === "TriggerHeating-valLe") { log("Routine UserExit TriggerHeating-valLe aufgerufen " + id + " " + value + " " + routine); } if (routine === "TriggerHeating-valNe") { log("Routine UserExit TriggerHeating-valNe " + id + " " + value + " " + routine); } } // endfunction