From 19c18bd20c0fd561afd6d6aec543b695891a1d95 Mon Sep 17 00:00:00 2001 From: DanielHaefliger Date: Tue, 19 May 2026 09:59:11 +0200 Subject: [PATCH] =?UTF-8?q?Warmwassermodul=20hinzugef=C3=BCgt=20(ersetzt?= =?UTF-8?q?=20Boiler=20x=5FStufig)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ladestation_v2/form.json | 2 +- Warmwasser/README.md | 121 ++++++++++++ Warmwasser/form.json | 121 ++++++++++++ Warmwasser/module.json | 12 ++ Warmwasser/module.php | 403 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 658 insertions(+), 1 deletion(-) create mode 100644 Warmwasser/README.md create mode 100644 Warmwasser/form.json create mode 100644 Warmwasser/module.json create mode 100644 Warmwasser/module.php diff --git a/Ladestation_v2/form.json b/Ladestation_v2/form.json index be54d2e..87e2adb 100644 --- a/Ladestation_v2/form.json +++ b/Ladestation_v2/form.json @@ -3,7 +3,7 @@ { "type": "Select", "name": "Ladestation", - "caption": "Ladestation", + "caption": "Ladestation Typ", "options": [ { "caption": "Go-E Wallbox (Alte Version)", diff --git a/Warmwasser/README.md b/Warmwasser/README.md new file mode 100644 index 0000000..238c803 --- /dev/null +++ b/Warmwasser/README.md @@ -0,0 +1,121 @@ +# Enelix-Verbrauchermodul Warmwasser (Boiler) + +Dieses Modul integriert elektrische Warmwasserboiler mit geschalteten Heizstäben mit variablen Heizstufen in das Belevo Energiemanagement-System (Enelix). Es steuert die Leistungsstufen dynamisch nach PV-Überschuss, Peak-Shaving-Vorgaben, Temperaturvorgaben sowie Zeitplänen und integriert einen automatischen Legionellenschutz. + +--- + +## Inhaltsverzeichnis + +1. [Funktionsumfang](#1-funktionsumfang) +2. [Voraussetzungen](#2-voraussetzungen) +3. [Installation](#3-installation) +4. [Instanz anlegen & Konfiguration](#4-instanz-anlegen--konfiguration) +5. [Statusvariablen & Profile](#5-statusvariablen--profile) +6. [WebFront / Bedienung](#6-webfront--bedienung) +7. [Mapping auf Code-Komponenten](#7-mapping-auf-code-komponenten) +8. [Zukünftige Erweiterungen](#8-zukünftige-erweiterungen) + +--- + +## 1. Funktionsumfang + +- **Stufensteuerung (LeistungsStufen):** Definition beliebiger Leistungsschritte (Watt) mit direkter Zuweisung von IP-Symcon Schaltkontakten (Variablen-IDs). +- **Temperaturüberwachung & Glättung:** Auswertung eines Temperaturfühlers mit optionaler PT1-Glättung (Tiefpassfilter über `ZeitKonstante`), um Messrauschen zu minimieren. +- **Betriebsmodi (PV vs. Peak):** + - *Peak-Shaving:* Heizen nur bei Unterschreitung der `Mindesttemperatur` (Zwangsaufheizung), optional auch ganz deaktivierbar. + - *Solarladen:* Stufenweise Aufheizung bis zur `Maximaltemperatur` mit überschüssiger PV-Leistung. Fällt die Temperatur unter `Mindesttemperatur`, wird mit maximaler Leistung geheizt. +- **Legionellenschutz:** Ein interner Zähler (`LegioCounter`) überwacht die Zeit seit der letzten Hochtemperaturphase. Wird ein Grenzwert überschritten, wird die `Maximaltemperatur` (bzw. später auch die `Mindesttemperatur` im Nachtbetrieb) automatisch auf die `Legionellentemperatur` angehoben. +- **Vorausschauende Zeitpläne (Landwirtschaft):** Über einen JSON-Zeitplan können Zieltemperaturen zu bestimmten Uhrzeiten definiert werden. Das Modul berechnet über die physikalische Wärmekapazität (Volumen, Delta-T) vorausschauend, ob und wann die Heizung aktiviert werden muss, um das Ziel rechtzeitig zu erreichen. +- **Taktschutz (Idle-Logik):** Ein einstellbarer Zähler (`IdleCounterMax`) verhindert ein zu häufiges Schalten der Heizstäbe sowie eine Mindestlaufzeit. + +--- + +## 2. Voraussetzungen + +- IP-Symcon **≥ 8.0** +- Modul-URL: `https://git.belevo.ch/dh/enelix.git` +- Vorhandener und eingerichteter Enelix Manager. +- Physische Schaltaktoren für die Boiler-Heizstäbe. +- Temperaturfühler im Boiler mit Fühlerwert als Variable in Symcon. + +--- + +## 3. Installation + +1. In IP-Symcon **Module Control** öffnen. +2. **Hinzufügen → Git-Repository**. +3. URL eingeben und **Installieren**. +4. IP-Symcon neu starten. + +--- + +## 4. Instanz anlegen & Konfiguration + +### 4.1 Instanz anlegen + +- Rechtsklick **Instanzen** → **Instanz hinzufügen** +- Filter: **Belevo** +- Auswahl: **Warmwasser (Verbraucher)** + +### 4.2 Properties + +| Name | Typ | Beschreibung | +|---|---|---| +| **LeistungsStufen** | String (JSON) | Konfiguration der Leistungsschritte und der verknüpften Aktor-Variablen-IDs. | +| **Boilerfuehler_PT1** | SelectVariable | Variablen-ID des Hardware-Temperaturfühlers. | +| **Boilertemperatur_glätten** | Boolean | Aktiviert das Software-Tiefpassfilter für den Fühlerwert. | +| **ZeitKonstante** | NumberSpinner | Zeitkonstante für die Temperatur-Glättung. | +| **Boilervolumen** | NumberSpinner | Boilervolumen in Litern (Basis für die thermodynamische Berechnung im Zeitplan). | +| **Zeitplan** | String (JSON) | Optionaler Zeitplan für Zieltemperaturen (Format: `[{"Uhrzeit":"06:00","Solltemperatur":55}]`). | +| **Interval** | NumberSpinner | Berechnungsintervall in Sekunden. | +| **IdleCounterMax** | NumberSpinner | Anzahl Intervall-Zyklen zwischen zwei Schaltvorgängen (Verzögerung). | + +--- + +## 5. Statusvariablen & Profile + +| Ident | Typ | Beschreibung | +|---|---|---| +| **Mindesttemperatur** | Integer | Unteres Limit. Wird dieser Wert unterschritten, wird immer geheizt (Netzbezug). | +| **Maximaltemperatur** | Integer | Oberes Limit für PV-Überschussladung. | +| **Legionellentemperatur**| Integer | Zieltemperatur für den automatischen Legionellenzyklus (z.B. 65°C). | +| **Boilertemperatur** | Float | Aktuelle, ggf. geglättete Ist-Temperatur des Boilers. | +| **LegioCounter** | Integer | Interner Zähler für die Laufzeit seit der letzten Legionellen-Desinfektion. | +| **Aktuelle_Leistung** | Integer | Die aktuell vom Manager zugewiesene Soll-Leistung in Watt. | +| **PowerSteps** | String | JSON-Array der dem Manager aktuell angebotenen möglichen Leistungsschritte. | +| **Idle** | Boolean | Status der Sperrzeit (`true` = bereit für neue Werte, `false` = gesperrt). | +| **Sperre_Prio** | Integer | Priorität für die Leistungszuteilung im Peak-Shaving-Modus. | +| **PV_Prio** | Integer | Priorität für die Leistungszuteilung im Solarlade-Modus. | +| **Bezogene_Energie** | Float | Rechnerisch aufsummierte Energie (Leistung × Zeit). | +| **Is_Peak_Shaving** | Boolean | Vom Manager übermittelter aktueller Betriebsmodus. | + +Für alle Sollwerte wird jeweils eine Hysterese von 5 °C miteingehalten. +--- + +## 6. WebFront / Bedienung + +Im WebFront dient die Instanz zur Kontrolle und Anpassung der Temperaturvorgaben: +- Einstellen von `Mindesttemperatur`, `Maximaltemperatur` und `Legionellentemperatur`. +- Überwachung der aktuellen `Boilertemperatur`. +- Visualisierung der vom Manager freigegebenen `Aktuelle_Leistung`. +- Die Anpassung von Prioritäten (`PV_Prio`, `Sperre_Prio`). + +--- + +## 7. Mapping auf Code-Komponenten + +| Komponente | Funktion im Code (`module.php`) | +|---|---| +| **Datenaufbereitung** | `LadeUndSortiereLeistungen()` decodiert das JSON-Array `LeistungsStufen` und sortiert die Watt-Werte aufsteigend. | +| **Temperaturberechnung**| `GetCurrentData()` liest den PT1-Fühler aus und berechnet abhängig von `Boilertemperatur_glätten` den gleitenden Durchschnitt. | +| **Zeitplan & Thermodynamik**| `calculateRequiredHeat()` und `canBoilerReachTemperature()` berechnen über Masse, Temperaturdifferenz und spezifische Wärmekapazität, ob die Restzeit zur Zielerreichung ausreicht. | +| **Betriebslogik & Limits**| `GetCurrentData($Peak)` wertet Temperaturen, Legionellen-Zähler und Peak-Shaving-Status aus und passt die angebotenen `PowerSteps` an. | +| **Leistungszuteilung** | `SetAktuelle_Leistung($power)` ermittelt über `GetKontaktIDZuLeistung()` die passende Aktor-ID und schaltet exakt den geforderten Heizstab ein (und alle anderen aus). | +| **Legionellenschutz** | Erhöht `LegioCounter` zyklisch. Bei Überschreiten definierter Grenzen (z.B. > 69120 Zyklen) wird `MaxTemp` = `LegioTemp` gesetzt. Priorisierter Nachtbetrieb via `ist_nachts()`. | +| **Taktschutz (Idle)** | `CheckIdle()` und `ProcessIdleCounter()` setzen bei Leistungsänderungen den Timer, um erneute Schaltvorgänge temporär zu blockieren. | + +--- + +## 8. Zukünftige Erweiterungen + +- **Temperaturschichtung:** Unterstützung für zwei Fühler (Boiler Oben / Boiler Unten) zur präziseren Erkennung der durchgeladenen Energie. \ No newline at end of file diff --git a/Warmwasser/form.json b/Warmwasser/form.json new file mode 100644 index 0000000..0d6157c --- /dev/null +++ b/Warmwasser/form.json @@ -0,0 +1,121 @@ +{ + "elements": [ + { + "type": "Label", + "caption": "Konfiguration der nötigen Schaltkontakte und Nennleistungen" + }, + { + "type":"Select", + "name":"Boilertemperatur_glätten", + "caption":"Boilertemperatur glätten", + "options":[ + { + "caption":"Ja", + "value":true + }, + { + "caption":"Nein", + "value":false + } + ] + }, + { + "type": "NumberSpinner", + "name": "ZeitKonstante", + "caption": "Zeit Konstante", + "suffix": "" + }, + { + "type": "NumberSpinner", + "name": "Interval", + "caption": "Intervall Neuberechnung der Werte Erst für spätere Versionen, aktuell auf 5 lassen!", + "suffix": "Sekunden" + }, + { + "type": "NumberSpinner", + "name": "IdleCounterMax", + "caption": "Zyklen zwischen zwei Leistungsänderungen (Multipliziert sich mit Interval)", + "suffix": "" + }, + { + "type":"List", + "name":"LeistungsStufen", + "caption":"Anzahl Stufen mit den dazugehörigen Leistungen", + "add":true, + "delete":true, + "columns":[ + { + "caption":"Stufe(n)", + "name":"Stufe", + "width":"200px", + "add":"Stufe", + "edit":{ + "type":"NumberSpinner" + } + }, + { + "caption":"Leistung in W", + "name":"Leistung", + "width":"300px", + "add":0, + "edit":{ + "type":"NumberSpinner" + } + }, + { + "caption":"Schaltkontakt", + "name":"Schaltkontakt_Stufe", + "width":"400px", + "add":0, + "edit":{ + "type":"SelectVariable" + } + } + + ] + }, + { + "type": "NumberSpinner", + "name": "Boilervolumen", + "caption": "Boilervolumen", + "suffix": "Liter" + }, + { + "type": "SelectVariable", + "name": "Boilerfuehler_PT1", + "caption": "Variable für Boilerfühler PT1", + "test": true + }, + { + "type": "List", + "name": "Zeitplan", + "caption": "Zeitplan für Solltemperaturen", + "columns": [ + { + "caption": "Uhrzeit", + "name": "Uhrzeit", + "width": "150px", + "add": "00:00", + "edit": { + "type": "ValidationTextBox" + } + }, + { + "caption": "Solltemperatur", + "name": "Solltemperatur", + "width": "150px", + "add": 0, + "edit": { + "type": "NumberSpinner" + } + } + ], + "add": true, + "delete": true, + "sort": { + "column": "Uhrzeit", + "direction": "ascending" + } + } + ] +} \ No newline at end of file diff --git a/Warmwasser/module.json b/Warmwasser/module.json new file mode 100644 index 0000000..c32d7a9 --- /dev/null +++ b/Warmwasser/module.json @@ -0,0 +1,12 @@ +{ + "id": "{72B9606B-D5E6-6033-F2E4-0B2C5533F726}", + "name": "Warmwasser", + "type": 3, + "vendor": "Belevo AG", + "aliases": [], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "GEF", + "url": "" +} \ No newline at end of file diff --git a/Warmwasser/module.php b/Warmwasser/module.php new file mode 100644 index 0000000..3279405 --- /dev/null +++ b/Warmwasser/module.php @@ -0,0 +1,403 @@ +RegisterPropertyString("LeistungsStufen", json_encode([])); // Bezeichnung der Liste + $this->RegisterPropertyInteger("ZeitKonstante", 120); + $this->RegisterPropertyInteger("Boilerfuehler_PT1", 0); + $this->RegisterPropertyBoolean("Boilertemperatur_glätten", false); + $this->RegisterPropertyInteger("Boilervolumen", 300); + $this->RegisterPropertyString("Zeitplan", ""); + $this->RegisterPropertyInteger("Interval", 5); // Recheninterval + // Boiler spezifische Variablen + $this->RegisterVariableInteger("Mindesttemperatur", "Mindesttemperatur", "", 45); + $this->RegisterVariableInteger("Maximaltemperatur", "Maximaltemperatur", "", 60); + $this->RegisterVariableInteger("Legionellentemperatur", "Legionellentemperatur", "", 65); + $this->RegisterVariableInteger("LegioCounter", "LegioCounter", "", 0); + $this->RegisterVariableInteger("Boilertemperatur", "Boilertemperatur", "", 0); + // Variabeln für Kommunkation mit Manager + $this->RegisterVariableInteger("Sperre_Prio", "Sperre_Prio"); + $this->RegisterVariableInteger("PV_Prio", "PV_Prio"); + $this->RegisterVariableBoolean("Idle", "Idle", "", 0); + $this->RegisterVariableInteger("Aktuelle_Leistung", "Aktuelle_Leistung", "", 0); + $this->RegisterVariableFloat("Bezogene_Energie", "Bezogene_Energie", "", 0); + $this->RegisterVariableString("PowerSteps", "PowerSteps"); + $this->RegisterVariableInteger("Power", "Power", '', 0); + $this->RegisterVariableBoolean("Is_Peak_Shaving", "Is_Peak_Shaving", "", true); + $this->RegisterVariableInteger("Leistung_Delta", "Leistung_Delta", "", 0); + // Hilfsvariabeln für Idle zustand + $this->RegisterPropertyInteger("IdleCounterMax", 2); + $this->RegisterVariableInteger("IdleCounter", "IdleCounter", "", 0); + $this->SetValue("IdleCounter", 0); + // Initialisiere Idle + $this->SetValue("Idle", true); + $this->RegisterTimer("Timer_Do_UserCalc_Boiler", $this->ReadPropertyInteger("Interval") * 1000, "IPS_RequestAction(" . $this->InstanceID . ', "Do_UserCalc", "");'); + } + public function ApplyChanges() + { + parent::ApplyChanges(); + $this->SetTimerInterval("Timer_Do_UserCalc_Boiler", $this->ReadPropertyInteger("Interval") * 1000); + } + public function RequestAction($Ident, $Value) + { + switch ($Ident) + { + case "SetAktuelle_Leistung": + $this->SetValue("Power", (int)$Value); + break; + case "GetCurrentData": + $this->SetValue("Is_Peak_Shaving", (bool)$Value); + break; + case "Do_UserCalc": + $this->SetAktuelle_Leistung($this->GetValue("Power")); + $this->GetCurrentData($this->GetValue("Is_Peak_Shaving")); + break; + default: + break; + } + } + private function LadeUndSortiereLeistungen() + { + // Array initialisieren, immer mit 0 + $this->leistungArray = [0]; + // JSON aus der Property auslesen + $json = $this->ReadPropertyString("LeistungsStufen"); + $leistungsStufen = json_decode($json, true); + if (is_array($leistungsStufen)) + { + foreach ($leistungsStufen as $stufe) + { + $this->leistungArray[] = $stufe['Leistung'] ? ? 0; + } + } + // Array sortieren + $len = count($this->leistungArray); + for ($i = 0;$i < $len - 1;$i++) + { + for ($j = 0;$j < $len - $i - 1;$j++) + { + if ($this->leistungArray[$j] > $this->leistungArray[$j + 1]) + { + $temp = $this->leistungArray[$j]; + $this->leistungArray[$j] = $this->leistungArray[$j + 1]; + $this->leistungArray[$j + 1] = $temp; + } + } + } + } + // Spezialfunktion für Landwirtschaft, Boiler kann auf Uhrzeiten mit einer Solltemperatur beheizt werden + public function getNextTimeAndTemperature($zeitplan) + { + $arr = json_decode($zeitplan, true); + if (empty($arr)) + { + return null; + } + $currentTime = new DateTime(); + $nextEntry = null; + $minDiff = PHP_INT_MAX; + foreach ($arr as $entry) + { + $entryTime = DateTime::createFromFormat('H:i', $entry['Uhrzeit']); + if ($entryTime < $currentTime) + { + $entryTime->modify('+1 day'); + } + $diff = $currentTime->diff($entryTime)->format('%r%a') * 24 * 60 + $currentTime->diff($entryTime)->format('%r%h') * 60 + $currentTime->diff($entryTime)->format('%r%i'); + if ($diff < $minDiff) + { + $minDiff = $diff; + $nextEntry = $entry; + } + } + return $nextEntry; + } + // zeit berechnen bis zum nächsten "warmsein" + public function calculateRemainingTime($nextTime) + { + $currentTime = new DateTime(); + $nextDateTime = DateTime::createFromFormat('H:i', $nextTime); + if ($nextDateTime < $currentTime) + { + $nextDateTime->modify('+1 day'); + } + $interval = $currentTime->diff($nextDateTime); + return $interval->h + ($interval->i / 60); + } + // Wärmemenge berechnen zum nächsten warm sein + public function calculateRequiredHeat($boilervolumen, $tempDiff) + { + // Annahme: spezifische Wärmekapazität von Wasser = 4.186 J/g°C + // 1 Liter Wasser = 1000 Gramm + $specificHeatCapacity = 4.186; // J/g°C + $waterMass = $boilervolumen * 1000; // Gramm + return $specificHeatCapacity * $waterMass * $tempDiff; // Joules + + } + // Reicht zeit noch aus um wärmemenge zu errecihen , sonst dann später einschalten + public function canBoilerReachTemperature($boilervolumen, $boilerTemper, $nextTemp, $remainingTime, $vollleistung) + { + $tempDiff = $nextTemp - $boilerTemper; + $requiredHeat = $this->calculateRequiredHeat($boilervolumen, $tempDiff); + $availableHeat = $vollleistung * $remainingTime * 3600; // Leistung in Watt * Zeit in Sekunden + return $availableHeat >= $requiredHeat; + } + // Methode zum Setzen des aktuellen Stromverbrauchs + public function SetAktuelle_Leistung(int $power) + { + // Lade sicherheitshalber das aktuelle LeistungArray + $this->LadeUndSortiereLeistungen(); + // Schleife über alle Leistungsstufen + foreach ($this->leistungArray as $leistung) + { + $kontaktID = $this->GetKontaktIDZuLeistung($leistung); + // Prüfen, ob Variable existiert und gültige ID + if ($kontaktID > 0 && IPS_VariableExists($kontaktID)) + { + // Setze TRUE für die aktuelle Leistungsstufe, FALSE für alle anderen + SetValue($kontaktID, ($leistung === $power)); + } + else + { + if ($kontaktID > 0) + { + IPS_LogMessage("ERROR", "KontaktID $kontaktID existiert nicht oder ist ungültig!"); + } + } + } + // Prüfe auf Änderung der Power im Vergleich zur letzten Einstellung + $lastPower = GetValue($this->GetIDForIdent("Aktuelle_Leistung")); + if ($power != $lastPower) + { + $this->SetValue("Idle", false); + $this->SetValue("IdleCounter", $this->ReadPropertyInteger("IdleCounterMax")); + } + // Setze die neue Aktuelle_Leistung + $this->SetValue("Aktuelle_Leistung", $power); + $this->SetValue("Bezogene_Energie", ($this->GetValue("Bezogene_Energie") + ($this->GetValue("Aktuelle_Leistung") * ($this->ReadPropertyInteger("Interval") / 3600)))); + // IdleCounter verarbeiten + $this->ProcessIdleCounter(); + } + // Variabeln id zu leistung schalten + private function GetKontaktIDZuLeistung(int $leistung) : int + { + $json = $this->ReadPropertyString("LeistungsStufen"); + $leistungsStufen = json_decode($json, true); + if (is_array($leistungsStufen)) + { + foreach ($leistungsStufen as $stufe) + { + if (($stufe['Leistung'] ? ? 0) == $leistung) + { + return $stufe['Schaltkontakt_Stufe'] ? ? 0; + } + } + } + return 0; + } + // Methode zum Abrufen der aktuellen Daten + public function GetCurrentData(bool $Peak) + { + $LegioCounter = $this->GetValue("LegioCounter"); + $this->LadeUndSortiereLeistungen(); + $boilertemperatur_glätten = $this->ReadPropertyBoolean("Boilertemperatur_glätten"); + if ($boilertemperatur_glätten) + { + // Wenn Glättung aktiviert ist, führe das Glätten durch + $boilerFuehlerPT1ID = $this->ReadPropertyInteger("Boilerfuehler_PT1"); + if (IPS_VariableExists($boilerFuehlerPT1ID)) + { + $boilerPT1 = GetValue($boilerFuehlerPT1ID); + } + else + { + $boilerPT1 = 0.0; // Standardwert + + } + $boilerTempID = $this->GetIDForIdent("Boilertemperatur"); + if (IPS_VariableExists($boilerTempID)) + { + $boilerTemp = $this->GetValue("Boilertemperatur"); + } + else + { + $boilerTemp = 0.0; // Standardwert + + } + // PT + $time_constant = $this->ReadPropertyInteger("ZeitKonstante"); + $delta_t = 5; // Zeitdifferenz zwischen den Messungen (30 Sekunden) + $alpha = $delta_t / ($time_constant + $delta_t); + $newBoilerTemp = $boilerTemp + $alpha * ($boilerPT1 - $boilerTemp); + $this->SetValue("Boilertemperatur", $newBoilerTemp); + } + else + { + // Wenn Glättung nicht aktiviert ist, setze die Boilertemperatur direkt auf den Wert des Boilerfühlers + $boilerFuehlerPT1ID = $this->ReadPropertyInteger("Boilerfuehler_PT1"); + if (IPS_VariableExists($boilerFuehlerPT1ID)) + { + $boilerPT1 = GetValue($boilerFuehlerPT1ID); + } + else + { + $boilerPT1 = 0.0; // Standardwert + + } + // Setze Boilertemperatur direkt auf den Wert des Boilerfühlers + $this->SetValue("Boilertemperatur", $boilerPT1); + } + $boilerTemp = $this->GetValue("Boilertemperatur"); + $minTemp = $this->GetValue("Mindesttemperatur"); + $maxTemp = $this->GetValue("Maximaltemperatur"); + $LegioTemp = $this->GetValue("Legionellentemperatur"); + $nextEntry = $this->getNextTimeAndTemperature($this->ReadPropertyString("Zeitplan")); + if ($nextEntry !== null) + { + $remainingTime = $this->calculateRemainingTime($nextEntry['Uhrzeit']); + $nextTemp = $nextEntry['Solltemperatur']; + if (!$this->canBoilerReachTemperature($this->ReadPropertyInteger("Boilervolumen") , $boilerTemp, $nextTemp, $remainingTime, $vollLeistung)) + { + $minTemp = $nextTemp; + } + } + // Falluntercheidung auf basis obn wieder legionellen nötig sind + if ($boilerTemp > $LegioTemp) + { + $LegioCounter = 0; + } + else + { + $LegioCounter = $LegioCounter + 1; + } + if ($LegioCounter > 69120) + { + $maxTemp = $LegioTemp; + } + if ($LegioCounter > 120960 && $this->ist_nachts()) + { + $minTemp = $LegioTemp; + } + if ($LegioCounter > 138240) + { // Timeout für Legio wenn temperatur nicht erreicht werden kann, setze legionellenfunktion zurück + $LegioCounter = 0; + } + $this->SetValue("LegioCounter", $LegioCounter); + // Fallunterscheidung ob peakbetrieb ist + if ($Peak) + { + if ($boilerTemp < $minTemp) + { + $this->SetValue("PowerSteps", json_encode($this->leistungArray)); + } + elseif ($boilerTemp < $minTemp + 5 && $this->IstEineStufeAktiv()) + { + $this->SetValue("PowerSteps", json_encode($this->leistungArray)); + } + else + { + $this->SetValue("PowerSteps", json_encode([0])); + } + } + else + { + if ($boilerTemp < $minTemp) + { + $this->SetValue("PowerSteps", json_encode([max($this->leistungArray) ])); + } + elseif ($boilerTemp < $minTemp + 5 && $this->IstEineStufeAktiv()) + { + $this->SetValue("PowerSteps", json_encode([max($this->leistungArray) ])); + } + elseif ($boilerTemp < $maxTemp - 5) + { + $this->SetValue("PowerSteps", json_encode($this->leistungArray)); + } + elseif ($boilerTemp < $maxTemp && $this->IstEineStufeAktiv()) + { + $this->SetValue("PowerSteps", json_encode($this->leistungArray)); + } + else + { + $this->SetValue("PowerSteps", json_encode([0])); + } + } + } + private function IstEineStufeAktiv() : bool + { + $json = $this->ReadPropertyString("LeistungsStufen"); + $leistungsStufen = json_decode($json, true); + if (is_array($leistungsStufen)) + { + foreach ($leistungsStufen as $stufe) + { + $kontaktID = $stufe['Schaltkontakt_Stufe'] ? ? 0; + if ($kontaktID > 0 && IPS_VariableExists($kontaktID)) + { + if (GetValue($kontaktID) === true) + { + return true; // mindestens eine Stufe ist aktiv + + } + } + } + } + return false; // keine aktiv + + } + private function ProcessIdleCounter() + { + // IdleCounter auslesen und verarbeiten + $idleCounter = $this->GetValue("IdleCounter"); + if ($idleCounter > 0) + { + $this->SetValue("Idle", false); + $this->SetValue("IdleCounter", $idleCounter - 1); + } + else + { + $this->SetValue("Idle", true); + } + } + private function CheckIdle($power) + { + $lastpower = GetValue("Aktuelle_Leistung"); + if ($lastpower != GetValue("Aktuelle_Leistung")) + { + $this->SetValue("Idle", false); + $this->SetValue("IdleCounter", $this->ReadPropertyInteger("IdleCounterMax")); + } + // IdleCounter auslesen und verarbeiten + $idleCounter = $this->GetValue("IdleCounter"); + if ($idleCounter > 0) + { + $this->SetValue("Idle", false); + $this->SetValue("IdleCounter", $idleCounter - 1); + } + else + { + $this->SetValue("Idle", true); + } + } + //Hilfsfunktion für nachtbetrieb, wenn nachttarif noch aktiv ist + private function ist_nachts() + { + date_default_timezone_set("Europe/Berlin"); // Setze hier deine Zeitzone + $aktuelle_zeit = strtotime(date("H:i")); // Aktuelle Zeit in Stunden und Minuten umwandeln + $start_nacht = strtotime("22:00"); // Startzeit der Nacht (22 Uhr) + $ende_nacht = strtotime("07:00"); // Endzeit der Nacht (7 Uhr) + if ($aktuelle_zeit >= $start_nacht || $aktuelle_zeit < $ende_nacht) + { + return true; + } + else + { + return false; + } + } +} +?>