diff --git a/Bridge/README.md b/Bridge/README.md index c0631845..1a1588b1 100644 --- a/Bridge/README.md +++ b/Bridge/README.md @@ -1,7 +1,103 @@ -# IPS-Z2MBridge - Anbindung von www.zigbee2mqtt.io an IP-Symcon. - Modul für die Zigbee2MQTT Bridge +# Bridge + Modul für alle Systemweiten Funktionen von Zigbee2MQTT -Die Bridge wird aktuell NICHT benötigt. Also auch nicht anlegen! -Zukünftig wird es für die Bridge neue Funktionalitäten geben. Diese werden in der BEta ausgetestet und dann in die Master übertragen. \ No newline at end of file +## Inhaltverzeichnis +- [1. Gruppen in Z2M](#1-gruppen-in-z2m) +- [2. Konfiguration](#2-konfiguration) +- [3. Funktionen](#3-funktionen) + + | +## 3. Instanz-Funktionen + +```php +bool Z2M_InstallSymconExtension(int $InstanzID); +``` +Die aktuelle Symcon Erweiterung wird in Z2M installiert. + +-- + +```php +bool Z2M_SetLastSeen(int $InstanzID); +``` +Die Konfiguration der `last_seen` Einstellung in Z2M wird auf `epoch` verändert, damit die Instanzen in Symcon den Wert korrekt darstellen können. + +-- +```php +bool Z2M_SetPermitJoin(int $InstanzID, bool $PermitJoin); +``` + +-- +```php +bool Z2M_SetLogLevel(int $InstanzID, string $LogLevel); +``` + +-- +```php +bool Z2M_Restart(int $InstanzID); +``` + +-- +```php +bool Z2M_CreateGroup(int $InstanzID, string $GroupName); +``` + +-- +```php +bool Z2M_DeleteGroup(int $InstanzID, string $GroupName); +``` + +-- +```php +bool Z2M_RenameGroup(int $InstanzID, string $OldName, string $NewName); +``` + +-- +```php +bool Z2M_AddDeviceToGroup(int $InstanzID, string $GroupName, string $DeviceName); +``` + +-- +```php +bool Z2M_RemoveDeviceFromGroup(int $InstanzID, string $GroupName, string $DeviceName); +``` + +-- +```php +bool Z2M_RemoveAllDevicesFromGroup(int $InstanzID, string $GroupName); +``` + +-- +```php +bool Z2M_Bind(int $InstanzID, string $SourceDevice, string $TargetDevice); +``` + +-- +```php +bool Z2M_Unbind(int $InstanzID, string $SourceDevice, string $TargetDevice); +``` + +-- +```php +bool Z2M_RequestNetworkmap(int $InstanzID); +``` + +-- +```php +bool Z2M_RenameDevice(int $InstanzID, string $OldDeviceName, string $NewDeviceName); +``` + +-- +```php +bool Z2M_RemoveDevice(int $InstanzID, string $DeviceName); +``` + +-- +```php +bool Z2M_CheckOTAUpdate(int $InstanzID, string $DeviceName); +``` + +-- +```php +bool Z2M_PerformOTAUpdate(int $InstanzID, string $DeviceName); +``` diff --git a/Bridge/form.json b/Bridge/form.json index d8d7838e..b1a56d79 100644 --- a/Bridge/form.json +++ b/Bridge/form.json @@ -2,8 +2,50 @@ "elements": [ { "type": "ValidationTextBox", - "name": "MQTTTopic", - "caption": "MQTT Topic" + "name": "MQTTBaseTopic", + "caption": "MQTT Base Topic" + } + ], + "actions": [ + { + "type": "Button", + "label": "Install or upgrade Symcon-Extension", + "onClick": "Z2M_InstallSymconExtension($id);" + }, + { + "type": "Button", + "label": "Set last_seen setting to epoch", + "onClick": "Z2M_SetLastSeen($id);" + }, + { + "type": "TestCenter" + }, + { + "type": "Label", + "caption": "Spenden / Schenkung" + }, + { + "type": "Label", + "caption": "Dieses Modul ist für die nicht kommerzielle Nutzung kostenlos, Schenkungen als Unterstützung für den Autor werden hier akzeptiert: " + }, + { + "type": "RowLayout", + "items": [ + { + "type": "Image", + "onClick": "echo 'https://www.paypal.com/donate?hosted_button_id=EK4JRP87XLSHW';", + "image": "data:image/png;base64,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ZrBplgJXJ/3OA0do9FRbgAMdMwKlYjyPei1SAu5FhYGVXbSC6ZrRTMbTv5gMZgem2CeF2pdEB7F4vKvJLQHbFVdgbjvNQ5hAROuNbZCJ3eQ9wA2Kap2BBiCGPI2CdgT4rRDKUR5bdW9b2UVR2sEOmm6jcYFzmCH/HBOJg6lrJexUsVgqxjYaS9H3H8vcToYpe4BkVAfESru141QF7kfZPtkcVXvPQs1t/w64CLgUS/PakslZB9yssEVMyN2SXIvAFlU2S+O7jtDcAI4AawT2ZEvoilINoZJX/RJwMzV9at7kVoDtkz12FSGWqw+FfLgUeDOW3czFUsj1wG+wghWYvjghedfDtXcFjeUo4fOYQeQwz7VRYWcrP5k5bj6/Wfi93ezdt5dAyCPMwvZkKZk0nWw4Sc4LZmMG9YrM5R8jvBflyOPx7WFyVCwgs4AOhDGNjJALVdm1i4EPPOvxmt4JOW4MoCX0bSKYb1qOicd0erUbeAPwD5WArj5zCi948tHqbwKf4MEomKtdTWNufS3oRsAXP8WTxgDCmkLt84lLaHT9m4DrQY6bz86PF540BoCAKidjojT9YUoR6CP1IYczzpPDAK78O8kBzNuwL2yU8Y9Hf4RlEsdSl/i/oZUfhT5xhFrGpjdjYq+Wez8M3IkyTNv0T84cx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Ecx3Gmx/8AQ1faHO6uaXsAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTgtMDUtMjVUMTI6NDA6MTArMDI6MDB4/FYOAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE4LTA1LTI1VDEyOjQwOjEwKzAyOjAwCaHusgAAAEZ0RVh0c29mdHdhcmUASW1hZ2VNYWdpY2sgNi43LjgtOSAyMDE2LTA2LTE2IFExNiBodHRwOi8vd3d3LmltYWdlbWFnaWNrLm9yZ+a/NLYAAAAYdEVYdFRodW1iOjpEb2N1bWVudDo6UGFnZXMAMaf/uy8AAAAYdEVYdFRodW1iOjpJbWFnZTo6aGVpZ2h0ADUxMsDQUFEAAAAXdEVYdFRodW1iOjpJbWFnZTo6V2lkdGgANTEyHHwD3AAAABl0RVh0VGh1bWI6Ok1pbWV0eXBlAGltYWdlL3BuZz+yVk4AAAAXdEVYdFRodW1iOjpNVGltZQAxNTI3MjQ0ODEwnCKKMAAAABN0RVh0VGh1bWI6OlNpemUAMTIuOEtCQhoRQ10AAABCdEVYdFRodW1iOjpVUkkAZmlsZTovLy4vdXBsb2Fkcy81Ni9rSWZ4SUF5LzE0ODcvODM5My1wYXlwYWxfMTAyNDYxLnBuZ6xvuXsAAAAASUVORK5CYII=" + }, + { + "type": "Label", + "caption": "" + }, + { + "type": "Image", + "onClick": "echo 'https://www.amazon.de/hz/wishlist/ls/3JVWED9SZMDPK?ref_=wl_share';", + "image": "data:image/png;base64,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" + } + ] } ] } \ No newline at end of file diff --git a/Bridge/locale.json b/Bridge/locale.json index 2005ec11..776ef6d1 100644 --- a/Bridge/locale.json +++ b/Bridge/locale.json @@ -1,7 +1,25 @@ { "translations": { "de": { - "State": "Status" + "Install or upgrade Symcon-Extension": "Symcon Erweiterung installieren oder updaten", + "Symcon-Extension is up-to-date": "Symcon Erweiterung ist aktuell", + "Set last_seen setting to epoch": "Setze die last_seen Einstellung auf epoch", + "last_seen setting is correct": "last_seen Einstellung ist korrekt", + "Error": "Fehler", + "Warning": "Warnungen", + "State": "Status", + "Log Level": "Protokollierung", + "Network Map": "Netzwerkkarte", + "Allow joining the network": "Beitritt zum Netzwerk zulassen", + "Restart Required": "Neustart erforderlich", + "Perform a restart": "Neustart durchführen", + "Network Channel": "Netzwerkkanal", + "Extension Version": "Erweiterung Version", + "Extension is up to date": "Erweiterung ist aktuell", + "Wrong last_seen setting in Zigbee2MQTT. Please set last_seen to epoch.": "Falsche Einstellung für last_seen in Zigbee2MQTT. Bitte last_seen auf epoch einstellen.", + "Symcon Extension in Zigbee2MQTT is outdated. Please update the extension.": "Symcon Erweiterung in Zigbee2MQTT ist veraltet. Bitte Erweiterung updaten.", + "No Symcon Extension in Zigbee2MQTT installed. Please install the extension.": "Symcon Erweiterung in Zigebee2MQTT nicht installiert. Bitte die Erweiterung installieren.", + "Extension Loaded": "Erweiterung geladen" } } } \ No newline at end of file diff --git a/Bridge/module.php b/Bridge/module.php index e24caec4..55c7839c 100644 --- a/Bridge/module.php +++ b/Bridge/module.php @@ -1,131 +1,502 @@ '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}', + 'PacketType' => 3, + 'QualityOfService' => 0, + 'Retain' => false, + 'Topic' => '', + 'Payload' => '' + ]; public function Create() { //Never delete this line! parent::Create(); $this->ConnectParent('{C6D2AEB3-6E1F-4B2E-8E69-3A1A00246850}'); - $this->RegisterPropertyString('MQTTTopic', 'bridge'); + $this->RegisterPropertyString('MQTTBaseTopic', 'zigbee2mqtt'); + $Version = 'unknown'; + $File = file(dirname(__DIR__) . '/libs/IPSymconExtension.js'); + $Start = strpos($File[2], 'Version: '); + if ($Start) { + $Version = trim(substr($File[2], $Start + strlen('Version: '))); + } + $this->actualExtensionVersion = $Version; + $this->ExtensionFilename = ''; + $this->ConfigLastSeen = 'epoch'; + $this->TransactionData = []; } public function ApplyChanges() { + $this->TransactionData = []; //Never delete this line! parent::ApplyChanges(); $this->ConnectParent('{C6D2AEB3-6E1F-4B2E-8E69-3A1A00246850}'); - //Setze Filter für ReceiveData - $MQTTTopic = $this->ReadPropertyString('MQTTTopic'); - $this->SetReceiveDataFilter('.*' . $MQTTTopic . '.*'); + $BaseTopic = $this->ReadPropertyString('MQTTBaseTopic'); + if (empty($BaseTopic)) { + $this->SetStatus(IS_INACTIVE); + $this->SetReceiveDataFilter('NOTHING_TO_RECEIVE'); //block all + } else { + $this->SetStatus(IS_ACTIVE); + //Setze Filter für ReceiveData + $this->SetReceiveDataFilter('.*"Topic":"' . $this->ReadPropertyString('MQTTBaseTopic') . '/bridge/.*'); + } + $this->RegisterProfileIntegerEx('Z2M.bridge.restart', '', '', '', [ + [0, $this->Translate('Restart'), '', 0xFF0000], + ]); + $this->RegisterProfileStringEx('Z2M.brigde.loglevel', '', '', '', [ + ['error', $this->Translate('Error'), '', 0x00FF00], + ['warning', $this->Translate('Warning'), '', 0x00FF00], + ['info', $this->Translate('Information'), '', 0x00FF00], + ['debug', $this->Translate('Debug'), '', 0x00FF00], + ]); } public function ReceiveData($JSONString) { - $this->SendDebug('JSON', $JSONString, 0); - if (!empty($this->ReadPropertyString('MQTTTopic'))) { - $Buffer = json_decode($JSONString); - // Buffer decodieren und in eine Variable schreiben - $this->SendDebug('MQTT Topic', $Buffer->Topic, 0); - $this->SendDebug('MQTT Payload', $Buffer->Payload, 0); - if (property_exists($Buffer, 'Topic')) { - $Payload = json_decode($Buffer->Payload); - if (fnmatch('*state*', $Buffer->Topic)) { - $this->RegisterVariableBoolean('Z2M_State', $this->Translate('State'), ''); - switch ($Buffer->Payload) { - case 'online': - SetValue($this->GetIDForIdent('Z2M_State'), true); - break; - case 'offline': - SetValue($this->GetIDForIdent('Z2M_State'), false); - break; - default: - $this->SendDebug('Bridge State', 'Invalid Payload' . $Buffer->Payload, 0); - } + $BaseTopic = $this->ReadPropertyString('MQTTBaseTopic'); + if (empty($BaseTopic)) { + return ''; + } + $this->SendDebug('ReceiveData', $JSONString, 0); + + $Buffer = json_decode($JSONString, true); + if (!isset($Buffer['Topic'])) { + return ''; + } + $ReceiveTopic = $Buffer['Topic']; + $this->SendDebug('MQTT FullTopic', $ReceiveTopic, 0); + $Topic = substr($ReceiveTopic, strlen($BaseTopic . '/bridge/')); + $Topics = explode('/', $Topic); + $Topic = array_shift($Topics); + $this->SendDebug('MQTT Topic', $Topic, 0); + $this->SendDebug('MQTT Payload', $Buffer['Payload'], 0); + $Payload = json_decode($Buffer['Payload'], true); + switch ($Topic) { + case 'request': //nothing todo + break; + case 'response': //response from request + if (isset($Payload['transaction'])) { + $this->UpdateTransaction($Payload); + break; } - if (fnmatch('*config*', $Buffer->Topic)) { - if (property_exists($Payload, 'log_level')) { - $this->RegisterVariableString('Z2M_Log_Level', $this->Translate('Log Level'), ''); - SetValue($this->GetIDForIdent('Z2M_Log_Level'), $Payload->log_level); - } - if (property_exists($Payload, 'permit_join')) { - $this->RegisterVariableString('Z2M_Permit_Join', $this->Translate('Permit Join'), ''); - SetValue($this->GetIDForIdent('Z2M_Permit_Join'), $Payload->permit_join); + if (count($Topics)) { + if ($Topics[0] == 'networkmap') { + if ($Payload['status'] == 'ok') { + $this->RegisterVariableString($Payload['data']['type'], $this->Translate('Network Map')); + $this->SetValue($Payload['data']['type'], $Payload['data']['value']); + } } } - if (fnmatch('*event*', $Buffer->Topic)) { - if (property_exists($Payload, 'type')) { - switch ($Payload->type) { - case 'device_announce': - $this->RegisterVariableString('Z2M_Device_Announce', $this->Translate('Announce'), ''); - SetValue($this->GetIDForIdent('Z2M_Device_Announce'), $Buffer->Payload); - break; - } + break; + case 'state': + $this->RegisterVariableBoolean('state', $this->Translate('State')); + $this->SetValue('state', $Payload['state'] == 'online'); + break; + case 'info': + if (isset($Payload['log_level'])) { + $this->RegisterVariableString('log_level', $this->Translate('Log Level'), 'Z2M.brigde.loglevel'); + $this->EnableAction('log_level'); + $this->SetValue('log_level', $Payload['log_level']); + } + if (isset($Payload['permit_join'])) { + $this->RegisterVariableBoolean('permit_join', $this->Translate('Allow joining the network'), '~Switch'); + $this->EnableAction('permit_join'); + $this->SetValue('permit_join', $Payload['permit_join']); + } + if (isset($Payload['restart_required'])) { + $this->RegisterVariableBoolean('restart_required', $this->Translate('Restart Required')); + $this->SetValue('restart_required', $Payload['restart_required']); + $this->RegisterVariableInteger('restart_request', $this->Translate('Perform a restart'), 'Z2M.bridge.restart'); + $this->EnableAction('restart_request'); + } + if (isset($Payload['version'])) { + $this->RegisterVariableString('version', $this->Translate('Version')); + $this->SetValue('version', $Payload['version']); + } + if (isset($Payload['config']['advanced']['last_seen'])) { + $this->SendDebug('last_seen', $Payload['config']['advanced']['last_seen'], 0); + $this->ConfigLastSeen = $Payload['config']['advanced']['last_seen']; + if ($Payload['config']['advanced']['last_seen'] != 'epoch') { + $this->LogMessage($this->Translate('Wrong last_seen setting in Zigbee2MQTT. Please set last_seen to epoch.'), KL_ERROR); } } - if (fnmatch('*log*', $Buffer->Topic)) { - if (property_exists($Payload, 'type')) { - switch ($Payload->type) { - case 'pairing': - $this->RegisterVariableString('Z2M_Pairing', $this->Translate('Pairing'), ''); - SetValue($this->GetIDForIdent('Z2M_Pairing'), $Buffer->Payload); - break; - case 'device_connected': - $this->RegisterVariableString('Z2M_Device_Connected', $this->Translate('Device Connected'), ''); - SetValue($this->GetIDForIdent('Z2M_Device_Connected'), $Buffer->Payload); - break; - case 'device removed': - $this->RegisterVariableString('Z2M_Device_Removed', $this->Translate('Device Removed'), ''); - SetValue($this->GetIDForIdent('Z2M_Device_Removed'), $Buffer->Payload); - break; - case 'device_banned': - $this->RegisterVariableString('Z2M_Device_Banned', $this->Translate('Device Banned'), ''); - SetValue($this->GetIDForIdent('Z2M_Device_Banned'), $Buffer->Payload); - break; - case 'device_renamed': - $this->RegisterVariableString('Z2M_Device_Renamed', $this->Translate('Device Renamed'), ''); - SetValue($this->GetIDForIdent('Z2M_Device_Renamed'), $Payload->message->from . ' >> ' . $Payload->message->to); - break; - case 'device_bind': - $this->RegisterVariableString('Z2M_Device_bind', $this->Translate('Device Bind'), ''); - SetValue($this->GetIDForIdent('Z2M_Device_bind'), $Buffer->Payload); - break; - case 'device_unbind': - $this->RegisterVariableString('Z2M_Device_Unbind', $this->Translate('Device Unbind'), ''); - SetValue($this->GetIDForIdent('Z2M_Device_Unbind'), $Buffer->Payload); - break; - case 'device_group_add': - $this->RegisterVariableString('Z2M_Device_Group_Add', $this->Translate('Device Group Add'), ''); - SetValue($this->GetIDForIdent('Z2M_Device_Group_Add'), $Buffer->Payload); - break; - case 'device_group_remove': - $this->RegisterVariableString('Z2M_Device_Group_Remove', $this->Translate('Device Group Remove'), ''); - SetValue($this->GetIDForIdent('Z2M_Device_Group_Remove'), $Buffer->Payload); - break; - case 'device_group_remove_all': - $this->RegisterVariableString('Z2M_Device_Group_Remove_All', $this->Translate('Device Group Remove All'), ''); - SetValue($this->GetIDForIdent('Z2M_Device_Group_Remove_All'), $Buffer->Payload); - break; - case 'devices': - $this->RegisterVariableString('Z2M_Devices', $this->Translate('Devices'), ''); - SetValue($this->GetIDForIdent('Z2M_Devices'), $Buffer->Payload); - break; - case 'device_publish_error': - $this->RegisterVariableString('Z2M_Device_Publish_Error', $this->Translate('Device Publish Error'), ''); - SetValue($this->GetIDForIdent('Z2M_Device_Publish_Error'), $Payload->device_publish_error); - break; + if (isset($Payload['network'])) { + $this->RegisterVariableInteger('network_channel', $this->Translate('Network Channel')); + $this->SetValue('network_channel', $Payload['network']['channel']); + } + break; + case 'extensions': + $foundExtension = false; + foreach ($Payload as $Extension) { + if (strpos($Extension['code'], 'class IPSymconExtension')) { + $foundExtension = true; + $this->ExtensionName = $Extension['name']; + $Version = 'unknown'; + $Lines = explode("\n", $Extension['code']); + $Start = strpos($Lines[2], 'Version: '); + if ($Start) { + $Version = trim(substr($Lines[2], $Start + strlen('Version: '))); } + $this->RegisterVariableString('extension_version', $this->Translate('Extension Version')); + $this->SetValue('extension_version', $Version); + $this->RegisterVariableBoolean('extension_is_current', $this->Translate('Extension is up to date')); + $this->SetValue('extension_is_current', $this->actualExtensionVersion == $Version); + if ($this->actualExtensionVersion != $Version) { + $this->LogMessage($this->Translate('Symcon Extension in Zigbee2MQTT is outdated. Please update the extension.'), KL_ERROR); + } + break; } } + $this->RegisterVariableBoolean('extension_loaded', $this->Translate('Extension Loaded')); + $this->SetValue('extension_loaded', $foundExtension); + if (!$foundExtension) { + $this->LogMessage($this->Translate('No Symcon Extension in Zigbee2MQTT installed. Please install the extension.'), KL_ERROR); + } + break; + } + return ''; + } + + public function RequestAction($Ident, $Value) + { + switch ($Ident) { + case 'permit_join': + $this->SetPermitJoin((bool) $Value); + break; + case 'log_level': + $this->SetLogLevel((string) $Value); + break; + case'restart_request': + $this->Restart(); + break; + } + } + + public function GetConfigurationForm() + { + $Form = json_decode(file_get_contents(__DIR__ . '/form.json'), true); + if ($this->GetValue('extension_loaded') && $this->GetValue('extension_is_current')) { + $Form['actions'][0]['enabled'] = false; + $Form['actions'][0]['label'] = $this->Translate('Symcon-Extension is up-to-date'); + } + if ($this->ConfigLastSeen == 'epoch') { + $Form['actions'][1]['enabled'] = false; + $Form['actions'][1]['label'] = $this->Translate('last_seen setting is correct'); + } + return json_encode($Form); + } + + public function InstallSymconExtension() + { + if (empty($this->ExtensionName)) { + $ExtensionName = 'IPSymconExtension.js'; + } + $Topic = '/bridge/request/extension/save'; + $Payload = ['name'=>$ExtensionName, 'code'=>file_get_contents(dirname(__DIR__) . '/libs/IPSymconExtension.js')]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function SetLastSeen() + { + $Topic = '/bridge/request/options'; + $Payload = [ + 'options'=> [ + 'advanced'=> [ + 'last_seen'=> 'epoch' + ] + ] + ]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function SetPermitJoin(bool $PermitJoin) + { + $Topic = '/bridge/request/permit_join'; + $Payload = ['value'=>$PermitJoin]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function SetLogLevel(string $LogLevel) + { + $Topic = '/bridge/request/options'; + $Payload = ['options' =>['advanced' => ['log_level'=> $LogLevel]]]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function Restart() + { + $Topic = '/bridge/request/restart'; + $Result = $this->SendData($Topic); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function CreateGroup(string $GroupName) + { + $Topic = '/bridge/request/group/add'; + $Payload = ['friendly_name' => $GroupName]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function DeleteGroup(string $GroupName) + { + $Topic = '/bridge/request/group/remove'; + $Payload = ['id' => $GroupName]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function RenameGroup(string $OldName, string $NewName) + { + $Topic = '/bridge/request/group/rename'; + $Payload = ['from' => $OldName, 'to' => $NewName]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function AddDeviceToGroup(string $GroupName, string $DeviceName) + { + $Topic = '/bridge/request/group/members/add'; + $Payload = ['group'=>$GroupName, 'device' => $DeviceName]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function RemoveDeviceFromGroup(string $GroupName, string $DeviceName) + { + $Topic = '/bridge/request/group/members/remove'; + $Payload = ['group'=>$GroupName, 'device' => $DeviceName, 'skip_disable_reporting'=>true]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function RemoveAllDevicesFromGroup(string $GroupName) + { + $Topic = '/bridge/request/group/members/remove_all'; + $Payload = ['group'=>$GroupName]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function Bind(string $SourceDevice, string $TargetDevice) + { + $Topic = '/bridge/request/device/bind'; + $Payload = ['from' => $SourceDevice, 'to' => $TargetDevice]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function Unbind(string $SourceDevice, string $TargetDevice) + { + $Topic = '/bridge/request/device/unbind'; + $Payload = ['from' => $SourceDevice, 'to' => $TargetDevice]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function RequestNetworkmap() + { + $Topic = '/bridge/request/networkmap'; + $Payload = ['type' => 'graphviz', 'routes' => true]; + return $this->SendData($Topic, $Payload, 0); + } + + public function RenameDevice(string $OldDeviceName, string $NewDeviceName) + { + $Topic = '/bridge/request/device/rename'; + $Payload = ['from' => $OldDeviceName, 'to' => $NewDeviceName]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function RemoveDevice(string $DeviceName) + { + $Topic = '/bridge/request/device/remove'; + $Payload = ['id'=>$DeviceName]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function CheckOTAUpdate(string $DeviceName) + { + $Topic = '/bridge/request/device/ota_update/check'; + $Payload = ['id'=>$DeviceName]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + + public function PerformOTAUpdate(string $DeviceName) + { + $Topic = '/bridge/request/device/ota_update/update'; + $Payload = ['id'=>$DeviceName]; + $Result = $this->SendData($Topic, $Payload); + if ($Result) { //todo check the Response + return true; + } + return false; + } + private function SendData(string $Topic, array $Payload = [], int $Timeout = 5000) + { + if ($Timeout) { + $TransactionId = $this->AddTransaction($Payload); + } + $this->SendDebug(__FUNCTION__ . ':Topic', $Topic, 0); + $this->SendDebug(__FUNCTION__ . ':Payload', json_encode($Payload), 0); + $DataJSON = self::BuildRequest($this->ReadPropertyString('MQTTBaseTopic') . $Topic, $Payload); + $this->SendDataToParent($DataJSON); + if ($Timeout) { + $Result = $this->WaitForTransactionEnd($TransactionId, $Timeout); + if ($Result === false) { + trigger_error($this->Translate('Zigbee2MQTT did not response.'), E_USER_NOTICE); + return false; } + return $Result; + } + return true; + } + + private function WaitForTransactionEnd(int $TransactionId, int $Timeout) + { + $Sleep = intdiv($Timeout, 1000); + for ($i = 0; $i < 1000; $i++) { + $Buffer = $this->TransactionData; + if (!array_key_exists($TransactionId, $Buffer)) { + return false; + } + if (count($Buffer[$TransactionId])) { + $this->RemoveTransaction($TransactionId); + return $Buffer[$TransactionId]; + } + IPS_Sleep($Sleep); + } + $this->RemoveTransaction($TransactionId); + return false; + } + //################# SENDQUEUE + + private function AddTransaction(array &$Payload) + { + if (!$this->lock('TransactionData')) { + throw new Exception($this->Translate('TransactionData is locked'), E_USER_NOTICE); + } + $TransactionId = mt_rand(1, 10000); + $Payload['transaction'] = $TransactionId; + $TransactionData = $this->TransactionData; + $TransactionData[$TransactionId] = []; + $this->TransactionData = $TransactionData; + $this->unlock('TransactionData'); + return $TransactionId; + } + + private function UpdateTransaction(array $Data) + { + if (!$this->lock('TransactionData')) { + throw new Exception($this->Translate('TransactionData is locked'), E_USER_NOTICE); + } + $TransactionData = $this->TransactionData; + if (array_key_exists($Data['transaction'], $TransactionData)) { + $TransactionData[$Data['transaction']] = $Data; + $this->TransactionData = $TransactionData; + $this->unlock('TransactionData'); + return; } + $this->unlock('TransactionData'); + return; + } + + private function RemoveTransaction(int $TransactionId) + { + if (!$this->lock('TransactionData')) { + throw new Exception($this->Translate('TransactionData is locked'), E_USER_NOTICE); + } + $TransactionData = $this->TransactionData; + unset($TransactionData[$TransactionId]); + $this->TransactionData = $TransactionData; + $this->unlock('TransactionData'); + } + + private static function BuildRequest(string $Topic, array $Payload) + { + return json_encode( + array_merge( + self::$MQTTDataArray, + [ + 'Topic' => $Topic, + 'Payload'=> json_encode($Payload) + ] + ), + JSON_UNESCAPED_SLASHES + ); } } diff --git a/libs/BufferHelper.php b/libs/BufferHelper.php new file mode 100644 index 00000000..f7fc049e --- /dev/null +++ b/libs/BufferHelper.php @@ -0,0 +1,72 @@ + + * @copyright 2018 Michael Tröger + * @license https://creativecommons.org/licenses/by-nc-sa/4.0/ CC BY-NC-SA 4.0 + * @version 5.0 + */ + +/** + * Trait welcher Objekt-Eigenschaften in den Instance-Buffer schreiben und lesen kann. + */ +trait BufferHelper +{ + /** + * Wert einer Eigenschaft aus den InstanceBuffer lesen. + * + * @access public + * @param string $name PropertyName + * @return mixed Value of Name + */ + public function __get($name) + { + if (strpos($name, 'Multi_') === 0) { + $Lines = ''; + foreach ($this->{'BufferListe_' . $name} as $BufferIndex) { + $Lines .= $this->{'Part_' . $name . $BufferIndex}; + } + return unserialize($Lines); + } + return unserialize($this->GetBuffer($name)); + } + + /** + * Wert einer Eigenschaft in den InstanceBuffer schreiben. + * + * @access public + * @param string $name PropertyName + * @param mixed Value of Name + */ + public function __set($name, $value) + { + $Data = serialize($value); + if (strpos($name, 'Multi_') === 0) { + $OldBuffers = $this->{'BufferListe_' . $name}; + if ($OldBuffers == false) { + $OldBuffers = []; + } + $Lines = str_split($Data, 8000); + foreach ($Lines as $BufferIndex => $BufferLine) { + $this->{'Part_' . $name . $BufferIndex} = $BufferLine; + } + $NewBuffers = array_keys($Lines); + $this->{'BufferListe_' . $name} = $NewBuffers; + $DelBuffers = array_diff_key($OldBuffers, $NewBuffers); + foreach ($DelBuffers as $DelBuffer) { + $this->{'Part_' . $name . $DelBuffer} = ''; + } + return; + } + $this->SetBuffer($name, $Data); + } +} diff --git a/libs/IPSymconExtension.js b/libs/IPSymconExtension.js index 5854733e..c4954f32 100644 --- a/libs/IPSymconExtension.js +++ b/libs/IPSymconExtension.js @@ -1,5 +1,9 @@ -const ZigbeeHerdsmanConverters = require('zigbee-herdsman-converters'); +/* + IPSymconExtension + Version: 4.4.0 +*/ +const ZigbeeHerdsmanConverters = require('zigbee-herdsman-converters'); class IPSymconExtension { constructor(zigbee, mqtt, state, publishEntityState, eventBus, settings, logger) { this.zigbee = zigbee; @@ -64,9 +68,9 @@ class IPSymconExtension { #createDevicePayload(device, boolExposes) { const definition = this.zigbeeHerdsmanConverters.findByDevice(device.zh); let exposes; - if (boolExposes) { - exposes = device.exposes(); - } + if (boolExposes) { + exposes = device.exposes(); + } return { ieeeAddr: device.ieeeAddr, diff --git a/libs/SemaphoreHelper.php b/libs/SemaphoreHelper.php new file mode 100644 index 00000000..e68b5bf2 --- /dev/null +++ b/libs/SemaphoreHelper.php @@ -0,0 +1,51 @@ + + * @copyright 2018 Michael Tröger + * @license https://creativecommons.org/licenses/by-nc-sa/4.0/ CC BY-NC-SA 4.0 + * @version 5.0 + */ + +/** + * Biete Funktionen um auf Objekte Thread-Safe zuzugreifen. + */ +trait Semaphore +{ + /** + * Versucht eine Semaphore zu setzen und wiederholt dies bei Misserfolg bis zu 100 mal. + * @param string $ident Ein String der den Lock bezeichnet. + * @return boolean TRUE bei Erfolg, FALSE bei Misserfolg. + */ + private function lock($ident) + { + for ($i = 0; $i < 100; $i++) { + if (IPS_SemaphoreEnter(__CLASS__ . '.' . (string) $this->InstanceID . (string) $ident, 1)) { + return true; + } else { + IPS_Sleep(mt_rand(1, 5)); + } + } + return false; + } + + /** + * Löscht eine Semaphore. + * @param string $ident Ein String der den Lock bezeichnet. + */ + private function unlock($ident) + { + IPS_SemaphoreLeave(__CLASS__ . '.' . (string) $this->InstanceID . (string) $ident); + } +} + +/* @} */ diff --git a/libs/Zigbee2MQTTBridgeHelper.php b/libs/Zigbee2MQTTBridgeHelper.php deleted file mode 100644 index 75dd0ccd..00000000 --- a/libs/Zigbee2MQTTBridgeHelper.php +++ /dev/null @@ -1,152 +0,0 @@ -ReadPropertyString('MQTTTopic') . '/group/' . $group_name . '/add'; - $Data['Payload'] = $friendly_name; - $DataJSON = json_encode($Data, JSON_UNESCAPED_SLASHES); - $this->SendDebug(__FUNCTION__ . 'Add Group Topic', $Data['Topic'], 0); - $this->SendDebug(__FUNCTION__, $DataJSON, 0); - $this->SendDataToParent($DataJSON); - } - - public function RemoveGroup(string $group_name, string $friendly_name) - { - $Data['DataID'] = '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}'; - $Data['PacketType'] = 3; - $Data['QualityOfService'] = 0; - $Data['Retain'] = false; - $Data['Topic'] = MQTT_GROUP_TOPIC . '/' . $this->ReadPropertyString('MQTTTopic') . '/group/' . $group_name . '/remove'; - $Data['Payload'] = $friendly_name; - $DataJSON = json_encode($Data, JSON_UNESCAPED_SLASHES); - $this->SendDebug(__FUNCTION__ . 'Remove Group Topic', $Data['Topic'], 0); - $this->SendDebug(__FUNCTION__, $DataJSON, 0); - $this->SendDataToParent($DataJSON); - } - - public function RemoveAllGroup(string $group_name, string $friendly_name) - { - $Data['DataID'] = '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}'; - $Data['PacketType'] = 3; - $Data['QualityOfService'] = 0; - $Data['Retain'] = false; - $Data['Topic'] = MQTT_GROUP_TOPIC . '/' . $this->ReadPropertyString('MQTTTopic') . '/group/' . $group_name . '/remove_all'; - $Data['Payload'] = $friendly_name; - $DataJSON = json_encode($Data, JSON_UNESCAPED_SLASHES); - $this->SendDebug(__FUNCTION__ . 'Publish Topic', $Data['Topic'], 0); - $this->SendDebug(__FUNCTION__, $DataJSON, 0); - $this->SendDataToParent($DataJSON); - } - - public function Bind(string $source_device, string $target_device) - { - $Data['DataID'] = '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}'; - $Data['PacketType'] = 3; - $Data['QualityOfService'] = 0; - $Data['Retain'] = false; - $Data['Topic'] = MQTT_GROUP_TOPIC . '/' . $this->ReadPropertyString('MQTTTopic') . '/bind/' . $source_device; - $Data['Payload'] = $target_device; - $DataJSON = json_encode($Data, JSON_UNESCAPED_SLASHES); - $this->SendDebug(__FUNCTION__ . 'Bind Topic', $Data['Topic'], 0); - $this->SendDebug(__FUNCTION__, $DataJSON, 0); - $this->SendDataToParent($DataJSON); - } - - public function Unbind(string $source_device, string $target_device) - { - $Data['DataID'] = '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}'; - $Data['PacketType'] = 3; - $Data['QualityOfService'] = 0; - $Data['Retain'] = false; - $Data['Topic'] = MQTT_GROUP_TOPIC . '/' . $this->ReadPropertyString('MQTTTopic') . '/unbind/' . $source_device; - $Data['Payload'] = $target_device; - $DataJSON = json_encode($Data, JSON_UNESCAPED_SLASHES); - $this->SendDebug(__FUNCTION__ . 'Unbind Topic', $Data['Topic'], 0); - $this->SendDebug(__FUNCTION__, $DataJSON, 0); - $this->SendDataToParent($DataJSON); - } - - public function getGroupMembership(string $friendly_name) - { - $Data['DataID'] = '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}'; - $Data['PacketType'] = 3; - $Data['QualityOfService'] = 0; - $Data['Retain'] = false; - $Data['Topic'] = MQTT_GROUP_TOPIC . '/' . $this->ReadPropertyString('MQTTTopic') . '/device/' . $friendly_name . '/get_group_membership'; - $Data['Payload'] = ''; - $DataJSON = json_encode($Data, JSON_UNESCAPED_SLASHES); - $this->SendDebug(__FUNCTION__ . 'getGroupMembership Topic', $Data['Topic'], 0); - $this->SendDebug(__FUNCTION__, $DataJSON, 0); - $this->SendDataToParent($DataJSON); - } - - public function Networkmap() - { - $Data['DataID'] = '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}'; - $Data['PacketType'] = 3; - $Data['QualityOfService'] = 0; - $Data['Retain'] = false; - $Data['Topic'] = MQTT_GROUP_TOPIC . '/' . $this->ReadPropertyString('MQTTTopic') . '/networkmap'; - $Data['Payload'] = 'graphviz'; - $DataJSON = json_encode($Data, JSON_UNESCAPED_SLASHES); - $this->SendDebug(__FUNCTION__ . 'Bind Topic', $Data['Topic'], 0); - $this->SendDebug(__FUNCTION__, $DataJSON, 0); - $this->SendDataToParent($DataJSON); - } - - public function RenameDevice(string $old_friendly_name, string $new_friendly_name) - { - $Data['DataID'] = '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}'; - $Data['PacketType'] = 3; - $Data['QualityOfService'] = 0; - $Data['Retain'] = false; - $Data['Topic'] = MQTT_GROUP_TOPIC . '/' . $this->ReadPropertyString('MQTTTopic') . '/config/rename'; - - $Payload['old'] = $old_friendly_name; - $Payload['new'] = $new_friendly_name; - $Data['Payload'] = json_encode($Payload); - - $DataJSON = json_encode($Data, JSON_UNESCAPED_SLASHES); - $this->SendDebug(__FUNCTION__ . 'RenameDevice Topic', $Data['Topic'], 0); - $this->SendDebug(__FUNCTION__, $DataJSON, 0); - $this->SendDataToParent($DataJSON); - } - - public function BanDevice(string $friendly_name) - { - $Data['DataID'] = '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}'; - $Data['PacketType'] = 3; - $Data['QualityOfService'] = 0; - $Data['Retain'] = false; - $Data['Topic'] = MQTT_GROUP_TOPIC . '/' . $this->ReadPropertyString('MQTTTopic') . '/config/ban'; - $Data['Payload'] = $friendly_name; - $DataJSON = json_encode($Data, JSON_UNESCAPED_SLASHES); - $this->SendDebug(__FUNCTION__ . 'Bind Topic', $Data['Topic'], 0); - $this->SendDebug(__FUNCTION__, $DataJSON, 0); - $this->SendDataToParent($DataJSON); - } - - public function RemoveDevice(string $friendly_name) - { - $Data['DataID'] = '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}'; - $Data['PacketType'] = 3; - $Data['QualityOfService'] = 0; - $Data['Retain'] = false; - $Data['Topic'] = MQTT_GROUP_TOPIC . '/' . $this->ReadPropertyString('MQTTTopic') . '/config/remove'; - $Data['Payload'] = $friendly_name; - $DataJSON = json_encode($Data, JSON_UNESCAPED_SLASHES); - $this->SendDebug(__FUNCTION__ . 'Bind Topic', $Data['Topic'], 0); - $this->SendDebug(__FUNCTION__, $DataJSON, 0); - $this->SendDataToParent($DataJSON); - } -}