Files
Symcon_Belevo_Energiemanage…/VGT_Sub/module.php
2026-02-10 13:51:09 +01:00

210 lines
6.9 KiB
PHP

<?php
declare(strict_types=1);
class VGT_Sub extends IPSModule
{
public function Create()
{
parent::Create();
// --- Eigenschaften ---
$this->RegisterPropertyString('MQTTBaseTopic', 'Test VGT_Steuerung/Komm');
$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);
// --- Variablen ---
$this->RegisterVariableString('Strategy', 'Strategy', '', 10);
$this->RegisterVariableInteger('PowerSetpoint', 'Power Setpoint', '', 20);
// Visu
$this->RegisterVariableFloat('Visu_SoC', 'State of Charge', '~Intensity.100', 30);
$this->RegisterVariableFloat('Visu_Production', 'Power Production', '~Watt', 40);
$this->RegisterVariableBoolean('Visu_IsReady', 'Is Ready', '~Switch', 50);
$this->EnableAction('Strategy');
$this->EnableAction('PowerSetpoint');
}
public function ApplyChanges()
{
parent::ApplyChanges();
// --- MQTT Filter setzen ---
// Wir abonnieren alles, was mit dem BaseTopic beginnt
$topic = $this->ReadPropertyString('MQTTBaseTopic');
$cleanTopic = preg_quote($topic, '/');
// Filterformat für MQTT Splitter
$this->SetReceiveDataFilter(".*\"Topic\":\"" . $cleanTopic . "/.*\".*");
// --- Events registrieren ---
$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);
}
// --- DATEN EMPFANGEN (Vom MQTT Splitter) ---
public function ReceiveData($JSONString)
{
$data = json_decode($JSONString);
// Der Splitter liefert 'Topic' und 'Payload' (Payload ist meist UTF-8)
$payload = utf8_decode($data->Payload);
$topic = $data->Topic;
$baseTopic = $this->ReadPropertyString('MQTTBaseTopic');
// Debugging (optional, schreibt ins Meldungsfenster)
// $this->SendDebug('MQTT In', "Topic: $topic | Payload: $payload", 0);
// Routing
if ($topic === $baseTopic . '/Writebefehl') {
$this->ProcessWriteCommand($payload);
}
elseif ($topic === $baseTopic . '/Lesebefehl') {
$this->ProcessReadCommand();
}
}
// --- LOGIK ---
public function MessageSink($TimeStamp, $SenderID, $Message, $Data)
{
if ($Message == VM_UPDATE) {
$socID = $this->ReadPropertyInteger('SourceSoC');
if ($SenderID == $socID) {
$this->SetValue('Visu_SoC', $Data[0]);
}
$this->ExecuteControlLogic();
}
}
public function RequestAction($Ident, $Value)
{
switch ($Ident) {
case 'Strategy':
case 'PowerSetpoint':
$this->SetValue($Ident, $Value);
$this->ExecuteControlLogic();
break;
}
}
private function ProcessWriteCommand($jsonInput)
{
if ($jsonInput == "") return;
$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();
// Antwort Senden
$output = [
'power_setpoint' => $this->GetValue('PowerSetpoint'),
'strategy' => $this->GetValue('Strategy')
];
$this->SendMQTT($this->ReadPropertyString('MQTTBaseTopic') . '/Writebefehl Antwort', json_encode($output));
}
private function ProcessReadCommand()
{
$data = $this->GatherHardwareData();
$this->SetValue('Visu_Production', $data['raw_prod']);
$this->SetValue('Visu_IsReady', $data['is_ready']);
$mqttData = [
'power_production' => -1 * $data['raw_prod'],
'is_ready' => $data['is_ready'],
'is_running' => $data['is_running'],
'state_of_charge' => $data['soc'],
'min_soc' => $data['min_soc'],
'max_soc' => $data['max_soc']
];
$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);
}
}
}
private function GatherHardwareData()
{
$safeGet = function($propName) {
$id = $this->ReadPropertyInteger($propName);
return IPS_VariableExists($id) ? GetValue($id) : 0;
};
return [
'raw_prod' => $safeGet('SourcePowerProd'),
'is_ready' => (bool)$safeGet('SourceIsReady'),
'is_running' => (bool)$safeGet('SourceIsRunning'),
'soc' => $safeGet('SourceSoC'),
'min_soc' => $safeGet('SourceMinSoC'),
'max_soc' => $safeGet('SourceMaxSoC'),
];
}
// Hilfsfunktion: MQTT Senden
protected function SendMQTT($Topic, $Payload)
{
// DataID für MQTT Publish (TX) an den Splitter
$Data['DataID'] = '{F7A0DD2E-7684-95C0-64C2-D2A9DC47577B}';
$Data['PacketType'] = 3; // 3 = MQTT Publish
$Data['QualityOfService'] = 0;
$Data['Retain'] = false;
$Data['Topic'] = $Topic;
$Data['Payload'] = $Payload;
$JSON = json_encode($Data);
// Senden an Parent (MQTT Splitter)
$this->SendDataToParent($JSON);
}
}
?>