Energiemanager optimiert, Easee-Ladestation eingefügt.
This commit is contained in:
@@ -25,13 +25,7 @@
|
||||
{
|
||||
"type": "SelectVariable",
|
||||
"name": "Variable_Leistung",
|
||||
"caption": "Variable mit der Nennleistung",
|
||||
"test": true
|
||||
},
|
||||
{
|
||||
"type": "SelectVariable",
|
||||
"name": "Variable_Temperatur",
|
||||
"caption": "Variable mit der Solltemperatur Boiler",
|
||||
"caption": "Variable mit der Heizstufe",
|
||||
"test": true
|
||||
},
|
||||
{
|
||||
|
||||
@@ -12,10 +12,9 @@ class Ansteuerung_Askoheat extends IPSModule
|
||||
$this->RegisterPropertyBoolean("Boilertemperatur_glätten", false);
|
||||
$this->RegisterPropertyInteger("Boilervolumen", 300);
|
||||
$this->RegisterPropertyString("Zeitplan", "");
|
||||
$this->RegisterPropertyInteger("BoilerLeistung", 3500); // Standardwert für Volllast
|
||||
$this->RegisterPropertyInteger("BoilerLeistung", 4400); // Standardwert für Volllast
|
||||
$this->RegisterPropertyInteger("Interval", 5); // Recheninterval
|
||||
$this->RegisterPropertyInteger("Variable_Leistung", 0); // Recheninterval
|
||||
$this->RegisterPropertyInteger("Variable_Temperatur", 0); // Recheninterval
|
||||
$this->RegisterPropertyInteger("Variable_Temperatur_Ist", 0); // Recheninterval
|
||||
|
||||
// Variabeln für Kommunkation mit Manager
|
||||
@@ -132,7 +131,15 @@ class Ansteuerung_Askoheat extends IPSModule
|
||||
// Methode zum Setzen des aktuellen Stromverbrauchs
|
||||
public function SetAktuelle_Leistung(int $power)
|
||||
{
|
||||
RequestAction($this->ReadPropertyInteger("Variable_Leistung"), $power);
|
||||
if($power>0){
|
||||
|
||||
RequestAction($this->ReadPropertyInteger("Variable_Leistung"), round($power / round(($this->ReadPropertyInteger("BoilerLeistung")/7))));
|
||||
|
||||
}else{
|
||||
|
||||
RequestAction($this->ReadPropertyInteger("Variable_Leistung"), 0);
|
||||
|
||||
}
|
||||
|
||||
// Prüfe auf Änderung der Power im Vergleich zur letzten Einstellung
|
||||
$lastPower = GetValue($this->GetIDForIdent("Aktuelle_Leistung"));
|
||||
@@ -156,7 +163,7 @@ class Ansteuerung_Askoheat extends IPSModule
|
||||
public function Calc_Seven_Steps(int $power)
|
||||
{
|
||||
$steps = [];
|
||||
$increment = $power / 7;
|
||||
$increment = round($power / 7);
|
||||
|
||||
for ($i = 0; $i <= 7; $i++) {
|
||||
$steps[] = $i * $increment;
|
||||
|
||||
@@ -1,67 +1,95 @@
|
||||
# Manager_1
|
||||
Beschreibung des Moduls.
|
||||
# Belevo EMS Hauptmanager
|
||||
|
||||
### Inhaltsverzeichnis
|
||||
Das **Belevo EMS Hauptmanager**-Modul fasst mehrere untergeordnete **Belevo EMS Manager**-Instanzen zusammen und verteilt die globale verfügbare Leistung direkt auf die einzelnen Verbraucher-Instanzen – nicht nur auf die Manager. Jeder Unter-Manager meldet seine Verbraucher-Daten als JSON, der Hauptmanager berechnet die finalen Zuweisungen und liefert für jeden Unter-Manager ein JSON mit den fertigen Verbraucher-Leistungen zurück.
|
||||
|
||||
1. [Funktionsumfang](#1-funktionsumfang)
|
||||
2. [Voraussetzungen](#2-voraussetzungen)
|
||||
3. [Software-Installation](#3-software-installation)
|
||||
4. [Einrichten der Instanzen in IP-Symcon](#4-einrichten-der-instanzen-in-ip-symcon)
|
||||
5. [Statusvariablen und Profile](#5-statusvariablen-und-profile)
|
||||
6. [WebFront](#6-webfront)
|
||||
7. [PHP-Befehlsreferenz](#7-php-befehlsreferenz)
|
||||
---
|
||||
|
||||
### 1. Funktionsumfang
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
*
|
||||
1. [Beschreibung](#beschreibung)
|
||||
2. [Voraussetzungen](#voraussetzungen)
|
||||
3. [Installation](#installation)
|
||||
4. [Instanz anlegen & Konfiguration](#instanz-anlegen--konfiguration)
|
||||
5. [Statusvariablen](#statusvariablen)
|
||||
6. [Funktionsweise](#funktionsweise)
|
||||
1. [Mode-Entscheid](#1-mode-entscheid)
|
||||
2. [Datenaggregation](#2-datenaggregation)
|
||||
3. [Globale Soll-Ist-Differenz](#3-globale-soll-ist-differenz)
|
||||
4. [Verbraucher-Priorisierung](#4-verbraucher-priorisierung)
|
||||
5. [Leistungszuweisung](#5-leistungszuweisung)
|
||||
6. [Rückgabe an Unter-Manager](#6-rückgabe-an-unter-manager)
|
||||
7. [Beispiel-Workflow](#beispiel-workflow)
|
||||
8. [Mapping auf Code-Komponenten](#mapping-auf-code-komponenten)
|
||||
9. [Zukünftige Erweiterungen](#zukünftige-erweiterungen)
|
||||
10. [Support](#support)
|
||||
|
||||
### 2. Voraussetzungen
|
||||
---
|
||||
|
||||
- IP-Symcon ab Version 7.1
|
||||
## Beschreibung
|
||||
|
||||
### 3. Software-Installation
|
||||
Der Hauptmanager koordiniert beliebig viele Unter-Manager (z. B. Hausanschluss, ZEV-Cluster, LEG-Center). Jeder Unter-Manager sammelt in seinem Zyklus die Daten aller angeschlossenen Verbraucher (JSON-Array mit Feldern wie `deviceID`, `priority`, `requestedLevels`, `currentDrawn`, `receivedTotal` usw.) und stellt dieses JSON in seiner `DatenZurueck`-Variable bereit. Der Hauptmanager
|
||||
|
||||
* Über den Module Store das 'Manager_1'-Modul installieren.
|
||||
* Alternativ über das Module Control folgende URL hinzufügen
|
||||
1. liest alle `DatenZurueck` JSONs der Unter-Manager ein,
|
||||
2. entscheidet global über Solarlade- oder Peak-Shaving-Mode,
|
||||
3. berechnet eine einzige **globale** Soll–Ist-Differenz,
|
||||
4. führt Priorisierung und Fair-Round-Robin über **alle** Verbraucher durch,
|
||||
5. verteilt die verfügbare Leistung in Stufen (je Verbraucher aus `PowerSteps`),
|
||||
6. erzeugt für jeden Unter-Manager ein JSON mit den finalen `assignedLevel`-Werten pro `deviceID`,
|
||||
7. schreibt diese JSONs in die jeweilige `DatenHoch`-Variable.
|
||||
|
||||
### 4. Einrichten der Instanzen in IP-Symcon
|
||||
Die Unter-Manager übernehmen das JSON und senden die fertigen `assignedLevel`-Werte an ihre lokalen Verbraucher-Instanzen.
|
||||
|
||||
Unter 'Instanz hinzufügen' kann das 'Manager_1'-Modul mithilfe des Schnellfilters gefunden werden.
|
||||
- Weitere Informationen zum Hinzufügen von Instanzen in der [Dokumentation der Instanzen](https://www.symcon.de/service/dokumentation/konzepte/instanzen/#Instanz_hinzufügen)
|
||||
---
|
||||
|
||||
__Konfigurationsseite__:
|
||||
## Voraussetzungen
|
||||
|
||||
Name | Beschreibung
|
||||
-------- | ------------------
|
||||
|
|
||||
|
|
||||
- IP-Symcon **≥ 8.0**
|
||||
- Bereits installierte **Belevo EMS Manager**-Instanzen
|
||||
- Zugriff auf Git-Repository:
|
||||
https://git.belevo.ch/dh/Symcon_Belevo_Energiemanagement_testing.git
|
||||
|
||||
### 5. Statusvariablen und Profile
|
||||
yaml
|
||||
Kopieren
|
||||
Bearbeiten
|
||||
|
||||
Die Statusvariablen/Kategorien werden automatisch angelegt. Das Löschen einzelner kann zu Fehlfunktionen führen.
|
||||
---
|
||||
|
||||
#### Statusvariablen
|
||||
## Installation
|
||||
|
||||
Name | Typ | Beschreibung
|
||||
------ | ------- | ------------
|
||||
| |
|
||||
| |
|
||||
1. In IP-Symcon **Module Control** öffnen
|
||||
2. **Hinzufügen → Git-Repository**
|
||||
3. URL eintragen (s. o.)
|
||||
4. Modul **„Belevo EMS Hauptmanager“** installieren
|
||||
5. IP-Symcon neu starten
|
||||
|
||||
#### Profile
|
||||
---
|
||||
|
||||
Name | Typ
|
||||
------ | -------
|
||||
|
|
||||
|
|
||||
## Instanz anlegen & Konfiguration
|
||||
|
||||
### 6. WebFront
|
||||
1. Rechtsklick auf **Instanzen** → **Instanz hinzufügen**
|
||||
2. Filter: **Belevo**
|
||||
3. **„Belevo EMS Hauptmanager“** auswählen und Instanz erstellen
|
||||
|
||||
Die Funktionalität, die das Modul im WebFront bietet.
|
||||
### Properties
|
||||
|
||||
### 7. PHP-Befehlsreferenz
|
||||
| Name | Typ | Beschreibung |
|
||||
|------------------------|----------------|------------------------------------------------------------------------------|
|
||||
| **HauptmanagerAktiv** | Boolean | Schaltet globale Verteilungslogik ein/aus |
|
||||
| **Interval** | Integer (s) | Zyklusintervall für Verteilung und Mode-Entscheid |
|
||||
| **Sollleistung_Max** | Float (W) | Max. Gesamtleistung, die verteilt werden darf |
|
||||
| **Ueberschussleistung**| Float (W) | Untergrenze für Solarlade-Mode (z. B. 0 W) |
|
||||
| **Manager_Liste** | InstanceList | Liste aller untergeordneten Belevo EMS Manager-Instanzen |
|
||||
| **DatenZurueck** | SelectVariable | Variable-ID (Integer) in jeder Unter-Manager-Instanz, aus der JSON gelesen wird |
|
||||
| **DatenHoch** | SelectVariable | Variable-ID in jeder Unter-Manager-Instanz, in die JSON geschrieben wird |
|
||||
|
||||
`boolean GEF_BeispielFunktion(integer $InstanzID);`
|
||||
Erklärung der Funktion.
|
||||
---
|
||||
|
||||
Beispiel:
|
||||
`GEF_BeispielFunktion(12345);`
|
||||
## Statusvariablen
|
||||
|
||||
| Ident | Typ | Profil | Beschreibung |
|
||||
|--------------------------|-----------|--------------|-----------------------------------------------------|
|
||||
| **LetzteBerechnung** | DateTime | — | Zeitstempel der letzten Verteilung |
|
||||
| **Globale_Differenz** | Float | ~Watt~~W~ | Zuletzt berechnete Soll–Ist-Differenz global (W) |
|
||||
| **Anzahl_Manager** | Integer | — | Anzahl aktuell verbundener Unter-Manager |
|
||||
|
||||
---
|
||||
@@ -110,12 +110,12 @@ class HauptManager extends IPSModule
|
||||
}
|
||||
|
||||
// Addiere die aktuell bereits verwendete Leistung auf, um sie bei der verteilung zu berücksichtigen
|
||||
if(in_array(0, $user["PowerSteps"], true)){
|
||||
//if(in_array(0, $user["PowerSteps"], true)){
|
||||
|
||||
// Addiere die aktuell bereits verwendete Leistung auf, um sie bei der verteilung zu berücksichtigen
|
||||
$totalAktuelle_Leistung += ($user["Aktuelle_Leistung"]- $user["Leistung_Delta"]);
|
||||
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
// Berücksichtigung der bereits verteilten Leistungen (nachher kann dafür wieder bei 0 begonnen werden zu verteilen)
|
||||
@@ -206,9 +206,11 @@ class HauptManager extends IPSModule
|
||||
if (empty($samePriorityUsers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$withZero = [];
|
||||
$withoutZero = [];
|
||||
// Verbraucher die nicht 0 Annhemen können, bekommen einfach den tiefsten wert
|
||||
$withoutZeroHigh = [];
|
||||
$withoutZeroLow = [];
|
||||
/* Alter Verteilalgor. zu testzwecken auskommentiert, wird dann gelöscht wenns funktioniert.
|
||||
foreach ($samePriorityUsers as $entry) {
|
||||
if (min($entry["PowerSteps"]) <= 0) {
|
||||
$withZero[] = $entry;
|
||||
@@ -217,18 +219,55 @@ class HauptManager extends IPSModule
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert
|
||||
if (!empty($withoutZero)) {
|
||||
foreach ($withoutZero as $entry) {
|
||||
$resultArray[] = [
|
||||
'user' => $user['InstanceID'],
|
||||
'Writeback' => $user['Writeback'],
|
||||
'Set_Leistung' => min($entry["PowerSteps"])
|
||||
];
|
||||
$instanceID = $entry["InstanceID"];
|
||||
$minPowerStep = min($entry["PowerSteps"]);
|
||||
|
||||
IPS_RequestAction($instanceID,"SetAktuelle_Leistung",$minPowerStep);
|
||||
//$remainingPower -= $entry["Aktuelle_Leistung"];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/* Neuer Block */
|
||||
foreach ($samePriorityUsers as $entry) {
|
||||
$withZero[] = $entry;
|
||||
|
||||
if (min($entry["PowerSteps"]) > 0) {
|
||||
|
||||
$withoutZeroHigh[] = $entry;
|
||||
}
|
||||
if (max($entry["PowerSteps"]) < 0) {
|
||||
|
||||
$withoutZeroLow[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
// Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert
|
||||
if (!empty($withoutZeroHigh)) {
|
||||
foreach ($withoutZeroHigh as $entry) {
|
||||
$instanceID = $entry["InstanceID"];
|
||||
$minPowerStep = min($entry["PowerSteps"]);
|
||||
|
||||
$remainingPower -= $minPowerStep;
|
||||
}
|
||||
}
|
||||
// Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert
|
||||
if (!empty($withoutZeroLow)) {
|
||||
foreach ($withoutZeroLow as $entry) {
|
||||
$instanceID = $entry["InstanceID"];
|
||||
$minPowerStep = max($entry["PowerSteps"]);
|
||||
|
||||
$remainingPower += $minPowerStep;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Neuer Block Ende */
|
||||
|
||||
|
||||
|
||||
// Nun die verteilen, die 0 erhalten können.
|
||||
$samePriorityUsers = $withZero;
|
||||
@@ -299,22 +338,36 @@ class HauptManager extends IPSModule
|
||||
|
||||
// Prüfen, dass jeder User mindestens seinen minimalwert an Leistung bekommt
|
||||
foreach ($userEnergyProv as $userInstanceID => $leistung) {
|
||||
$minimalleitsung = min(
|
||||
$positiveValues = array_filter(
|
||||
array_column(
|
||||
array_filter($allSteps, function ($entry) use (
|
||||
$userInstanceID
|
||||
) {
|
||||
return $entry["user"] == $userInstanceID;
|
||||
}),
|
||||
"step"
|
||||
)
|
||||
$samePriorityUsers,
|
||||
'PowerSteps',
|
||||
'InstanceID'
|
||||
)[$userInstanceID],
|
||||
function ($l) {
|
||||
return $l >= 0;
|
||||
}
|
||||
);
|
||||
// 2. Falls keine Werte ≥ 0 vorhanden sind, auf 0 zurückfallen
|
||||
$fallbackMinimum = empty($positiveValues)
|
||||
? max( array_column(
|
||||
$samePriorityUsers,
|
||||
'PowerSteps',
|
||||
'InstanceID'
|
||||
)[$userInstanceID])
|
||||
: min($positiveValues);
|
||||
|
||||
IPS_LogMessage("Manager", $userInstanceID);
|
||||
IPS_LogMessage("Manager", $minimalleitsung);
|
||||
IPS_LogMessage("Manager", $remainingPower);
|
||||
// Jedem user den höheren der beiden werte aus minimalwert oder vergebenem zuteilen
|
||||
$leistung = max($leistung["Set_Leistung"], $minimalleitsung);
|
||||
// 3. minimalleistung = dieser Fallback
|
||||
$minimalleistung = $fallbackMinimum;
|
||||
|
||||
// 4. den höheren Wert wählen und für IPS negativieren
|
||||
//$schreibleistung = max($leistung, $minimalleistung);
|
||||
|
||||
if (abs($leistung["Set_Leistung"]) > abs($minimalleistung)) {
|
||||
$leistung = $leistung["Set_Leistung"];
|
||||
} else {
|
||||
$leistung = $minimalleistung;
|
||||
}
|
||||
|
||||
|
||||
// Methode SetAktuelle_Leistung für jeden Verbraucher mit der entsprechenden Energie aufrufen
|
||||
@@ -387,22 +440,37 @@ class HauptManager extends IPSModule
|
||||
|
||||
// Prüfen, dass jeder User mindestens seinen minimalwert an Leistung bekommt
|
||||
foreach ($userEnergyProv as $userInstanceID => $leistung) {
|
||||
$minimalleitsung = min(
|
||||
$positiveValues = array_filter(
|
||||
array_column(
|
||||
array_filter($allSteps, function ($entry) use (
|
||||
$userInstanceID
|
||||
) {
|
||||
return $entry["user"] == $userInstanceID;
|
||||
}),
|
||||
"step"
|
||||
)
|
||||
$samePriorityUsers,
|
||||
'PowerSteps',
|
||||
'InstanceID'
|
||||
)[$userInstanceID],
|
||||
function ($l) {
|
||||
return $l <= 0;
|
||||
}
|
||||
);
|
||||
// 2. Falls keine Werte ≥ 0 vorhanden sind, auf 0 zurückfallen
|
||||
$fallbackMinimum = empty($positiveValues)
|
||||
? min( array_column(
|
||||
$samePriorityUsers,
|
||||
'PowerSteps',
|
||||
'InstanceID'
|
||||
)[$userInstanceID])
|
||||
: max($positiveValues);
|
||||
|
||||
IPS_LogMessage("Manager", $userInstanceID);
|
||||
IPS_LogMessage("Manager", $minimalleitsung);
|
||||
IPS_LogMessage("Manager", $remainingPower);
|
||||
// 3. minimalleistung = dieser Fallback
|
||||
$minimalleistung = $fallbackMinimum;
|
||||
|
||||
// 4. den höheren Wert wählen und für IPS negativieren
|
||||
//$schreibleistung = max($leistung, $minimalleistung);
|
||||
|
||||
if (abs($leistung["Set_Leistung"]) > abs($minimalleistung)) {
|
||||
$leistung = $leistung["Set_Leistung"];
|
||||
} else {
|
||||
$leistung = $minimalleistung;
|
||||
}
|
||||
// Jedem user den höheren der beiden werte aus minimalwert oder vergebenem zuteilen
|
||||
$leistung = max($leistung["Set_Leistung"], $minimalleitsung)*-1;
|
||||
|
||||
|
||||
// Methode SetAktuelle_Leistung für jeden Verbraucher mit der entsprechenden Energie aufrufen
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
{
|
||||
"caption": "Easee - Nur Solarladen",
|
||||
"value": 5
|
||||
},
|
||||
{
|
||||
"caption": "Easee",
|
||||
"value": 6
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -48,35 +52,48 @@
|
||||
{
|
||||
"type": "NumberSpinner",
|
||||
"name": "Zeit_Zwischen_Zustandswechseln",
|
||||
"caption": "Mindestlaufzeit des Verbrauchers bei Lastschaltung (Für Easee Solarladen zwingend 1 Minute einstellen)",
|
||||
"caption": "Mindestlaufzeit des Verbrauchers bei Lastschaltung ",
|
||||
"suffix": ""
|
||||
},
|
||||
{
|
||||
"type": "NumberSpinner",
|
||||
"name": "Ein_Zeit",
|
||||
"caption": "Mindestlaufzeit Ladestation wenn Ein",
|
||||
"suffix": ""
|
||||
},
|
||||
{
|
||||
"type": "NumberSpinner",
|
||||
"name": "Aus_Zeit",
|
||||
"caption": "Mindestlaufzeit Ladestation wenn Aus",
|
||||
"suffix": ""
|
||||
},
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"name": "IP_Adresse",
|
||||
"caption": "IP-Adresse Ladestation"
|
||||
},
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"name": "Geräte-ID Smart-Me / ECarUp / Easee",
|
||||
"caption": "ID"
|
||||
|
||||
},
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"name": "Seriennummer Smart-Me / ECarUp / Easee",
|
||||
"caption": "Seriennummer"
|
||||
"caption": "Geräte-ID Smart-Me / ECarUp / Easee",
|
||||
"name": "ID"
|
||||
|
||||
},
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"name": "Username / Benutzernahme Solarladen",
|
||||
"caption": "Username"
|
||||
"caption": "Seriennummer Smart-Me / ECarUp / Easee",
|
||||
"name": "Seriennummer"
|
||||
|
||||
},
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"caption": "Username / Benutzernahme Solarladen",
|
||||
"name": "Username"
|
||||
|
||||
},
|
||||
{ "type": "PasswordTextBox",
|
||||
"name": "Password -> Bei Anwendung mit Pico-Stationen",
|
||||
"caption": "Passwort"
|
||||
"caption": "Password -> Bei Anwendung mit Pico-Stationen",
|
||||
"name": "Password"
|
||||
},
|
||||
{
|
||||
"type": "SelectVariable",
|
||||
|
||||
@@ -17,6 +17,8 @@ class Ladestation_v2 extends IPSModule
|
||||
$this->RegisterPropertyInteger("Interval", 5); // Recheninterval
|
||||
$this->RegisterPropertyInteger("Max_Current_abs", 32); // Recheninterval
|
||||
$this->RegisterPropertyInteger("Zeit_Zwischen_Zustandswechseln", 1);
|
||||
$this->RegisterPropertyInteger("Ein_Zeit", 0); // Recheninterval
|
||||
$this->RegisterPropertyInteger("Aus_Zeit", 0); // Recheninterval
|
||||
$this->RegisterPropertyInteger("Token_Easee", 0); // Recheninterval
|
||||
$this->RegisterPropertyInteger("Token_ECarUp", 0); // Recheninterval
|
||||
|
||||
@@ -41,8 +43,18 @@ class Ladestation_v2 extends IPSModule
|
||||
IPS_SetHidden($this->GetIDForIdent("Peak"), true);
|
||||
|
||||
$this->RegisterVariableBoolean("IsTimerActive", "IsTimerActive", "", false);
|
||||
IPS_SetHidden($this->GetIDForIdent("IsTimerActive"), true);
|
||||
|
||||
$this->RegisterTimer("ZustandswechselTimer",0,"IPS_RequestAction(" .$this->InstanceID .', "ResetTimer", "");');
|
||||
|
||||
$this->RegisterVariableBoolean("IsTimerActive_Null_Timer", "IsTimerActive_Null_Timer", "", false);
|
||||
IPS_SetHidden($this->GetIDForIdent("IsTimerActive_Null_Timer"), true);
|
||||
|
||||
$this->RegisterTimer("Null_Timer",0,"IPS_RequestAction(" .$this->InstanceID .', "ResetNullTimer", "");');
|
||||
|
||||
$this->RegisterVariableString("Letzer_User", "Letzter registrierter Benutzer");
|
||||
IPS_SetHidden($this->GetIDForIdent("Letzer_User"), true);
|
||||
|
||||
|
||||
|
||||
// Variabeln für Kommunkation mit Manager
|
||||
@@ -75,6 +87,9 @@ class Ladestation_v2 extends IPSModule
|
||||
$this->RegisterVariableInteger("Leistung_Delta", "Leistung_Delta", "", 0);
|
||||
IPS_SetHidden($this->GetIDForIdent("Leistung_Delta"), true);
|
||||
|
||||
$this->RegisterVariableString("Token_Intern", "Internes Token Easee");
|
||||
IPS_SetHidden($this->GetIDForIdent("Token_Intern"), true);
|
||||
|
||||
// Hilfsvariabeln für Idle zustand
|
||||
$this->RegisterPropertyInteger("IdleCounterMax", 2);
|
||||
$this->RegisterVariableInteger("IdleCounter", "IdleCounter", "", 0);
|
||||
@@ -85,6 +100,7 @@ class Ladestation_v2 extends IPSModule
|
||||
$this->SetValue("Idle", true);
|
||||
|
||||
$this->RegisterTimer("Timer_Do_UserCalc_EVC",$this->ReadPropertyInteger("Interval")*1000,"IPS_RequestAction(" .$this->InstanceID .', "Do_UserCalc", "");');
|
||||
$this->RegisterTimer("Timer_Refresh_Token",0,"IPS_RequestAction(" .$this->InstanceID .', "Refresh_Token", "");');
|
||||
|
||||
}
|
||||
|
||||
@@ -93,6 +109,14 @@ class Ladestation_v2 extends IPSModule
|
||||
parent::ApplyChanges();
|
||||
$this->SetTimerInterval("Timer_Do_UserCalc_EVC",$this->ReadPropertyInteger("Interval")*1000);
|
||||
|
||||
// erstelle einen Timer der das Request token aktualisiert wenn die station dies braucht.
|
||||
if($this->ReadPropertyInteger("Ladestation")==6){
|
||||
|
||||
$this->Refresh_Token();
|
||||
$this->SetTimerInterval("Timer_Refresh_Token",1800000);
|
||||
|
||||
}
|
||||
|
||||
// Zusätzliche Anpassungen nach Bedarf
|
||||
}
|
||||
|
||||
@@ -144,6 +168,14 @@ class Ladestation_v2 extends IPSModule
|
||||
$this->ResetTimer();
|
||||
break;
|
||||
|
||||
case "ResetNullTimer":
|
||||
$this->ResetNullTimer();
|
||||
break;
|
||||
|
||||
case "Refresh_Token":
|
||||
$this->Refresh_Token();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("Invalid Ident");
|
||||
}
|
||||
@@ -158,6 +190,8 @@ class Ladestation_v2 extends IPSModule
|
||||
$this->SetValue("Car_detected", true);
|
||||
if($this->GetValue("Max_Current")<6){
|
||||
$this->SetValue("Car_is_full", true);
|
||||
$this->ResetTimer();
|
||||
$this->ResetNullTimer();
|
||||
|
||||
}
|
||||
|
||||
@@ -165,7 +199,8 @@ class Ladestation_v2 extends IPSModule
|
||||
$this->SetValue("Car_detected", false);
|
||||
$this->SetValue("Leistung_Delta", 0);
|
||||
$this->SetValue("Car_is_full", false);
|
||||
$this->SetValue("IsTimerActive", false);
|
||||
$this->ResetTimer();
|
||||
$this->ResetNullTimer();
|
||||
$this->SetValue("Is_1_ph", false);
|
||||
$this->SetValue("Aktuelle_Leistung", 0);
|
||||
$this->SetValue("IdleCounter", 0);
|
||||
@@ -231,6 +266,21 @@ class Ladestation_v2 extends IPSModule
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Methode zum Setzen der PowerSteps und Timer starten
|
||||
public function SetTimerNullMindestlast($time)
|
||||
{
|
||||
if($time>0){
|
||||
$this->SetTimerInterval("Null_Timer", $time * 60000); // Timer in Millisekunden
|
||||
// Timer-Status auf true setzen
|
||||
$this->SetValue("IsTimerActive_Null_Timer", true);
|
||||
}else{
|
||||
$this->SetValue("IsTimerActive_Null_Timer", false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Methode zum Zurücksetzen von PowerSteps nach Ablauf des Timers
|
||||
public function ResetTimer()
|
||||
{
|
||||
@@ -241,6 +291,17 @@ class Ladestation_v2 extends IPSModule
|
||||
$this->SetValue("IsTimerActive", false);
|
||||
}
|
||||
|
||||
// Methode zum Zurücksetzen von PowerSteps nach Ablauf des Timers
|
||||
public function ResetNullTimer()
|
||||
{
|
||||
// Timer stoppen
|
||||
$this->SetTimerInterval("Null_Timer", 0);
|
||||
|
||||
// Timer-Status auf false setzen
|
||||
$this->SetValue("IsTimerActive_Null_Timer", false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function Get_Car_Status(int $carType)
|
||||
{
|
||||
@@ -345,29 +406,99 @@ class Ladestation_v2 extends IPSModule
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$data = json_decode($response, true);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
IPS_LogMessage("Ladestation", "Fehler beim Abrufen der eCarUp API-Daten");
|
||||
return;
|
||||
if (json_last_error() === JSON_ERROR_NONE && isset($data["driverIdentifier"])) {
|
||||
|
||||
$this->SetValue("Letzer_User", $data["driverIdentifier"]);
|
||||
|
||||
if($this->ReadPropertyString("Username") === $data["driverIdentifier"]){
|
||||
$this->SetValue("Solarladen", true);
|
||||
}else{
|
||||
$this->SetValue("Solarladen", false);
|
||||
}
|
||||
}
|
||||
|
||||
$data = json_decode($response, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
IPS_LogMessage("Ladestation", "Fehler beim Dekodieren der JSON-Antwort");
|
||||
return;
|
||||
}
|
||||
//Aktueller Zustand Ladestation abfragen (Leistung, auto eingesteckt?)
|
||||
$ch2 = curl_init();
|
||||
|
||||
if (isset($data['driverIdentifier']) && ($data['driverIdentifier']==$this->ReadPropertyInteger("Username"))) {
|
||||
$this->SetValue("Ladeleistung_Effektiv", $this->GetValue("Power"));
|
||||
$this->SetValue("Fahrzeugstatus", 2);
|
||||
$url2 = "https://api.easee.com/api/chargers/".$this->ReadPropertyString("Seriennummer")."/state";
|
||||
curl_setopt_array($ch2, [
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "GET",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: Bearer ".GetValue($this->ReadPropertyInteger("Token_Easee"))."",
|
||||
"content-type: application/*+json"
|
||||
],
|
||||
]);
|
||||
|
||||
curl_setopt($ch2, CURLOPT_URL, $url2);
|
||||
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
|
||||
$response_easee = curl_exec($ch2);
|
||||
curl_close($ch2);
|
||||
|
||||
$easee_data = json_decode($response_easee, true);
|
||||
|
||||
|
||||
if (json_last_error() === JSON_ERROR_NONE && isset($easee_data["chargerOpMode"])) {
|
||||
|
||||
if ($easee_data["chargerOpMode"] != 1 && (($easee_data["chargerOpMode"] != 6)||($this->GetValue("Car_detected")==true)) && $easee_data["chargerOpMode"] != 7) {
|
||||
$car_on_station = true;
|
||||
|
||||
} else {
|
||||
$car_on_station = false;
|
||||
$this->SetValue("Fahrzeugstatus", 1);
|
||||
$this->SetValue("Ladeleistung_Effektiv", 0);
|
||||
}
|
||||
else{
|
||||
$car_on_station = false;
|
||||
}
|
||||
$this->SetValue("Ladeleistung_Effektiv", round($easee_data["totalPower"]*1000));
|
||||
$this->SetValue("Fahrzeugstatus", $easee_data["chargerOpMode"]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 6:
|
||||
|
||||
|
||||
|
||||
//Aktueller Zustand Ladestation abfragen (Leistung, auto eingesteckt?)
|
||||
$ch2 = curl_init();
|
||||
|
||||
$url2 = "https://api.easee.com/api/chargers/".$this->ReadPropertyString("Seriennummer")."/state";
|
||||
curl_setopt_array($ch2, [
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "GET",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: Bearer ".$this->GetBuffer("Token_Intern")."",
|
||||
"content-type: application/*+json"
|
||||
],
|
||||
]);
|
||||
|
||||
curl_setopt($ch2, CURLOPT_URL, $url2);
|
||||
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
|
||||
$response_easee = curl_exec($ch2);
|
||||
curl_close($ch2);
|
||||
|
||||
$easee_data = json_decode($response_easee, true);
|
||||
|
||||
|
||||
if (json_last_error() === JSON_ERROR_NONE && isset($easee_data["chargerOpMode"])) {
|
||||
|
||||
if ($easee_data["chargerOpMode"] != 1 && (($easee_data["chargerOpMode"] != 6)||($this->GetValue("Car_detected")==true)) && $easee_data["chargerOpMode"] != 7) {
|
||||
$car_on_station = true;
|
||||
}
|
||||
else{
|
||||
$car_on_station = false;
|
||||
}
|
||||
$this->SetValue("Ladeleistung_Effektiv", round($easee_data["totalPower"]*1000));
|
||||
$this->SetValue("Fahrzeugstatus", $easee_data["chargerOpMode"]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -384,10 +515,10 @@ class Ladestation_v2 extends IPSModule
|
||||
{
|
||||
$maxCurrent = 32;
|
||||
if($is_1_ph){
|
||||
$maxCurrent = 1.5 + ($this->GetValue("Ladeleistung_Effektiv") / 230);
|
||||
$maxCurrent = 2.5 + ($this->GetValue("Ladeleistung_Effektiv") / 230);
|
||||
}
|
||||
else{
|
||||
$maxCurrent = 1.5 + ($this->GetValue("Ladeleistung_Effektiv") / (1.71*400));
|
||||
$maxCurrent = 2.5 + ($this->GetValue("Ladeleistung_Effektiv") / (1.71*400));
|
||||
}
|
||||
if($maxCurrent>$this->ReadPropertyInteger("Max_Current_abs")){
|
||||
$maxCurrent = $this->ReadPropertyInteger("Max_Current_abs");
|
||||
@@ -406,10 +537,24 @@ class Ladestation_v2 extends IPSModule
|
||||
return $current;
|
||||
}
|
||||
|
||||
public function Get_Array_From_Current(bool $is_1_ph, float $current)
|
||||
public function Get_Array_From_Current(bool $is_1_ph, float $current, int $power, bool $timer)
|
||||
{
|
||||
$resultArray = [];
|
||||
|
||||
if ($timer) {
|
||||
// Timer an
|
||||
if ($power === 0) {
|
||||
// power == 0: nur eine 0 zurückgeben
|
||||
$resultArray[] = 0;
|
||||
return $resultArray;
|
||||
}
|
||||
// power > 0: keine 0 am Anfang, Schleife normal durchlaufen
|
||||
} else {
|
||||
// Timer aus: wie bisher, 0 am Anfang
|
||||
$resultArray[] = 0;
|
||||
}
|
||||
|
||||
// Schleife wie gehabt
|
||||
for ($i = 6; $i <= $current; $i++) {
|
||||
if ($is_1_ph) {
|
||||
$resultArray[] = $i * 230;
|
||||
@@ -423,6 +568,7 @@ class Ladestation_v2 extends IPSModule
|
||||
|
||||
|
||||
|
||||
|
||||
public function SetAktuelle_Leistung(int $power)
|
||||
{
|
||||
// Hier eine Leistungsänderung detektieren für Idle und interne Mindestlaufzeiten
|
||||
@@ -431,6 +577,14 @@ class Ladestation_v2 extends IPSModule
|
||||
$this->SetTimerOn();
|
||||
}
|
||||
|
||||
if (($this->GetValue("Aktuelle_Leistung") == 0) && ($power>0)) {
|
||||
$this->SetTimerNullMindestlast($this->ReadPropertyInteger("Ein_Zeit"));
|
||||
}
|
||||
elseif(($this->GetValue("Aktuelle_Leistung") > 0) && ($power==0)){
|
||||
$this->SetTimerNullMindestlast($this->ReadPropertyInteger("Aus_Zeit"));
|
||||
|
||||
}
|
||||
|
||||
$internalPower = GetValue($this->GetIDForIdent("Aktuelle_Leistung"));
|
||||
|
||||
// Aktuelle Leistungsvorgabe setzen
|
||||
@@ -444,7 +598,7 @@ class Ladestation_v2 extends IPSModule
|
||||
if ($power != $internalPower) {
|
||||
// Setze die interne Leistungsvorgabe
|
||||
|
||||
// Idle für 4 Zyklen auf false setzen
|
||||
// Idle für x Zyklen auf false setzen
|
||||
SetValue($this->GetIDForIdent("Idle"), false);
|
||||
SetValue(
|
||||
$this->GetIDForIdent("IdleCounter"),
|
||||
@@ -511,13 +665,13 @@ class Ladestation_v2 extends IPSModule
|
||||
if (!$ladebereit) {
|
||||
$powerSteps = [0];
|
||||
} elseif (!$Peak && !$solarladen) {
|
||||
$powerSteps = [max($this->Get_Array_From_Current($this->GetValue("Is_1_ph"),$this->GetValue("Max_Current")))];
|
||||
$powerSteps = [max($this->Get_Array_From_Current($this->GetValue("Is_1_ph"),$this->GetValue("Max_Current"), $this->GetValue("Aktuelle_Leistung"), $this->GetValue("IsTimerActive_Null_Timer")))];
|
||||
} elseif (!$Peak && $solarladen) {
|
||||
$powerSteps = $this->Get_Array_From_Current($this->GetValue("Is_1_ph"),$this->GetValue("Max_Current"));
|
||||
$powerSteps = $this->Get_Array_From_Current($this->GetValue("Is_1_ph"),$this->GetValue("Max_Current"), $this->GetValue("Aktuelle_Leistung"), $this->GetValue("IsTimerActive_Null_Timer"));
|
||||
} elseif ($solarladen && $Peak) {
|
||||
$powerSteps = [0];
|
||||
} else {
|
||||
$powerSteps = $this->Get_Array_From_Current($this->GetValue("Is_1_ph"),$this->GetValue("Max_Current"));
|
||||
$powerSteps = $this->Get_Array_From_Current($this->GetValue("Is_1_ph"),$this->GetValue("Max_Current"), $this->GetValue("Aktuelle_Leistung"), $this->GetValue("IsTimerActive_Null_Timer"));
|
||||
}
|
||||
|
||||
// PowerSteps in der RegisterVariable speichern
|
||||
@@ -528,7 +682,7 @@ class Ladestation_v2 extends IPSModule
|
||||
if($this->GetValue("Aktuelle_Leistung")>(1.11*$this->GetValue("Ladeleistung_Effektiv"))){
|
||||
$this->SetValue("Pending_Counter", $counter+1);
|
||||
|
||||
}elseif(max($this->Get_Array_From_Current($this->GetValue("Is_1_ph"),$this->GetValue("Max_Current")))<(1.11*$this->GetValue("Ladeleistung_Effektiv"))){ // diesen elseif ggf wieder rauslöschen
|
||||
}elseif(max($this->Get_Array_From_Current($this->GetValue("Is_1_ph"),$this->GetValue("Max_Current"), $this->GetValue("Aktuelle_Leistung"), $this->GetValue("IsTimerActive_Null_Timer")))<(1.11*$this->GetValue("Ladeleistung_Effektiv"))){ // diesen elseif ggf wieder rauslöschen
|
||||
$this->SetValue("Pending_Counter", $counter+1);
|
||||
|
||||
}
|
||||
@@ -544,9 +698,6 @@ class Ladestation_v2 extends IPSModule
|
||||
$this->Calc_Max_Current($this->GetValue("Is_1_ph"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return $powerSteps;
|
||||
}
|
||||
|
||||
@@ -571,6 +722,9 @@ class Ladestation_v2 extends IPSModule
|
||||
case 5:
|
||||
// Keine base Url nötig
|
||||
break;
|
||||
case 6:
|
||||
// Keine base Url nötig
|
||||
break;
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
@@ -595,6 +749,9 @@ class Ladestation_v2 extends IPSModule
|
||||
// Nichts zu tun für Dummy station
|
||||
return;
|
||||
case 5:
|
||||
if($this->ReadPropertyString("Username") != $this->GetValue("Letzer_User")){
|
||||
return;
|
||||
}
|
||||
$url = "https://api.easee.com/api/chargers/".$this->ReadPropertyString("Seriennummer")."/commands/set_dynamic_charger_current";
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_ENCODING => "",
|
||||
@@ -602,12 +759,30 @@ class Ladestation_v2 extends IPSModule
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "POST",
|
||||
CURLOPT_POSTFIELDS => "{\"amps\":". $value .",\"minutes\":1}",
|
||||
CURLOPT_POSTFIELDS => "{\"amps\":". $value .",\"minutes\":0}",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: Bearer ".GetValue($this->ReadPropertyInteger("Token_Easee"))."",
|
||||
"content-type: application/*+json"
|
||||
],
|
||||
]);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
|
||||
$url = "https://api.easee.com/api/chargers/".$this->ReadPropertyString("Seriennummer")."/commands/set_dynamic_charger_current";
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "POST",
|
||||
CURLOPT_POSTFIELDS => "{\"amps\":". $value .",\"minutes\":0}",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: Bearer ".$this->GetBuffer("Token_Intern")."",
|
||||
"content-type: application/*+json"
|
||||
],
|
||||
]);
|
||||
break;
|
||||
|
||||
default:
|
||||
return "Invalid station type.";
|
||||
@@ -637,6 +812,9 @@ class Ladestation_v2 extends IPSModule
|
||||
// Nichts zu tun für Dummy station
|
||||
return;
|
||||
case 5:
|
||||
if($this->ReadPropertyString("Username") != $this->GetValue("Letzer_User")){
|
||||
return;
|
||||
}
|
||||
$url2 = "https://api.easee.com/api/chargers/".$this->ReadPropertyString("Seriennummer")."/commands/set_dynamic_charger_current";
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_ENCODING => "",
|
||||
@@ -644,13 +822,31 @@ class Ladestation_v2 extends IPSModule
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "POST",
|
||||
CURLOPT_POSTFIELDS => "{\"amps\":". $value .",\"minutes\":1}",
|
||||
CURLOPT_POSTFIELDS => "{\"amps\":". $value .",\"minutes\":0}",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: Bearer ".GetValue($this->ReadPropertyInteger("Token_Easee"))."",
|
||||
"content-type: application/*+json"
|
||||
],
|
||||
]);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
|
||||
$url2 = "https://api.easee.com/api/chargers/".$this->ReadPropertyString("Seriennummer")."/commands/set_dynamic_charger_current";
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "POST",
|
||||
CURLOPT_POSTFIELDS => "{\"amps\":". $value .",\"minutes\":0}",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: Bearer ".$this->GetBuffer("Token_Intern")."",
|
||||
"content-type: application/*+json"
|
||||
],
|
||||
]);
|
||||
break;
|
||||
|
||||
default:
|
||||
return "Invalid station type.";
|
||||
}
|
||||
@@ -678,6 +874,43 @@ class Ladestation_v2 extends IPSModule
|
||||
}
|
||||
}
|
||||
|
||||
public function Refresh_Token(){
|
||||
|
||||
$payload = json_encode([
|
||||
'userName' => $this->ReadPropertyString("Username"),
|
||||
'password' => $this->ReadPropertyString("Password"),
|
||||
]);
|
||||
|
||||
// cURL-Handle initialisieren
|
||||
$ch = curl_init('https://api.easee.com/api/accounts/login');
|
||||
|
||||
// cURL-Optionen setzen
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true, // Antwort als String zurückgeben
|
||||
CURLOPT_POST => true, // POST-Methode verwenden
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/json',
|
||||
],
|
||||
CURLOPT_POSTFIELDS => $payload, // JSON-Body übergeben
|
||||
]);
|
||||
|
||||
// Anfrage ausführen und Antwort speichern
|
||||
$response = curl_exec($ch);
|
||||
|
||||
// Auf Fehler prüfen
|
||||
if ($response === false) {
|
||||
echo 'cURL-Fehler: ' . curl_error($ch);
|
||||
curl_close($ch);
|
||||
exit;
|
||||
}
|
||||
|
||||
// cURL-Handle schließen
|
||||
curl_close($ch);
|
||||
$this->SetBuffer("Token_Intern", (json_decode($response, true)['accessToken']));
|
||||
|
||||
}
|
||||
|
||||
public function CheckIdle($power)
|
||||
{
|
||||
$lastpower = GetValue($this->GetIDForIdent("Aktuelle_Leistung"));
|
||||
|
||||
@@ -1,67 +1,125 @@
|
||||
# Manager_1
|
||||
Beschreibung des Moduls.
|
||||
# Belevo EMS Manager
|
||||
|
||||
### Inhaltsverzeichnis
|
||||
Dieses Modul ist das Herzstück des Belevo Energiemanagement-Systems. Es steuert in festgelegten Intervallen die Leistungsverteilung auf Ihre Verbraucher-Instanzen, trifft den Solarlade- vs. Peak-Shaving-Entscheid und verteilt entsprechend die Leistungen an die Verbraucher.
|
||||
|
||||
---
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
1. [Funktionsumfang](#1-funktionsumfang)
|
||||
2. [Voraussetzungen](#2-voraussetzungen)
|
||||
3. [Software-Installation](#3-software-installation)
|
||||
4. [Einrichten der Instanzen in IP-Symcon](#4-einrichten-der-instanzen-in-ip-symcon)
|
||||
5. [Statusvariablen und Profile](#5-statusvariablen-und-profile)
|
||||
6. [WebFront](#6-webfront)
|
||||
7. [PHP-Befehlsreferenz](#7-php-befehlsreferenz)
|
||||
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
|
||||
---
|
||||
|
||||
*
|
||||
## 1. Funktionsumfang
|
||||
|
||||
### 2. Voraussetzungen
|
||||
- **Zyklische Steuerung**
|
||||
In konfigurierbaren Intervallen (`Interval`) wird die Methode `DistributeEnergy()` ausgelöst.
|
||||
- **Mode-Entscheid**
|
||||
Basierend auf der aktuellen Netz-Sollleistung (`Sollleistung_Max`) und der PV-Überschuss-Grenze (`Ueberschussleistung`) wählt der Manager automatisch zwischen
|
||||
- **Solarlade-Mode** (`Is_Peak_Shaving = false`)
|
||||
- **Peak-Shaving-Mode** (`Is_Peak_Shaving = true`)
|
||||
- **Leistungskonten**
|
||||
Jeder Verbraucher meldet über seine Instanzvariablen (`Power`, `Bezogene_Energie`, `PowerSteps`, `PV_Prio` / `Sperre_Prio`) seinen Bedarf bzw. Reduktions- oder Einspeise-Potential.
|
||||
- **Priorisierung & Fairness**
|
||||
1. Sortierung der Verbraucher nach Priorität (`PV_Prio` im Solarmode, `Sperre_Prio` im Shaving-Mode)
|
||||
2. Bei Gleichpriorität abwechselnde Zuteilung nach bisher bezogener Energie (`Bezogene_Energie`)
|
||||
- **Differenz-Verteilung**
|
||||
Die verbleibende Soll–Ist-Differenz D wird in Stufen (je Verbraucher aus `PowerSteps`) abgearbeitet, bis D ≃ 0.
|
||||
- **Externe Kommunikation**
|
||||
Über die Properties `DatenHoch` und `DatenZuruck` (Variable-IDs) können mehrere Manager zu einem Übergeordneten Hauptmanager zusammengefasst werden (V-ZEV, LEG).
|
||||
|
||||
- IP-Symcon ab Version 7.1
|
||||
---
|
||||
|
||||
### 3. Software-Installation
|
||||
## 2. Voraussetzungen
|
||||
|
||||
* Über den Module Store das 'Manager_1'-Modul installieren.
|
||||
* Alternativ über das Module Control folgende URL hinzufügen
|
||||
- IP-Symcon **≥ 8.0**
|
||||
- Modul-URL:
|
||||
https://git.belevo.ch/dh/Symcon_Belevo_Energiemanagement_testing.git
|
||||
|
||||
### 4. Einrichten der Instanzen in IP-Symcon
|
||||
yaml
|
||||
Kopieren
|
||||
Bearbeiten
|
||||
- Einmalige manuelle Anmeldung aller Verbraucher in `Verbraucher_Liste`
|
||||
|
||||
Unter 'Instanz hinzufügen' kann das 'Manager_1'-Modul mithilfe des Schnellfilters gefunden werden.
|
||||
- Weitere Informationen zum Hinzufügen von Instanzen in der [Dokumentation der Instanzen](https://www.symcon.de/service/dokumentation/konzepte/instanzen/#Instanz_hinzufügen)
|
||||
---
|
||||
|
||||
__Konfigurationsseite__:
|
||||
## 3. Installation
|
||||
|
||||
Name | Beschreibung
|
||||
-------- | ------------------
|
||||
|
|
||||
|
|
||||
1. In IP-Symcon **Module Control** öffnen
|
||||
2. **Hinzufügen → Git-Repository**
|
||||
3. URL eingeben (oben) und **Installieren**
|
||||
4. IP-Symcon neu starten
|
||||
|
||||
### 5. Statusvariablen und Profile
|
||||
---
|
||||
|
||||
Die Statusvariablen/Kategorien werden automatisch angelegt. Das Löschen einzelner kann zu Fehlfunktionen führen.
|
||||
## 4. Instanz anlegen & Konfiguration
|
||||
|
||||
#### Statusvariablen
|
||||
### 4.1 Instanz anlegen
|
||||
|
||||
Name | Typ | Beschreibung
|
||||
------ | ------- | ------------
|
||||
| |
|
||||
| |
|
||||
- Rechtsklick **Instanzen** → **Instanz hinzufügen**
|
||||
- Filter: **Belevo**
|
||||
- Auswahl: **Manager**
|
||||
|
||||
#### Profile
|
||||
### 4.2 Properties
|
||||
|
||||
Name | Typ
|
||||
------ | -------
|
||||
|
|
||||
|
|
||||
| Name | Typ | Beschreibung |
|
||||
|--------------------------|-----------------|----------------------------------------------------------------------------|
|
||||
| **Peakleistung** | NumberSpinner | Sollwert-Vorgabe für Peak-Shaving (Watt) |
|
||||
| **Ueberschussleistung** | NumberSpinner | Sollwert-Vorgabe für Solarladen (Watt) |
|
||||
| **Netzbezug** | SelectVariable | Variable mit dem aktuellen Netz-Ist-Wert (Watt) |
|
||||
| **HauptmanagerAktiv** | CheckBox | Schaltet die Ünergeordnete Manager-Logik global ein/aus |
|
||||
| **ManagerID** | NumberSpinner | Eindeutige ID für externe Manager‐Kommunikation |
|
||||
| **DatenHoch** | SelectVariable | Variable, in die Manager-Statistiken oder Statusdaten geschrieben werden |
|
||||
| **DatenZuruck** | SelectVariable | Variable, aus der Verbraucherdaten importiert werden |
|
||||
| **Interval** | NumberSpinner | Intervall für Neuberechnung der Werte (Sekunden) |
|
||||
| **Verbraucher_Liste** | List | Auswahl aller Verbraucher-Instanzen, die gesteuert werden sollen |
|
||||
|
||||
### 6. WebFront
|
||||
---
|
||||
|
||||
Die Funktionalität, die das Modul im WebFront bietet.
|
||||
## 5. Statusvariablen & Profile
|
||||
|
||||
### 7. PHP-Befehlsreferenz
|
||||
| Ident | Typ | Profil | Beschreibung |
|
||||
|-------------------------|------------|----------------|-------------------------------------------------------------------|
|
||||
| **Is_Peak_Shaving** | Boolean | — | Modusanzeige (false = Solarladen, true = Peak-Shaving) |
|
||||
| **LetzteBerechnung** | DateTime | — | Zeitpunkt der letzten Zyklus-Ausführung |
|
||||
| **Aktuelle_Differenz** | Float | ~Watt~~W~ | Zuletzt berechnete Soll–Ist-Differenz |
|
||||
|
||||
`boolean GEF_BeispielFunktion(integer $InstanzID);`
|
||||
Erklärung der Funktion.
|
||||
---
|
||||
|
||||
Beispiel:
|
||||
`GEF_BeispielFunktion(12345);`
|
||||
## 6. WebFront / Bedienung
|
||||
|
||||
Im WebFront können Sie
|
||||
- den **aktuellen Modus** (`Is_Peak_Shaving`) beobachten
|
||||
|
||||
---
|
||||
|
||||
## 7. Mapping auf Code-Komponenten
|
||||
|
||||
| Komponente | Modul-Datei | Funktion |
|
||||
|---------------------------|-----------------------------|------------------------------------------------------------|
|
||||
| **Timer-Registrierung** | `Manager/module.php` | `ApplyChanges()` → `SetTimerInterval('Interval', …)` |
|
||||
| **Zyklischer Aufruf** | `Manager/module.php` | `ManageTimer()` ruft `DistributeEnergy()` auf |
|
||||
| **Mode-Entscheidlogik** | `Manager/module.php` | Berechnung:
|
||||
```php
|
||||
$isPeak = $ist > (($peak + $solar) / 2);
|
||||
$this->SetValue('Is_Peak_Shaving', $isPeak);
|
||||
``` |
|
||||
| **Datenzugriff** | `Manager/module.php` | `$this->ReadPropertyInteger('Peakleistung')` usw. |
|
||||
| **Verbraucher-Schleife** | `Manager/module.php` | `$this->ReadPropertyArray('Verbraucher_Liste')` |
|
||||
| **Leistungszuteilung** | `Manager/module.php` | `DistributeEnergy()` → Round-Robin über `PowerSteps` |
|
||||
| **Externe Schnittstelle** | `Manager/module.php` | Verwendung von `DatenHoch` / `DatenZuruck` Variablen-IDs |
|
||||
|
||||
---
|
||||
|
||||
## 8. Zukünftige Erweiterungen
|
||||
|
||||
- **Automatische Registrierung** neuer Verbraucher (ohne manuelles Eintragen)
|
||||
|
||||
---
|
||||
@@ -169,12 +169,12 @@ class Manager extends IPSModule
|
||||
IPS_LogMessage("Manager", "nciht idle");
|
||||
|
||||
}
|
||||
if(in_array(0, $powerSteps, true)){
|
||||
//if(in_array(0, $powerSteps, true)){
|
||||
|
||||
// Addiere die aktuell bereits verwendete Leistung auf, um sie bei der verteilung zu berücksichtigen
|
||||
$totalAktuelle_Leistung += ($Aktuelle_Leistung-$delta);
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
}
|
||||
// Berücksichtigung der bereits verteilten Leistungen (nachher kann dafür wieder bei 0 begonnen werden zu verteilen)
|
||||
@@ -238,8 +238,15 @@ class Manager extends IPSModule
|
||||
continue;
|
||||
}
|
||||
$withZero = [];
|
||||
$withoutZero = [];
|
||||
$withoutZeroHigh = [];
|
||||
$withoutZeroLow = [];
|
||||
|
||||
// Verbraucher die nicht 0 Annhemen können, bekommen einfach den tiefsten wert
|
||||
|
||||
|
||||
|
||||
|
||||
/* Alter Verteilalgor. zu testzwecken auskommentiert, wird dann gelöscht wenns funktioniert.
|
||||
foreach ($samePriorityUsers as $entry) {
|
||||
if (min($entry["PowerSteps"]) <= 0) {
|
||||
$withZero[] = $entry;
|
||||
@@ -247,6 +254,7 @@ class Manager extends IPSModule
|
||||
$withoutZero[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
// Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert
|
||||
if (!empty($withoutZero)) {
|
||||
foreach ($withoutZero as $entry) {
|
||||
@@ -257,6 +265,45 @@ class Manager extends IPSModule
|
||||
//$remainingPower -= $entry["Aktuelle_Leistung"];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/* Neuer Block */
|
||||
foreach ($samePriorityUsers as $entry) {
|
||||
$withZero[] = $entry;
|
||||
|
||||
if (min($entry["PowerSteps"]) > 0) {
|
||||
|
||||
$withoutZeroHigh[] = $entry;
|
||||
}
|
||||
if (max($entry["PowerSteps"]) < 0) {
|
||||
|
||||
$withoutZeroLow[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
// Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert
|
||||
if (!empty($withoutZeroHigh)) {
|
||||
foreach ($withoutZeroHigh as $entry) {
|
||||
$instanceID = $entry["InstanceID"];
|
||||
$minPowerStep = min($entry["PowerSteps"]);
|
||||
|
||||
$remainingPower -= $minPowerStep;
|
||||
}
|
||||
}
|
||||
// Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert
|
||||
if (!empty($withoutZeroLow)) {
|
||||
foreach ($withoutZeroLow as $entry) {
|
||||
$instanceID = $entry["InstanceID"];
|
||||
$minPowerStep = max($entry["PowerSteps"]);
|
||||
|
||||
$remainingPower += $minPowerStep;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Neuer Block Ende */
|
||||
|
||||
|
||||
|
||||
|
||||
// Nun die verteilen, die 0 erhalten können.
|
||||
@@ -301,35 +348,51 @@ class Manager extends IPSModule
|
||||
|
||||
}
|
||||
|
||||
//else
|
||||
|
||||
|
||||
// mache invertierte verteilung
|
||||
|
||||
|
||||
// Prüfen, dass jeder User mindestens seinen minimalwert an Leistung bekommt
|
||||
foreach ($userEnergyProv as $userInstanceID => $leistung) {
|
||||
$minimalleitsung = min(
|
||||
// 1. Innerhalb der Schleife: alle nicht-negativen Leistungen sammeln
|
||||
$positiveValues = array_filter(
|
||||
array_column(
|
||||
array_filter($allSteps, function ($entry) use (
|
||||
$userInstanceID
|
||||
) {
|
||||
return $entry["user"] == $userInstanceID;
|
||||
}),
|
||||
"step"
|
||||
)
|
||||
$samePriorityUsers,
|
||||
'PowerSteps',
|
||||
'InstanceID'
|
||||
)[$userInstanceID],
|
||||
function ($l) {
|
||||
return $l >= 0;
|
||||
}
|
||||
);
|
||||
// 2. Falls keine Werte ≥ 0 vorhanden sind, auf 0 zurückfallen
|
||||
$fallbackMinimum = empty($positiveValues)
|
||||
? max( array_column(
|
||||
$samePriorityUsers,
|
||||
'PowerSteps',
|
||||
'InstanceID'
|
||||
)[$userInstanceID])
|
||||
: min($positiveValues);
|
||||
|
||||
// Jedem user den höheren der beiden werte aus minimalwert oder vergebenem zuteilen
|
||||
$leistung = max($leistung, $minimalleitsung);
|
||||
// 3. minimalleistung = dieser Fallback
|
||||
$minimalleistung = $fallbackMinimum;
|
||||
|
||||
// 4. den höheren Wert wählen und für IPS negativieren
|
||||
//$schreibleistung = max($leistung, $minimalleistung);
|
||||
|
||||
if (abs($leistung) > abs($minimalleistung)) {
|
||||
$schreibleistung = $leistung;
|
||||
} else {
|
||||
$schreibleistung = $minimalleistung;
|
||||
}
|
||||
|
||||
// Methode SetAktuelle_Leistung für jeden Verbraucher mit der entsprechenden Energie aufrufen
|
||||
// 5. Aktion ausführen
|
||||
if (IPS_InstanceExists($userInstanceID)) {
|
||||
IPS_RequestAction($userInstanceID,"SetAktuelle_Leistung",$leistung);
|
||||
IPS_LogMessage("Manager", "aufgerufen setleistung if". $leistung);
|
||||
|
||||
IPS_RequestAction(
|
||||
$userInstanceID,
|
||||
"SetAktuelle_Leistung",
|
||||
$schreibleistung
|
||||
);
|
||||
IPS_LogMessage(
|
||||
"Manager",
|
||||
"aufgerufen setleistung (Berechnung in foreach): " . $schreibleistung
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,7 +404,7 @@ class Manager extends IPSModule
|
||||
if($step<=0){
|
||||
$allSteps[] = [
|
||||
"user" => $user["InstanceID"],
|
||||
"Leistung_Delta" => $user["Leistung_Delta"],
|
||||
//"Leistung_Delta" => $user["Leistung_Delta"],
|
||||
"step" => -1*$step,
|
||||
];}
|
||||
}
|
||||
@@ -352,7 +415,6 @@ class Manager extends IPSModule
|
||||
return $a["step"] <=> $b["step"];
|
||||
});
|
||||
|
||||
|
||||
$remainingPower = $remainingPower *-1;
|
||||
// Iteriere durch alle Schritte
|
||||
foreach ($allSteps as $entry) {
|
||||
@@ -372,27 +434,49 @@ class Manager extends IPSModule
|
||||
|
||||
// Prüfen, dass jeder User mindestens seinen minimalwert an Leistung bekommt
|
||||
foreach ($userEnergyProv as $userInstanceID => $leistung) {
|
||||
$minimalleitsung = min(
|
||||
// 1. Innerhalb der Schleife: alle nicht-negativen Leistungen sammeln
|
||||
$positiveValues = array_filter(
|
||||
array_column(
|
||||
array_filter($allSteps, function ($entry) use (
|
||||
$userInstanceID
|
||||
) {
|
||||
return $entry["user"] == $userInstanceID;
|
||||
}),
|
||||
"step"
|
||||
)
|
||||
$samePriorityUsers,
|
||||
'PowerSteps',
|
||||
'InstanceID'
|
||||
)[$userInstanceID],
|
||||
function ($l) {
|
||||
return $l <= 0;
|
||||
}
|
||||
);
|
||||
// 2. Falls keine Werte ≥ 0 vorhanden sind, auf 0 zurückfallen
|
||||
$fallbackMinimum = empty($positiveValues)
|
||||
? min( array_column(
|
||||
$samePriorityUsers,
|
||||
'PowerSteps',
|
||||
'InstanceID'
|
||||
)[$userInstanceID])
|
||||
: max($positiveValues);
|
||||
|
||||
// Jedem user den höheren der beiden werte aus minimalwert oder vergebenem zuteilen
|
||||
$schreibleistung = max($leistung, $minimalleitsung)*-1;
|
||||
// 3. minimalleistung = dieser Fallback
|
||||
$minimalleistung = $fallbackMinimum;
|
||||
|
||||
// 4. den höheren Wert wählen und für IPS negativieren
|
||||
//$schreibleistung = max($leistung, $minimalleistung);
|
||||
|
||||
if (abs($leistung) > abs($minimalleistung)) {
|
||||
$schreibleistung = $leistung*-1;
|
||||
} else {
|
||||
$schreibleistung = $minimalleistung;
|
||||
}
|
||||
|
||||
// Methode SetAktuelle_Leistung für jeden Verbraucher mit der entsprechenden Energie aufrufen
|
||||
// 5. Aktion ausführen
|
||||
if (IPS_InstanceExists($userInstanceID)) {
|
||||
IPS_RequestAction($userInstanceID,"SetAktuelle_Leistung",$schreibleistung);
|
||||
IPS_LogMessage("Manager", "aufgerufen setleistung else ".$schreibleistung);
|
||||
|
||||
IPS_RequestAction(
|
||||
$userInstanceID,
|
||||
"SetAktuelle_Leistung",
|
||||
$schreibleistung
|
||||
);
|
||||
IPS_LogMessage(
|
||||
"Manager",
|
||||
"aufgerufen setleistung (Berechnung in foreach): " . $schreibleistung
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
PV_Visu/form.json
Normal file
9
PV_Visu/form.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"elements": [
|
||||
{ "type": "SelectVariable", "name": "VarProduction", "caption": "Produktion (kWh)" },
|
||||
{ "type": "SelectVariable", "name": "VarConsumption", "caption": "Verbrauch (kWh)" },
|
||||
{ "type": "SelectVariable", "name": "VarFeedIn", "caption": "Einspeisung (kWh)" },
|
||||
{ "type": "SelectVariable", "name": "VarGrid", "caption": "Bezug Netz (kWh)" }
|
||||
],
|
||||
"actions": []
|
||||
}
|
||||
78
PV_Visu/module.html
Normal file
78
PV_Visu/module.html
Normal file
@@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
html, body { margin: 0; padding: 8px; background: transparent; font-family: sans-serif; color: #ffffff; }
|
||||
.bar-block { margin-bottom: 20px; }
|
||||
.bar-title { font-size: 1.2em; font-weight: bold; margin-bottom: 6px; }
|
||||
.bar-container { width: 100%; background: #ddd; border-radius: 4px; overflow: hidden; height: 24px; position: relative; }
|
||||
.bar { height: 100%; float: left; position: relative; }
|
||||
.bar span { position: absolute; width: 100%; text-align: center; line-height: 24px; font-size: 0.8em; color: #fff; }
|
||||
.bar-cons { background: #4CAF50; }
|
||||
.bar-feed { background: #8BC34A; }
|
||||
.bar-pv { background: #FF9800; }
|
||||
.bar-grid { background: #FF5722; }
|
||||
.value-text { font-size: 0.95em; margin-top: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="pv_visu">
|
||||
<div class="bar-block">
|
||||
<div class="bar-title">Produktion (Eigenverbrauch / Einspeisung)</div>
|
||||
<div class="bar-container">
|
||||
<div class="bar bar-cons" id="barCons"><span id="barConsText"></span></div>
|
||||
<div class="bar bar-feed" id="barFeed"><span id="barFeedText"></span></div>
|
||||
</div>
|
||||
<div class="value-text" id="prodValues"></div>
|
||||
</div>
|
||||
<div class="bar-block">
|
||||
<div class="bar-title">Verbrauch (PV / Netz)</div>
|
||||
<div class="bar-container">
|
||||
<div class="bar bar-pv" id="barPV"><span id="barPVText"></span></div>
|
||||
<div class="bar bar-grid" id="barGrid"><span id="barGridText"></span></div>
|
||||
</div>
|
||||
<div class="value-text" id="consValues"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function Apply(data) {
|
||||
document.getElementById('barCons').style.width = data.prodCons + '%';
|
||||
document.getElementById('barFeed').style.width = data.prodFeed + '%';
|
||||
document.getElementById('barPV').style.width = data.consPV + '%';
|
||||
document.getElementById('barGrid').style.width = data.consGrid + '%';
|
||||
|
||||
document.getElementById('barConsText').innerText = data.prodCons + '%';
|
||||
document.getElementById('barFeedText').innerText = data.prodFeed + '%';
|
||||
document.getElementById('barPVText').innerText = data.consPV + '%';
|
||||
document.getElementById('barGridText').innerText = data.consGrid + '%';
|
||||
|
||||
document.getElementById('prodValues').innerText =
|
||||
'Gesamt: ' + data.value.prod + ' kWh, Eigenverbrauch: ' + (data.consPV/100*data.value.cons).toFixed(2) + ' kWh, Einspeisung: ' + data.value.feed + ' kWh';
|
||||
document.getElementById('consValues').innerText =
|
||||
'Gesamt: ' + data.value.cons + ' kWh, PV-Anteil: ' + (data.consPV/100*data.value.cons).toFixed(2) + ' kWh, Netz: ' + data.value.grid + ' kWh';
|
||||
}
|
||||
|
||||
function handleMessage(msg) {
|
||||
try {
|
||||
const data = typeof msg === 'string' ? JSON.parse(msg) : msg;
|
||||
Apply(data);
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Verarbeiten der Daten:', e, msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof registerMessageHandler === 'function') {
|
||||
registerMessageHandler(handleMessage);
|
||||
}
|
||||
|
||||
// Live-Aktualisierung alle 30 Sekunden
|
||||
function pollData() {
|
||||
if (typeof IPS !== 'undefined') {
|
||||
IPS.RequestAction('update', '');
|
||||
}
|
||||
}
|
||||
setInterval(pollData, 30000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
12
PV_Visu/module.json
Normal file
12
PV_Visu/module.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"id": "{DDE89CBE-4411-5FF4-4931-14204E05CAD0}",
|
||||
"name": "PV_Visu",
|
||||
"type": 3,
|
||||
"vendor": "Belevo AG",
|
||||
"aliases": [],
|
||||
"parentRequirements": [],
|
||||
"childRequirements": [],
|
||||
"implemented": [],
|
||||
"prefix": "",
|
||||
"url": ""
|
||||
}
|
||||
99
PV_Visu/module.php
Normal file
99
PV_Visu/module.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
class PV_Visu extends IPSModule
|
||||
{
|
||||
public function Create()
|
||||
{
|
||||
parent::Create();
|
||||
|
||||
$this->RegisterPropertyInteger('VarProduction', 0);
|
||||
$this->RegisterPropertyInteger('VarConsumption', 0);
|
||||
$this->RegisterPropertyInteger('VarFeedIn', 0);
|
||||
$this->RegisterPropertyInteger('VarGrid', 0);
|
||||
|
||||
$this->RegisterVariableString('JSONData', 'Visualisierungsdaten', '', 0);
|
||||
IPS_SetHidden($this->GetIDForIdent('JSONData'), true);
|
||||
|
||||
$this->SetVisualizationType(1); // HTML SDK Tile
|
||||
}
|
||||
|
||||
public function ApplyChanges()
|
||||
{
|
||||
parent::ApplyChanges();
|
||||
|
||||
foreach (['VarProduction', 'VarConsumption', 'VarFeedIn', 'VarGrid'] as $prop) {
|
||||
$vid = $this->ReadPropertyInteger($prop);
|
||||
if ($vid > 0) {
|
||||
$this->RegisterMessage($vid, VM_UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
$this->UpdateData(); // Initial
|
||||
}
|
||||
|
||||
public function MessageSink($TimeStamp, $SenderID, $Message, $Data)
|
||||
{
|
||||
if ($Message === VM_UPDATE) {
|
||||
$this->UpdateData();
|
||||
}
|
||||
}
|
||||
|
||||
public function GetVisualizationTile()
|
||||
{
|
||||
$initialData = '<script>handleMessage(' . json_encode($this->UpdateData()) . ');</script>';
|
||||
$html = file_get_contents(__DIR__ . '/module.html');
|
||||
return $html . $initialData;
|
||||
}
|
||||
|
||||
public function RequestAction($Ident, $Value)
|
||||
{
|
||||
if ($Ident === 'update') {
|
||||
return $this->UpdateData(); // Rückgabe für Visualisierung
|
||||
}
|
||||
throw new \Exception("Unknown Ident: $Ident");
|
||||
}
|
||||
|
||||
public function UpdateData()
|
||||
{
|
||||
$start = strtotime('today 00:00');
|
||||
$end = time();
|
||||
|
||||
$prod = $this->GetDailyTotal($this->ReadPropertyInteger('VarProduction'), $start, $end);
|
||||
$cons = $this->GetDailyTotal($this->ReadPropertyInteger('VarConsumption'), $start, $end);
|
||||
$feed = $this->GetDailyTotal($this->ReadPropertyInteger('VarFeedIn'), $start, $end);
|
||||
$grid = $this->GetDailyTotal($this->ReadPropertyInteger('VarGrid'), $start, $end);
|
||||
|
||||
$prodCons = $prod > 0 ? (($cons - $grid) / $prod) * 100 : 0;
|
||||
$prodFeed = $prod > 0 ? 100 - $prodCons : 0;
|
||||
$consPV = $cons > 0 ? min($prod, ($cons - $grid)) / $cons * 100 : 0;
|
||||
$consGrid = $cons > 0 ? 100 - $consPV : 0;
|
||||
|
||||
$data = [
|
||||
'prodCons' => round($prodCons, 1),
|
||||
'prodFeed' => round($prodFeed, 1),
|
||||
'consPV' => round($consPV, 1),
|
||||
'consGrid' => round($consGrid, 1),
|
||||
'value' => [
|
||||
'prod' => round($prod, 2),
|
||||
'cons' => round($cons, 2),
|
||||
'feed' => round($feed, 2),
|
||||
'grid' => round($grid, 2),
|
||||
],
|
||||
];
|
||||
|
||||
$json = json_encode($data);
|
||||
SetValueString($this->GetIDForIdent('JSONData'), $json);
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function GetDailyTotal(int $varID, int $start, int $end)
|
||||
{
|
||||
if ($varID <= 0) return 0.0;
|
||||
|
||||
$archiveID = @IPS_GetInstanceListByModuleID('{43192F0B-135B-4CE7-A0A7-1475603F3060}')[0];
|
||||
if (!$archiveID) return 0.0;
|
||||
|
||||
$values = @AC_GetAggregatedValues($archiveID, $varID, 1, $start, $end, 1);
|
||||
return isset($values[0]['Avg']) ? (float)$values[0]['Avg'] : 0.0;
|
||||
}
|
||||
}
|
||||
59
PV_Visu/readme.md
Normal file
59
PV_Visu/readme.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# PV_Visu
|
||||
|
||||
Visualisierung des Eigenverbrauchs: Tages-Quoten für PV-Produktion vs. Einspeisung und Verbrauch vs. Netz-Bezug.
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
1. [Funktionsumfang](#funktionsumfang)
|
||||
2. [Voraussetzungen](#voraussetzungen)
|
||||
3. [Installation](#installation)
|
||||
4. [Instanz einrichten](#instanz-einrichten)
|
||||
5. [WebFront](#webfront)
|
||||
6. [PHP-Befehlsreferenz](#php-befehlsreferenz)
|
||||
|
||||
## Funktionsumfang
|
||||
|
||||
- Anzeige von Tages-Quoten (%)
|
||||
- Produktion: Eigenverbrauch vs. Einspeisung
|
||||
- Verbrauch: PV-Anteil vs. Netz-Anteil
|
||||
- Zwei Balkendiagramme
|
||||
- Absolute Tages-Summen (kWh)
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- IP-Symcon ≥ 7.1
|
||||
- Archiv-Modul aktiviert
|
||||
- Vier kWh-Zähler-Variablen
|
||||
|
||||
## Installation
|
||||
|
||||
1. **Module Store** → Suche nach „PV_Visu“ und installieren
|
||||
2. **Alternativ**: Unter Module → Repositories folgende URL hinzufügen:
|
||||
```
|
||||
https://github.com/DeinRepo/PV_Visu.git
|
||||
```
|
||||
und Modul neu einlesen.
|
||||
|
||||
## Instanz einrichten
|
||||
|
||||
- **Instanz hinzufügen** → Filter: „PV_Visu“
|
||||
- Variablen zuweisen:
|
||||
|
||||
| Property | Beschreibung |
|
||||
| -------------- | -------------------------- |
|
||||
| VarProduction | PV-Produktionszähler (kWh) |
|
||||
| VarConsumption | Gesamtverbrauch (kWh) |
|
||||
| VarFeedIn | Einspeisung (kWh) |
|
||||
| VarGrid | Netz-Bezug (kWh) |
|
||||
|
||||
## WebFront
|
||||
|
||||
- **Tile-Typ:** PV_Visu
|
||||
- Balken 1 (Grün): Produktion
|
||||
- Balken 2 (Orange/Rot): Verbrauch
|
||||
|
||||
## PHP-Befehlsreferenz
|
||||
|
||||
```php
|
||||
IPS_RequestAction($InstanceID, 'update', true);
|
||||
```
|
||||
210
README.md
210
README.md
@@ -1,3 +1,209 @@
|
||||
# Energiemanager_Symconmodule_Demo
|
||||
# Symcon Belevo Energiemanagement Modul
|
||||
|
||||
Module zum Managen und Optimieren von Solarstrom und Lastmanagement innerhalb eines Symconservers
|
||||
Dieses Modul implementiert das neuartige Energiemanagementsystem der Belevo AG in IP-Symcon.
|
||||
Es verteilt elektrische Leistung auf mehrere Verbraucher (z. B. Elektroauto-Ladestationen, Boiler, Speicher) dynamisch nach Priorität und Fairness. Unterstützt werden:
|
||||
|
||||
- **Einzelhaus-Anschlüsse**
|
||||
- **ZEV/V-ZEV-Verbundsysteme**
|
||||
- **Lastenmanagement-Gemeinschaften (LEG)**
|
||||
|
||||
---
|
||||
|
||||
## Besonderheiten & Vorteile
|
||||
|
||||
- **Vollständige Skalierbarkeit** von Einfamilienhaus über ZEV/V-ZEV bis LEG
|
||||
- **Standardisierte Schnittstelle** für Manager ↔ Verbraucher (kein ständiges Nachkonfigurieren)
|
||||
- **Fairness** bei gleicher Priorität durch Round-Robin-Verteilung der Leistungsschritte
|
||||
- **Bidirektionaler Betrieb** (z. B. Batteriespeicher, bidirektionale Ladestationen)
|
||||
- **Dynamische Tarifgestaltung** über Prioritätsklassen
|
||||
- **Automatisches Peak-Shaving** ohne teure Netzspitzen
|
||||
- **Einfaches Hinzufügen/Entfernen** von Verbrauchern (aktuell manuell, später automatisch möglich)
|
||||
|
||||
---
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- IP-Symcon **≥ 8.0**
|
||||
- Zugriff auf das Git-Repository
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. In IP-Symcon **Module Control** öffnen
|
||||
2. **Hinzufügen → Git-Repository**
|
||||
3. URL eintragen: https://git.belevo.ch/dh/Symcon_Belevo_Energiemanagement_testing.git
|
||||
4. Modul installieren und IP-Symcon neu starten
|
||||
|
||||
---
|
||||
|
||||
## 1. Manager-Instanz anlegen
|
||||
|
||||
1. Rechtsklick auf **Instanzen** → **Instanz hinzufügen**
|
||||
2. Filter: **Belevo**
|
||||
3. **„Manager“** auswählen und Instanz erstellen
|
||||
|
||||
### Manager-Eigenschaften
|
||||
|
||||
| Property | Typ | Beschreibung |
|
||||
|------------------------------------|-----------------|------------------------------------------------------------------------------|
|
||||
| **Zeit_Zwischen_Zustandswechseln** | Integer (Sek.) | Dauer zwischen zwei Zyklen (Default: 5 s) |
|
||||
| **Interval** | Integer (Sek.) | Haupt-Timerintervall zur Neuberechnung |
|
||||
| **Sollleistung_Max** | Float (Watt) | Maximale erlaubte Gesamtleistung am Netzanschluss |
|
||||
| **Ueberschussleistung** | Float (Watt) | Untergrenze für Solarlade-Mode (z. B. 0 W) |
|
||||
| **HauptmanagerAktiv** | Boolean | Schaltet die gesamte Manager-Logik ein/aus |
|
||||
| **Verbraucher_Liste** | InstanceList | Liste aller Verbraucher-Instanzen, die der Manager steuern soll |
|
||||
|
||||
### Manager-Statusvariablen
|
||||
|
||||
| Ident | Typ | Beschreibung |
|
||||
|-------------------------|-----------|------------------------------------------------------------------------------|
|
||||
| **Is_Peak_Shaving** | Boolean | `true` = Peak-Shaving-Mode; `false` = Solarlade-Mode |
|
||||
| **LetzteBerechnung** | DateTime | Zeitstempel der letzten Ausführung |
|
||||
| **Aktuelle_Differenz** | Float (W) | Zuletzt berechnete Soll–Ist-Differenz |
|
||||
|
||||
---
|
||||
|
||||
## 2. Verbraucher-Instanzen anlegen
|
||||
|
||||
Für jedes zu steuernde Gerät:
|
||||
|
||||
1. Rechtsklick auf **Instanzen** → **Instanz hinzufügen**
|
||||
2. Filter: **Belevo**
|
||||
3. **„Belevo EMS Verbraucher“** auswählen und Instanz erstellen
|
||||
|
||||
### Verbraucher-Eigenschaften
|
||||
|
||||
| Property | Typ | Beschreibung |
|
||||
|-----------------|------------|----------------------------------------------------------------------------|
|
||||
| **PV_Prio** | Integer | Priorität im Solarlade-Mode (niedriger = höhere Priorität) |
|
||||
| **Sperre_Prio** | Integer | Priorität im Peak-Shaving-Mode (niedriger = höhere Priorität) |
|
||||
| **PowerSteps** | String | JSON-Array möglicher Leistungsstufen in Watt, z. B. `[0,1000,2000]` |
|
||||
|
||||
### Automatisch angelegte Variablen
|
||||
|
||||
| Ident | Typ | Profil | Beschreibung |
|
||||
|----------------------|----------|--------------|-----------------------------------------------------------------------|
|
||||
| **Power** | Float | ~Watt~~W~ | Aktuell angeforderte Leistung (W) |
|
||||
| **Aktuelle_Leistung**| Float | ~Watt~~W~ | Vom Manager zugewiesene Leistung im aktuellen Zyklus (W) |
|
||||
| **Bezogene_Energie** | Float | Wh | Kumulierte Energieabnahme seit Zyklusstart (Wh) |
|
||||
| **Leistung_Delta** | Float | ~Watt~~W~ | Differenz zwischen `Power` und `Aktuelle_Leistung` (W) |
|
||||
| **Idle** | Boolean | — | `true`, wenn Verbraucher gerade keine Änderung benötigt |
|
||||
|
||||
---
|
||||
|
||||
## 3. Funktionsweise
|
||||
|
||||
### 3.1 Mode-Entscheid
|
||||
|
||||
- In jedem Zyklus liest der Manager den **aktuellen Ist-Netzanschlusswert** (Summe aller `Aktuelle_Leistung`).
|
||||
- Er berechnet den **Schwellwert** als
|
||||
Threshold = (Sollleistung_Max + Ueberschussleistung) / 2
|
||||
|
||||
markdown
|
||||
Kopieren
|
||||
Bearbeiten
|
||||
- Ist der Ist-Wert **> Threshold**, setzt er **Peak-Shaving** (`Is_Peak_Shaving = true`), sonst **Solarladen** (`false`).
|
||||
- Diese Boolean teilt er allen Verbrauchern mit, damit sie ihre `PowerSteps`-Arrays entsprechend vorbereiten.
|
||||
|
||||
### 3.2 Soll–Ist-Berechnung
|
||||
|
||||
- Differenz **D** = `Sollleistung_Max` − Ist-Wert
|
||||
- **D > 0**: Bedarf → Solarlade-Zuteilung
|
||||
- **D < 0**: Überschuss → Peak-Shaving-Reduktion
|
||||
|
||||
### 3.3 Leistungszuweisung
|
||||
|
||||
- Für jeden Verbraucher wählt der Manager die **höchste** Stufe aus `PowerSteps`, die die verbleibende Differenz \|D\| **nicht überschreitet**.
|
||||
- Er durchläuft die Verbraucher nacheinander (Round-Robin), bis \|D\| annähernd erschöpft ist.
|
||||
|
||||
**Beispiel**
|
||||
- Verbraucher A: `[0,1000,2000]` Prio 1 5 kWh aktuell 2000 W
|
||||
- Verbraucher B: `[0,500,1500,2500]` Prio 2 0 kWh aktuell 500 W
|
||||
- Verbraucher C: `[0,500,1500,4000]` Prio 2 10 kWh aktuell 0 W
|
||||
- Verbraucher D: `[0,500,1200,2500]` Prio 2 15 kWh aktuell 0 W
|
||||
- Verbraucher E: `[0,200,1500]` Prio 3 15 kWh aktuell 0 W
|
||||
|
||||
---
|
||||
|
||||
#### Sortierte Stufen-Arrays
|
||||
|
||||
**Prio 1**
|
||||
[A0], [A1000], [A2000]
|
||||
|
||||
**Prio 2**
|
||||
[B0], [C0], [D0],
|
||||
[B500], [C500], [D500],
|
||||
[D1200], [B1500], [C1500],
|
||||
[B2500], [D2500], [C4000]
|
||||
|
||||
|
||||
**Prio 3**
|
||||
[E0], [E200], [E1500]
|
||||
|
||||
|
||||
---
|
||||
|
||||
#### Verteilungsschritte
|
||||
|
||||
1. **Delta berechnen**
|
||||
- Netzbezug = –5000 W
|
||||
- Sollwert = 0 W
|
||||
→ Differenz D = 5000 W
|
||||
|
||||
2. **Bereits verteilte Leistungen aufsummieren**
|
||||
- A = 2000 W
|
||||
- B = 500 W
|
||||
→ GesamtVerteilbareLeistung = 5000 + 2000 + 500 = **7500 W**
|
||||
|
||||
3. **Prio 1**
|
||||
7500 − 0 W (A0) = 7500
|
||||
7500 − 1000 W (A1000) = 6500
|
||||
(6500 + 1000 W vorher) − 2000 W (A2000) = 5500
|
||||
|
||||
→ A erhält **2000 W**
|
||||
→ Rest: 5500 W
|
||||
|
||||
4. **Prio 2**
|
||||
5500 − 0 W (B0) = 5500
|
||||
5500 − 0 W (C0) = 5500
|
||||
5500 − 0 W (D0) = 5500
|
||||
5500 − 500 W (B500) = 5000
|
||||
5000 − 500 W (C500) = 4500
|
||||
4500 − 500 W (D500) = 4000
|
||||
(4000 + 500 W vorher) − 1200 W (D1200) = 3300
|
||||
(3300 + 500 W vorher) − 1500 W (B1500) = 2300
|
||||
(2300 + 500 W vorher) − 1500 W (C1500) = 1300
|
||||
(1300 + 1500 W vorher) − 2500 W (B2500) = 300
|
||||
(300 + 1500 W vorher) − 2500 W (C2500) → negativ → nicht möglich
|
||||
|
||||
→ B erhält **2500 W**, C **1500 W**, D **1200 W**
|
||||
→ Rest: 300 W
|
||||
|
||||
5. **Prio 3**
|
||||
300 − 0 W (E0) = 300
|
||||
300 − 200 W (E200) = 100
|
||||
|
||||
→ E erhält **200 W**
|
||||
→ Verteilung beendet (Rest 100 W ungenutzt)
|
||||
|
||||
---
|
||||
|
||||
Durch dieses Verfahren – Aufaddierung der bereits verteilten Leistungen, Sortierung nach Priorität und Fairness sowie sukzessive Abarbeitung der Stufen-Arrays – wird die **Gesamtleistung optimal und gerecht** auf alle Verbraucher verteilt.
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 4. Registrierung der Verbraucher
|
||||
|
||||
- **Aktuell müssen alle Verbraucher manuell** in `Verbraucher_Liste` eingetragen werden.
|
||||
- **Zukünftige Versionen** werden eine automatische Geräteerkennung unterstützen, so dass neue Verbraucher ohne Konfigurationsaufwand hinzukommen können.
|
||||
|
||||
---
|
||||
|
||||
## 5. Support & Entwicklung
|
||||
|
||||
- **Issues & Feature-Requests** bitte im Git-Repository eröffnen.
|
||||
- **Entwickler**: Belevo AG
|
||||
|
||||
0
Symcon_Publish_to_Shelly_MQTT/README.md
Normal file
0
Symcon_Publish_to_Shelly_MQTT/README.md
Normal file
61
Symcon_Publish_to_Shelly_MQTT/form.json
Normal file
61
Symcon_Publish_to_Shelly_MQTT/form.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"name": "broker_address",
|
||||
"caption": "Broker-Adresse"
|
||||
},
|
||||
{
|
||||
"type": "NumberSpinner",
|
||||
"name": "broker_port",
|
||||
"caption": "Broker-Port"
|
||||
},
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"name": "username",
|
||||
"caption": "Benutzername"
|
||||
},
|
||||
{
|
||||
"type": "PasswordTextBox",
|
||||
"name": "password",
|
||||
"caption": "Passwort"
|
||||
},
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"name": "Topic",
|
||||
"caption": "MQTT Topic"
|
||||
},
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"name": "client_id",
|
||||
"caption": "Klient Id"
|
||||
},
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"name": "mqtt_instance_id",
|
||||
"caption": "MQTT Id"
|
||||
},
|
||||
{
|
||||
"type": "NumberSpinner",
|
||||
"name": "msg_id",
|
||||
"caption": "Message ID"
|
||||
},
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"name": "src",
|
||||
"caption": "Source"
|
||||
},
|
||||
{
|
||||
"type": "ValidationTextBox",
|
||||
"name": "method",
|
||||
"caption": "Method"
|
||||
},
|
||||
{
|
||||
"type": "SelectVariable",
|
||||
"name": "switch_bool",
|
||||
"caption": "Variable mit dem zu regelnden Shelly Kontakt",
|
||||
"validVariableTypes": [0]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
12
Symcon_Publish_to_Shelly_MQTT/module.json
Normal file
12
Symcon_Publish_to_Shelly_MQTT/module.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"id": "{F6020D23-AA8C-34C5-D92F-9034BEFBBCD8}",
|
||||
"name": "Symcon_Publish_to_Shelly_MQTT",
|
||||
"type": 3,
|
||||
"vendor": "Belevo AG",
|
||||
"aliases": [],
|
||||
"parentRequirements": [],
|
||||
"childRequirements": [],
|
||||
"implemented": [],
|
||||
"prefix": "GEF",
|
||||
"url": ""
|
||||
}
|
||||
90
Symcon_Publish_to_Shelly_MQTT/module.php
Normal file
90
Symcon_Publish_to_Shelly_MQTT/module.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
class Symcon_Publish_to_Shelly_MQTT extends IPSModule
|
||||
{
|
||||
public function Create()
|
||||
{
|
||||
parent::Create();
|
||||
|
||||
// MQTT-Verbindungsparameter als Properties
|
||||
$this->RegisterPropertyString("broker_address", "");
|
||||
$this->RegisterPropertyInteger("broker_port", 1883);
|
||||
$this->RegisterPropertyString("username", "");
|
||||
$this->RegisterPropertyString("password", "");
|
||||
$this->RegisterPropertyString("Topic", "");
|
||||
$this->RegisterPropertyString("client_id", "");
|
||||
$this->RegisterPropertyInteger("mqtt_instance_id", 0);
|
||||
// Nachricht-Payload-Parameter als Properties
|
||||
$this->RegisterPropertyInteger("msg_id", 100);
|
||||
$this->RegisterPropertyString("src", "user1");
|
||||
$this->RegisterPropertyString("method", "Switch.Set");
|
||||
$this->RegisterPropertyInteger("switch_bool", 0); // ID der Bool-Variable
|
||||
|
||||
|
||||
$this->RegisterTimer("Timer_Influx",5000,"IPS_RequestAction(" . $this->InstanceID . ', "GetAction", "");');
|
||||
}
|
||||
|
||||
public function ApplyChanges()
|
||||
{
|
||||
parent::ApplyChanges();
|
||||
|
||||
}
|
||||
public function RequestAction($Ident, $Value)
|
||||
{
|
||||
IPS_LogMessage("ShellySwitchSender", "RequestAction gestartet");
|
||||
switch ($Ident) {
|
||||
case "GetAction":
|
||||
$this->GetAction();
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Invalid action");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetAction()
|
||||
{
|
||||
IPS_LogMessage("ShellySwitchSender", "GetAction gestartet");
|
||||
|
||||
$mqttInstanceID = $this->ReadPropertyInteger("mqtt_instance_id");
|
||||
$topic = $this->ReadPropertyString("Topic");
|
||||
|
||||
$msg_id = $this->ReadPropertyInteger("msg_id");
|
||||
$src = $this->ReadPropertyString("src");
|
||||
$method = $this->ReadPropertyString("method");
|
||||
$boolVarID = $this->ReadPropertyInteger("switch_bool");
|
||||
|
||||
if (!IPS_VariableExists($boolVarID)) {
|
||||
IPS_LogMessage("ShellySwitchSender", "FEHLER: Bool-Variable mit ID $boolVarID existiert nicht.");
|
||||
return;
|
||||
}
|
||||
|
||||
$onValue = GetValueBoolean($boolVarID);
|
||||
|
||||
$payload = [
|
||||
"id" => 0,
|
||||
"src" => $src,
|
||||
"method" => $method,
|
||||
"params" => [
|
||||
"id" => $msg_id,
|
||||
"on" => $onValue
|
||||
]
|
||||
];
|
||||
|
||||
$jsonPayload = json_encode($payload);
|
||||
|
||||
IPS_LogMessage("ShellySwitchSender", "MQTT Payload: $jsonPayload");
|
||||
|
||||
if (!IPS_InstanceExists($mqttInstanceID)) {
|
||||
IPS_LogMessage("ShellySwitchSender", "FEHLER: MQTT-Instanz-ID $mqttInstanceID existiert nicht.");
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ RICHTIG: Direkt senden über MQTTClient_SendMessage
|
||||
MQTTClient_SendMessage($mqttInstanceID, $topic, $jsonPayload, 0, false);
|
||||
IPS_LogMessage("ShellySwitchSender", "Nachricht erfolgreich gesendet");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user