214 lines
8.5 KiB
PHP
214 lines
8.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
class VGT_Sub extends IPSModule
|
|
{
|
|
// GUIDs für den offiziellen IP-Symcon Datenfluss (MQTT)
|
|
const MQTT_TX_GUID = '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}'; // Wir senden das an den Splitter
|
|
const MQTT_RX_GUID = '{018EF6B5-AB94-40C6-AA53-46943E824ACF}'; // Wir empfangen das (implemented)
|
|
|
|
public function Create()
|
|
{
|
|
parent::Create();
|
|
|
|
// --- Konfiguration ---
|
|
$this->RegisterPropertyString('MQTTBaseTopic', 'Test VGT_Steuerung/Komm');
|
|
|
|
// Gateway-Konfig (Optional für Komfort)
|
|
$this->RegisterPropertyString('MQTTUser', '');
|
|
$this->RegisterPropertyString('MQTTPassword', '');
|
|
$this->RegisterPropertyBoolean('UpdateGatewayConfig', false);
|
|
|
|
// --- Hardware Quellen ---
|
|
$this->RegisterPropertyInteger('SourceSoC', 0);
|
|
$this->RegisterPropertyInteger('SourcePowerProd', 0);
|
|
$this->RegisterPropertyInteger('SourceIsReady', 0);
|
|
$this->RegisterPropertyInteger('SourceIsRunning', 0);
|
|
$this->RegisterPropertyInteger('SourceMinSoC', 0);
|
|
$this->RegisterPropertyInteger('SourceMaxSoC', 0);
|
|
$this->RegisterPropertyInteger('TargetControlVar', 0);
|
|
|
|
// --- Interne Variablen ---
|
|
$this->RegisterVariableString('Strategy', 'Strategy', '', 10);
|
|
$this->RegisterVariableInteger('PowerSetpoint', 'Power Setpoint', '', 20);
|
|
|
|
$this->EnableAction('Strategy');
|
|
$this->EnableAction('PowerSetpoint');
|
|
}
|
|
|
|
public function ApplyChanges()
|
|
{
|
|
parent::ApplyChanges();
|
|
|
|
// ---------------------------------------------------------------------
|
|
// DATENFLUSS: Filter setzen
|
|
// ---------------------------------------------------------------------
|
|
// Wir teilen dem Splitter mit: "Schick mir nur Daten für mein Topic"
|
|
// Das entlastet das System massiv.
|
|
$topic = $this->ReadPropertyString('MQTTBaseTopic');
|
|
if($topic !== "") {
|
|
$cleanTopic = preg_quote($topic, '/');
|
|
// Regex Filter auf das Feld "Topic" im JSON Datensatz
|
|
$this->SetReceiveDataFilter(".*\"Topic\":\"" . $cleanTopic . "/.*\".*");
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Komfort: Gateway Konfiguration durchreichen
|
|
// ---------------------------------------------------------------------
|
|
if ($this->ReadPropertyBoolean('UpdateGatewayConfig')) {
|
|
$this->ConfigureGateway();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Events
|
|
// ---------------------------------------------------------------------
|
|
$socID = $this->ReadPropertyInteger('SourceSoC');
|
|
if (IPS_VariableExists($socID)) $this->RegisterMessage($socID, VM_UPDATE);
|
|
$this->RegisterMessage($this->GetIDForIdent('Strategy'), VM_UPDATE);
|
|
$this->RegisterMessage($this->GetIDForIdent('PowerSetpoint'), VM_UPDATE);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// DATENFLUSS: Empfang (ReceiveData)
|
|
// -------------------------------------------------------------------------
|
|
// Wird automatisch aufgerufen, wenn der Splitter Daten hat, die zum Filter passen.
|
|
public function ReceiveData($JSONString)
|
|
{
|
|
$data = json_decode($JSONString);
|
|
|
|
// Daten aus dem JSON extrahieren (Standard Symcon Format)
|
|
$payload = utf8_decode($data->Payload);
|
|
$topic = $data->Topic;
|
|
|
|
$baseTopic = $this->ReadPropertyString('MQTTBaseTopic');
|
|
|
|
// Routing
|
|
if ($topic === $baseTopic . '/Writebefehl') {
|
|
$this->ProcessWriteCommand($payload);
|
|
}
|
|
elseif ($topic === $baseTopic . '/Lesebefehl') {
|
|
$this->ProcessReadCommand();
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// DATENFLUSS: Senden (SendDataToParent)
|
|
// -------------------------------------------------------------------------
|
|
protected function SendMQTT($Topic, $Payload)
|
|
{
|
|
// Wir bauen das JSON Paket exakt so, wie der MQTT Splitter es erwartet
|
|
$Data = [
|
|
'DataID' => self::MQTT_TX_GUID, // Die ID für "MQTT Publish"
|
|
'PacketType' => 3, // 3 = Publish
|
|
'QualityOfService' => 0,
|
|
'Retain' => false,
|
|
'Topic' => $Topic,
|
|
'Payload' => $Payload
|
|
];
|
|
|
|
// Ab an den Parent (Splitter)
|
|
$this->SendDataToParent(json_encode($Data));
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Interne Logik
|
|
// -------------------------------------------------------------------------
|
|
private function ProcessWriteCommand($jsonInput)
|
|
{
|
|
$data = json_decode($jsonInput, true);
|
|
if ($data === null) return;
|
|
|
|
if (isset($data['power_setpoint'])) {
|
|
$val = $data['power_setpoint'];
|
|
if ($val > 6000) $val = 6000; elseif ($val < -6000) $val = -6000;
|
|
$this->SetValue('PowerSetpoint', $val);
|
|
}
|
|
if (isset($data['strategy'])) {
|
|
$this->SetValue('Strategy', $data['strategy']);
|
|
}
|
|
|
|
$this->ExecuteControlLogic();
|
|
|
|
$output = ['power_setpoint' => $this->GetValue('PowerSetpoint'), 'strategy' => $this->GetValue('Strategy')];
|
|
$this->SendMQTT($this->ReadPropertyString('MQTTBaseTopic') . '/Writebefehl Antwort', json_encode($output));
|
|
}
|
|
|
|
private function ProcessReadCommand()
|
|
{
|
|
$safeGet = function($prop) { $id = $this->ReadPropertyInteger($prop); return IPS_VariableExists($id) ? GetValue($id) : 0; };
|
|
|
|
$mqttData = [
|
|
'power_production' => -1 * $safeGet('SourcePowerProd'),
|
|
'is_ready' => (bool)$safeGet('SourceIsReady'),
|
|
'is_running' => (bool)$safeGet('SourceIsRunning'),
|
|
'state_of_charge' => $safeGet('SourceSoC'),
|
|
'min_soc' => $safeGet('SourceMinSoC'),
|
|
'max_soc' => $safeGet('SourceMaxSoC')
|
|
];
|
|
$this->SendMQTT($this->ReadPropertyString('MQTTBaseTopic') . '/Lesebefehl Antwort', json_encode($mqttData));
|
|
}
|
|
|
|
private function ExecuteControlLogic()
|
|
{
|
|
$targetID = $this->ReadPropertyInteger('TargetControlVar');
|
|
if (!IPS_VariableExists($targetID)) return;
|
|
|
|
$strategy = $this->GetValue('Strategy');
|
|
$setpoint = $this->GetValue('PowerSetpoint');
|
|
$socID = $this->ReadPropertyInteger('SourceSoC');
|
|
$soc = IPS_VariableExists($socID) ? GetValue($socID) : 0;
|
|
|
|
if ($strategy == "activate") {
|
|
RequestAction($targetID, $setpoint);
|
|
} elseif ($strategy == "stop") {
|
|
RequestAction($targetID, 0);
|
|
} else {
|
|
if ((int)$soc == 50) RequestAction($targetID, 0);
|
|
elseif ($soc < 50) RequestAction($targetID, 2500);
|
|
else RequestAction($targetID, -2500);
|
|
}
|
|
}
|
|
|
|
// Hilfsfunktion: IO Konfigurieren
|
|
private function ConfigureGateway()
|
|
{
|
|
$instance = IPS_GetInstance($this->InstanceID);
|
|
$parentId = $instance['ConnectionID'];
|
|
|
|
if ($parentId > 0) {
|
|
$user = $this->ReadPropertyString('MQTTUser');
|
|
$pass = $this->ReadPropertyString('MQTTPassword');
|
|
|
|
// Versuch 1: Parent ist direkt konfigurierbar (z.B. Schnittcher Splitter)
|
|
if (@IPS_GetProperty($parentId, 'Username') !== false) {
|
|
IPS_SetProperty($parentId, 'Username', $user);
|
|
IPS_SetProperty($parentId, 'Password', $pass);
|
|
IPS_ApplyChanges($parentId);
|
|
return;
|
|
}
|
|
|
|
// Versuch 2: Standard Symcon Weg (Device -> Splitter -> IO)
|
|
// Wir müssen den IO unter dem Splitter finden
|
|
$parentInfo = IPS_GetInstance($parentId);
|
|
$ioID = $parentInfo['ConnectionID'];
|
|
if ($ioID > 0) {
|
|
if (@IPS_GetProperty($ioID, 'Username') !== false) {
|
|
IPS_SetProperty($ioID, 'Username', $user);
|
|
IPS_SetProperty($ioID, 'Password', $pass);
|
|
IPS_ApplyChanges($ioID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function RequestAction($Ident, $Value) {
|
|
$this->SetValue($Ident, $Value);
|
|
$this->ExecuteControlLogic();
|
|
}
|
|
|
|
public function MessageSink($TimeStamp, $SenderID, $Message, $Data) {
|
|
if ($Message == VM_UPDATE) $this->ExecuteControlLogic();
|
|
}
|
|
}
|
|
?>
|