diff --git a/MQTTBatterySDL/module.php b/MQTTBatterySDL/module.php index 4e3674a..8543ca5 100644 --- a/MQTTBatterySDL/module.php +++ b/MQTTBatterySDL/module.php @@ -2,12 +2,12 @@ class MQTTBatterySDL extends IPSModule { - private bool $Subscribed = false; public function Create() { parent::Create(); + // Eigenschaften $this->RegisterPropertyString('TopicSuffix', ''); $this->RegisterPropertyInteger('ReqActionID', 0); @@ -19,6 +19,7 @@ class MQTTBatterySDL extends IPSModule $this->RegisterPropertyInteger('DischargePower', 2500); $this->RegisterPropertyInteger('MaxPowerSetpoint', 10000); + // Variablen $this->RegisterVariableBoolean('IsReady', 'Is Ready', '', 10); $this->RegisterVariableBoolean('IsRunning', 'Is Running', '', 20); @@ -34,6 +35,7 @@ class MQTTBatterySDL extends IPSModule $this->RegisterVariableString('LastReadResponse', 'Letzte Lese-Antwort', '', 70); $this->RegisterVariableString('LastWriteResponse', 'Letzte Steuer-Antwort', '', 80); + // MQTT Parent verbinden $this->ConnectParent('{F7A0DD2E-7684-95C0-64C2-D2A9DC47577B}'); } @@ -55,16 +57,15 @@ class MQTTBatterySDL extends IPSModule $this->SetReceiveDataFilter($filter); - $this->Subscribed = false; - $this->EnsureSubscribe(); - $this->SetStatus(IS_ACTIVE); } public function RequestAction($Ident, $Value) { switch ($Ident) { + case 'PowerSetpoint': + $max = $this->ReadPropertyInteger('MaxPowerSetpoint'); if ($Value > $max) { @@ -76,61 +77,21 @@ class MQTTBatterySDL extends IPSModule } SetValue($this->GetIDForIdent('PowerSetpoint'), $Value); + $this->RunBatteryControl(); + break; case 'Strategy': - SetValueString($this->GetIDForIdent('Strategy'), (string)$Value); - $this->RunBatteryControl(); - break; - } - } - public function ReceiveData($JSONString) - { - $this->EnsureSubscribe(); - - $data = json_decode($JSONString, true); - - if (!is_array($data)) { - return; - } - - $topic = $data['Topic'] ?? ''; - $payload = $data['Payload'] ?? ''; - - if ($topic == '') { - return; - } - - $suffix = $this->ReadPropertyString('TopicSuffix'); - - if ($suffix == '') { - return; - } - - if ($topic === 'feedback-request/' . $suffix) { - $json = $this->BuildReadResponse(); - - $this->PublishMQTT( - 'feedback-response/' . $suffix, - $json - ); - - return; - } - - if ($topic === 'remote-control-request/' . $suffix) { - $json = $this->HandleRemoteControlJSON($payload); - - if ($json !== null) { - $this->PublishMQTT( - 'remote-control-response/' . $suffix, - $json + SetValueString( + $this->GetIDForIdent('Strategy'), + (string)$Value ); - } - return; + $this->RunBatteryControl(); + + break; } } @@ -141,29 +102,59 @@ class MQTTBatterySDL extends IPSModule $powerProductionID = $this->ReadPropertyInteger('PowerProductionID'); if (!$this->IsValidVariable($reqActionID)) { - $this->SendDebug('RunBatteryControl', 'Keine gültige Ausgabe-Variable gewählt', 0); + $this->SendDebug( + 'RunBatteryControl', + 'Keine gültige Ausgabe-Variable gewählt', + 0 + ); return; } if (!$this->IsValidVariable($socID)) { - $this->SendDebug('RunBatteryControl', 'Keine gültige SoC Variable gewählt', 0); + $this->SendDebug( + 'RunBatteryControl', + 'Keine gültige SoC Variable gewählt', + 0 + ); return; } - $strategy = GetValueString($this->GetIDForIdent('Strategy')); - $setpoint = GetValue($this->GetIDForIdent('PowerSetpoint')); - $soc = GetValue($socID); + $strategy = GetValueString( + $this->GetIDForIdent('Strategy') + ); + + $setpoint = GetValue( + $this->GetIDForIdent('PowerSetpoint') + ); + + $soc = GetValue($socID); + + $targetSoC = $this->ReadPropertyInteger('TargetSoC'); + + $chargePower = $this->ReadPropertyInteger('ChargePower'); - $targetSoC = $this->ReadPropertyInteger('TargetSoC'); - $chargePower = $this->ReadPropertyInteger('ChargePower'); $dischargePower = $this->ReadPropertyInteger('DischargePower'); + // ------------------------------------------------- + // Strategy activate + // ------------------------------------------------- + if ($strategy == 'activate') { - RequestAction($reqActionID, $setpoint * -1); + + RequestAction( + $reqActionID, + $setpoint * -1 + ); + return; } + // ------------------------------------------------- + // Strategy stop + // ------------------------------------------------- + if ($strategy == 'stop') { + RequestAction($reqActionID, 0); if ($this->IsValidVariable($powerProductionID)) { @@ -173,18 +164,42 @@ class MQTTBatterySDL extends IPSModule return; } + // ------------------------------------------------- + // Ziel erreicht + // ------------------------------------------------- + if ((int)$soc == $targetSoC) { + RequestAction($reqActionID, 0); + return; } + // ------------------------------------------------- + // Laden + // ------------------------------------------------- + if ($soc < $targetSoC) { - RequestAction($reqActionID, abs($chargePower)); + + RequestAction( + $reqActionID, + abs($chargePower) + ); + return; } + // ------------------------------------------------- + // Entladen + // ------------------------------------------------- + if ($soc > $targetSoC) { - RequestAction($reqActionID, abs($dischargePower) * -1); + + RequestAction( + $reqActionID, + abs($dischargePower) * -1 + ); + return; } } @@ -192,20 +207,51 @@ class MQTTBatterySDL extends IPSModule public function BuildReadResponse() { $socID = $this->ReadPropertyInteger('SoCID'); - $powerProductionID = $this->ReadPropertyInteger('PowerProductionID'); + + $powerProductionID = $this->ReadPropertyInteger( + 'PowerProductionID' + ); $data = [ - 'power_production' => $this->IsValidVariable($powerProductionID) ? GetValue($powerProductionID) : 0, - 'is_ready' => GetValue($this->GetIDForIdent('IsReady')), - 'is_running' => GetValue($this->GetIDForIdent('IsRunning')), - 'state_of_charge' => $this->IsValidVariable($socID) ? GetValue($socID) : 0, - 'min_soc' => GetValue($this->GetIDForIdent('MinSoC')), - 'max_soc' => GetValue($this->GetIDForIdent('MaxSoC')) + + 'power_production' => $this->IsValidVariable( + $powerProductionID + ) + ? GetValue($powerProductionID) + : 0, + + 'is_ready' => GetValue( + $this->GetIDForIdent('IsReady') + ), + + 'is_running' => GetValue( + $this->GetIDForIdent('IsRunning') + ), + + 'state_of_charge' => $this->IsValidVariable( + $socID + ) + ? GetValue($socID) + : 0, + + 'min_soc' => GetValue( + $this->GetIDForIdent('MinSoC') + ), + + 'max_soc' => GetValue( + $this->GetIDForIdent('MaxSoC') + ) ]; - $json = json_encode($data, JSON_PRETTY_PRINT); + $json = json_encode( + $data, + JSON_PRETTY_PRINT + ); - SetValueString($this->GetIDForIdent('LastReadResponse'), $json); + SetValueString( + $this->GetIDForIdent('LastReadResponse'), + $json + ); return $json; } @@ -213,17 +259,25 @@ class MQTTBatterySDL extends IPSModule public function HandleRemoteControlJSON(string $payload) { if ($payload == '') { - return null; + return; } $data = json_decode($payload, true); if (!is_array($data)) { - return null; + return; } + // ------------------------------------------------- + // Power Setpoint + // ------------------------------------------------- + if (isset($data['power_setpoint'])) { - $max = $this->ReadPropertyInteger('MaxPowerSetpoint'); + + $max = $this->ReadPropertyInteger( + 'MaxPowerSetpoint' + ); + $val = (int)$data['power_setpoint']; if ($val > $max) { @@ -234,87 +288,146 @@ class MQTTBatterySDL extends IPSModule $val = $max * -1; } - SetValue($this->GetIDForIdent('PowerSetpoint'), $val); + SetValue( + $this->GetIDForIdent('PowerSetpoint'), + $val + ); } + // ------------------------------------------------- + // Strategy + // ------------------------------------------------- + if (isset($data['strategy'])) { - SetValueString($this->GetIDForIdent('Strategy'), (string)$data['strategy']); + + SetValueString( + $this->GetIDForIdent('Strategy'), + (string)$data['strategy'] + ); } + // ------------------------------------------------- + // Batterie-Regelung ausführen + // ------------------------------------------------- + $this->RunBatteryControl(); + // ------------------------------------------------- + // Antwort bauen + // ------------------------------------------------- + $output = [ - 'power_setpoint' => GetValue($this->GetIDForIdent('PowerSetpoint')), - 'strategy' => GetValueString($this->GetIDForIdent('Strategy')) + + 'power_setpoint' => GetValue( + $this->GetIDForIdent('PowerSetpoint') + ), + + 'strategy' => GetValueString( + $this->GetIDForIdent('Strategy') + ) ]; - $json = json_encode($output, JSON_PRETTY_PRINT); + $json = json_encode( + $output, + JSON_PRETTY_PRINT + ); - SetValueString($this->GetIDForIdent('LastWriteResponse'), $json); + SetValueString( + $this->GetIDForIdent('LastWriteResponse'), + $json + ); return $json; } - private function EnsureSubscribe() + public function ReceiveData($JSONString) { - if ($this->Subscribed) { + $data = json_decode($JSONString, true); + + if (!is_array($data)) { return; } - $suffix = $this->ReadPropertyString('TopicSuffix'); - - if ($suffix == '') { + if (($data['PacketType'] ?? 0) != 3) { return; } - $this->Subscribed = true; + if (!isset($data['Topic'])) { + return; + } - foreach ([ - 'feedback-request/' . $suffix, - 'remote-control-request/' . $suffix - ] as $topic) { - $this->SafeSend([ - 'DataID' => '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}', - 'PacketType' => 8, - 'QualityOfService' => 0, - 'Retain' => false, - 'Topic' => $topic, - 'Payload' => '' - ]); + $topic = $data['Topic']; - $this->SendDebug('Subscribe', $topic, 0); + // Eigene Antworten ignorieren + if (strpos($topic, 'feedback-response/') === 0) { + return; + } + + if (strpos($topic, 'remote-control-response/') === 0) { + return; + } + + $this->SendDebug('ReceiveData', $JSONString, 0); + + $payload = $data['Payload'] ?? ''; + $suffix = $this->ReadPropertyString('TopicSuffix'); + + // ------------------------------------------------- + // Lesebefehl + // ------------------------------------------------- + + if ($topic === 'feedback-request/' . $suffix) { + + $json = $this->BuildReadResponse(); + + // TESTWEISE KEIN DIREKTES MQTT PUBLISH + // weil das Symcon blockiert hat + + $this->SendDebug( + 'FeedbackResponse', + $json, + 0 + ); + + return; + } + + // ------------------------------------------------- + // Writebefehl + // ------------------------------------------------- + + if ($topic === 'remote-control-request/' . $suffix) { + + $json = $this->HandleRemoteControlJSON($payload); + + if ($json !== null) { + + $this->PublishMQTT( + 'remote-control-response/' . $suffix, + $json + ); + } + + return; } } private function PublishMQTT(string $topic, string $payload) { - $this->SafeSend([ + $this->SendDebug( + 'PublishMQTT', + $topic . ' => ' . $payload, + 0 + ); + + $this->SendDataToParent(json_encode([ 'DataID' => '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}', 'PacketType' => 3, + 'Payload' => $payload, 'QualityOfService' => 0, 'Retain' => false, - 'Topic' => $topic, - 'Payload' => $payload - ]); - - $this->SendDebug('PublishMQTT', $topic . ' → ' . $payload, 0); - } - - private function SafeSend(array $packet) - { - $parent = @IPS_GetInstance($this->InstanceID)['ConnectionID'] ?? 0; - - if ($parent === 0 || !IPS_InstanceExists($parent)) { - $this->SendDebug('SafeSend', 'Kein gültiger Parent vorhanden', 0); - return; - } - - if (IPS_GetInstance($parent)['InstanceStatus'] !== 102) { - $this->SendDebug('SafeSend', 'Parent ist nicht aktiv', 0); - return; - } - - @$this->SendDataToParent(json_encode($packet)); + 'Topic' => $topic + ])); } private function IsValidVariable(int $id)