Improve category checks in MonitoringServer for robustness and clarity

This commit is contained in:
jhartworks
2026-09-09 11:35:48 +02:00
parent 8719dec2de
commit 2d337c6308
2 changed files with 224 additions and 265 deletions
+198 -249
View File
@@ -67,60 +67,70 @@ class MonitoringClient extends IPSModule {
} }
$module_id = "{01C00ADD-D04E-452E-B66A-D253278743FE}" /* Module ID of MQTT Server Device */; $module_id = "{01C00ADD-D04E-452E-B66A-D253278743FE}" /* Module ID of MQTT Server Device */;
$ident = "TempMQTTDevice"; $ident = "TempMQTTDevice";
// Each client instance owns its temporary device. Do not let different
// enter semaphore to ensure the temporary device gets used by one thread at a time // Monitoring Client instances block each other during a force update.
if(IPS_SemaphoreEnter($ident, 100)) { $semaphore = $ident . "_" . $this->InstanceID;
// get temporary MQTT Server Device or create if needed
$id = @IPS_GetObjectIDByIdent($ident, $_IPS['SELF']); // enter semaphore to ensure the temporary device gets used by one thread at a time
if($id === false) { if(IPS_SemaphoreEnter($semaphore, 5000)) {
$id = @IPS_CreateInstance($module_id); try {
if($id === false) { // get temporary MQTT Server Device or create if needed
return false; $id = @IPS_GetObjectIDByIdent($ident, $this->InstanceID);
} if($id === false) {
IPS_SetParent($id, $_IPS['SELF']); $id = @IPS_CreateInstance($module_id);
IPS_SetIdent($id, $ident); if($id === false) {
} return false;
}
// ensure the specified server instance is actually compatible IPS_SetParent($id, $this->InstanceID);
if(!IPS_IsInstanceCompatible($id, $server_id)) { IPS_SetIdent($id, $ident);
return false; }
}
// ensure the specified server instance is actually compatible
// ensure that the temporary device is actually connected to the correct server instance if(!IPS_IsInstanceCompatible($id, $server_id)) {
$inst_config = IPS_GetInstance($id); return false;
if($inst_config["ConnectionID"] != $server_id) { }
IPS_DisconnectInstance($id);
if(!@IPS_ConnectInstance($id, $server_id)) { // ensure that the temporary device is actually connected to the correct server instance
return false; $inst_config = IPS_GetInstance($id);
} if($inst_config["ConnectionID"] != $server_id) {
} IPS_DisconnectInstance($id);
if(!@IPS_ConnectInstance($id, $server_id)) {
// name object to help with debugging return false;
IPS_SetName($id, "Temporary MQTT Device for: " . $topic); }
}
// configure temporary device
$config_arr = array( // name object to help with debugging
"Retain" => $retain, IPS_SetName($id, "Temporary MQTT Device for: " . $topic);
"Topic" => $topic,
"Type" => $ips_var_type // configure temporary device
); $config_arr = array(
$config_str = json_encode($config_arr); "Retain" => $retain,
IPS_SetConfiguration($id, $config_str); "Topic" => $topic,
IPS_SetHidden($id,true); "Type" => $ips_var_type
IPS_ApplyChanges($id); );
$config_str = json_encode($config_arr);
// get Value variable and use it to publish the payload IPS_SetConfiguration($id, $config_str);
$var_id = @IPS_GetObjectIDByIdent("Value", $id); IPS_SetHidden($id,true);
RequestAction($var_id, $payload); IPS_ApplyChanges($id);
IPS_SemaphoreLeave($ident); // get Value variable and use it to publish the payload
} else { // semaphore timeout $var_id = @IPS_GetObjectIDByIdent("Value", $id);
return false; if ($var_id === false) {
} IPS_LogMessage("Monitoring Client", "MQTT device has no Value variable for topic: " . $topic);
return false;
return true; }
} // MQTT_Publish RequestAction($var_id, $payload);
return true;
} finally {
IPS_SemaphoreLeave($semaphore);
}
} else { // semaphore timeout
IPS_LogMessage("Monitoring Client", "MQTT publish timed out waiting for topic: " . $topic);
return false;
}
} // MQTT_Publish
public function MqttSync($server_id, $topic, $payload, $retain, $ident, $source) { public function MqttSync($server_id, $topic, $payload, $retain, $ident, $source) {
IPS_LogMessage("Monitoring Client", "MQTT Sync called with Topic: " . $topic . " Payload: " . $payload. " Source: " . $source . " Ident: " . $ident); IPS_LogMessage("Monitoring Client", "MQTT Sync called with Topic: " . $topic . " Payload: " . $payload. " Source: " . $source . " Ident: " . $ident);
@@ -240,201 +250,140 @@ class MonitoringClient extends IPSModule {
return true; return true;
} // MQTT_Sync } // MQTT_Sync
public function SendTopic() { private function resolveLinkTarget($objectId) {
$visited = array();
$mqttId = $this->ReadPropertyInteger("MqttCLientID"); while ($objectId > 0 && IPS_LinkExists($objectId)) {
$projectyear = $this->ReadPropertyInteger("Projectyear"); if (isset($visited[$objectId])) {
$projectnumber = $this->ReadPropertyInteger("Projectnumber"); return 0;
$projectname = $this->ReadPropertyString("Projectname"); }
$ispnumber = $this->ReadPropertyInteger("ISP");
$updatetime = $this->ReadPropertyInteger("Updatetime"); $visited[$objectId] = true;
$objectId = IPS_GetLink($objectId)["TargetID"];
$catIds[0]["id"] = $this->ReadPropertyInteger("ParseNotifyCategoryID"); }
$catIds[0]["top"] = "Projekte". $projectyear."/P".$projectnumber. "/ISP" .$ispnumber. "/Notify/";
$catIds[0]["setpoint"] = false; return $objectId;
$catIds[1]["id"] = $this->ReadPropertyInteger("ParseAlarmCategoryID"); }
$catIds[1]["top"] = "Projekte". $projectyear."/P".$projectnumber. "/ISP" .$ispnumber. "/Alarm/";
$catIds[1]["setpoint"] = false; /*
$catIds[2]["id"] = $this->ReadPropertyInteger("ParseAnalogCategoryID"); * Return every variable directly represented by a monitoring category.
$catIds[2]["top"] = "Projekte". $projectyear."/P".$projectnumber. "/ISP" .$ispnumber. "/Analog/"; * The parent name is deliberately kept from the visible object/link in the
$catIds[2]["setpoint"] = false; * configured tree, so existing MQTT topic names remain unchanged.
$catIds[3]["id"] = $this->ReadPropertyInteger("ParseSetpointCategoryID"); */
$catIds[3]["top"] = "Projekte". $projectyear."/P".$projectnumber. "/ISP" .$ispnumber. "/Setpoint/"; private function getCategoryVariables($categoryId) {
$catIds[3]["setpoint"] = true; $variables = array();
if (!IPS_CategoryExists($categoryId)) {
return $variables;
foreach ($catIds as $catId){ }
$isSetpoint = isset($catId["setpoint"]) ? $catId["setpoint"] : false; foreach (IPS_GetChildrenIDs($categoryId) as $entryId) {
$entryName = IPS_GetName($entryId);
$catChilds = IPS_GetChildrenIDs($catId["id"]); $targetId = $this->resolveLinkTarget($entryId);
if ($targetId <= 0 || !IPS_ObjectExists($targetId)) {
foreach ($catChilds as $catChild) { continue;
}
$childids = IPS_GetChildrenIDs($catChild);
// A variable or a link to a variable is located directly in the category.
$parname = IPS_GetName($catChild); if (IPS_VariableExists($targetId)) {
$variables[] = array(
if (IPS_VariableExists($catChild) != 1 && IPS_LinkExists($catChild) != 1){ "id" => $targetId,
"parent" => $entryName,
foreach ($childids as $childid){ "name" => IPS_GetName($targetId),
"direct" => true
foreach ($childids as $childid){ );
if (IPS_VariableExists($childid) != 1){ continue;
}
if(IPS_LinkExists($childid) == 1){
$linkInfo = IPS_GetLink($childid); // An object (or a link to an object) may contain one or several values.
$linkTarget = $linkInfo["TargetID"]; foreach (IPS_GetChildrenIDs($targetId) as $childId) {
$childid = $linkTarget; $variableId = $this->resolveLinkTarget($childId);
$varInfo = IPS_GetVariable($childid); if ($variableId <= 0 || !IPS_VariableExists($variableId)) {
} continue;
} }
$varInfo = IPS_GetVariable($childid); $variables[] = array(
$changedtime = $varInfo["VariableChanged"]; "id" => $variableId,
$varname = IPS_GetName($childid); "parent" => $entryName,
$topic = $catId["top"]. $parname."_".$varname; "name" => IPS_GetName($variableId),
$syncname = $parname."_".$varname; "direct" => false
$payload = round(getvalue($childid), 2); );
$time = time(); }
}
$isSetpoint = isset($catId["setpoint"]) ? $catId["setpoint"] : false;
return $variables;
if ($isSetpoint == true){ }
IPS_LogMessage("Monitoring Client", "Sync Setpoint: " . $topic . " mit Payload: " . $payload. " und Source: " . $childid . " und Ident: " . $syncname);
$this->MqttSync($mqttId, $topic, $payload, false, $syncname, $childid); private function getTopicForVariable($top, $variable, $force) {
// The old force path used the bare variable name for values directly in
}else{ // the category. Keep that legacy topic spelling intact.
if($changedtime > $time - $updatetime){ if ($force && $variable["direct"]) {
$this->MqttPublish($mqttId, $topic, $payload, false); return $top . $variable["name"];
} }
}
return $top . $variable["parent"] . "_" . $variable["name"];
} }
} public function SendTopic() {
}else{ $mqttId = $this->ReadPropertyInteger("MqttCLientID");
$childid = $catChild; $projectyear = $this->ReadPropertyInteger("Projectyear");
if (IPS_VariableExists($childid) != 1){ $projectnumber = $this->ReadPropertyInteger("Projectnumber");
$projectname = $this->ReadPropertyString("Projectname");
if(IPS_LinkExists($childid) == 1){ $ispnumber = $this->ReadPropertyInteger("ISP");
$linkInfo = IPS_GetLink($childid); $updatetime = $this->ReadPropertyInteger("Updatetime");
$linkTarget = $linkInfo["TargetID"];
$childid = $linkTarget; $catIds = array(
$varInfo = IPS_GetVariable($childid); array("id" => $this->ReadPropertyInteger("ParseNotifyCategoryID"), "top" => "Projekte" . $projectyear . "/P" . $projectnumber . "/ISP" . $ispnumber . "/Notify/", "setpoint" => false),
} array("id" => $this->ReadPropertyInteger("ParseAlarmCategoryID"), "top" => "Projekte" . $projectyear . "/P" . $projectnumber . "/ISP" . $ispnumber . "/Alarm/", "setpoint" => false),
} array("id" => $this->ReadPropertyInteger("ParseAnalogCategoryID"), "top" => "Projekte" . $projectyear . "/P" . $projectnumber . "/ISP" . $ispnumber . "/Analog/", "setpoint" => false),
array("id" => $this->ReadPropertyInteger("ParseSetpointCategoryID"), "top" => "Projekte" . $projectyear . "/P" . $projectnumber . "/ISP" . $ispnumber . "/Setpoint/", "setpoint" => true)
$varInfo = IPS_GetVariable($childid); );
$changedtime = $varInfo["VariableChanged"];
$varname = IPS_GetName($childid); foreach ($catIds as $catId) {
$topic = $catId["top"]. $parname."_".$varname; foreach ($this->getCategoryVariables($catId["id"]) as $variable) {
$payload = round(getvalue($childid), 2); $varInfo = IPS_GetVariable($variable["id"]);
$time = time(); $changedtime = $varInfo["VariableChanged"];
$topic = $this->getTopicForVariable($catId["top"], $variable, false);
$isSetpoint = isset($catId["setpoint"]) ? $catId["setpoint"] : false; $payload = round(GetValue($variable["id"]), 2);
if ($isSetpoint == true){ if ($catId["setpoint"]) {
IPS_LogMessage("Monitoring Client", "Sync Setpoint: " . $topic . " mit Payload: " . $payload. " und Source: " . $childid . " und Ident: " . $syncname); $syncname = $variable["parent"] . "_" . $variable["name"];
$this->MqttSync($mqttId, $topic, $payload, false, $parname."_".$varname, $childid); IPS_LogMessage("Monitoring Client", "Sync Setpoint: " . $topic . " mit Payload: " . $payload . " und Source: " . $variable["id"] . " und Ident: " . $syncname);
$this->MqttSync($mqttId, $topic, $payload, false, $syncname, $variable["id"]);
}else{ } elseif ($changedtime > time() - $updatetime) {
if($changedtime > $time - $updatetime){ $this->MqttPublish($mqttId, $topic, $payload, false);
$this->MqttPublish($mqttId, $topic, $payload, false); }
} }
} }
}
} $topic = "Projekte" . $projectyear . "/" . $projectnumber . "/ISP" . $ispnumber . "/Name";
$this->MqttPublish($mqttId, $topic, $projectname, false);
} }
$topic = "Projekte". $projectyear."/".$projectnumber. "/ISP" .$ispnumber. "/Name";
$this->MqttPublish($mqttId, $topic, $projectname, false); public function ForceSendTopic() {
$mqttId = $this->ReadPropertyInteger("MqttCLientID");
} $projectyear = $this->ReadPropertyInteger("Projectyear");
public function ForceSendTopic() { $projectnumber = $this->ReadPropertyInteger("Projectnumber");
$projectname = $this->ReadPropertyString("Projectname");
$mqttId = $this->ReadPropertyInteger("MqttCLientID"); $ispnumber = $this->ReadPropertyInteger("ISP");
$projectyear = $this->ReadPropertyInteger("Projectyear");
$projectnumber = $this->ReadPropertyInteger("Projectnumber"); $catIds = array(
$projectname = $this->ReadPropertyString("Projectname"); array("id" => $this->ReadPropertyInteger("ParseNotifyCategoryID"), "top" => "Projekte" . $projectyear . "/P" . $projectnumber . "/ISP" . $ispnumber . "/Notify/"),
$ispnumber = $this->ReadPropertyInteger("ISP"); array("id" => $this->ReadPropertyInteger("ParseAlarmCategoryID"), "top" => "Projekte" . $projectyear . "/P" . $projectnumber . "/ISP" . $ispnumber . "/Alarm/"),
$updatetime = $this->ReadPropertyInteger("Updatetime"); array("id" => $this->ReadPropertyInteger("ParseAnalogCategoryID"), "top" => "Projekte" . $projectyear . "/P" . $projectnumber . "/ISP" . $ispnumber . "/Analog/"),
array("id" => $this->ReadPropertyInteger("ParseSetpointCategoryID"), "top" => "Projekte" . $projectyear . "/P" . $projectnumber . "/ISP" . $ispnumber . "/Setpoint/")
$catIds[0]["id"] = $this->ReadPropertyInteger("ParseNotifyCategoryID"); );
$catIds[0]["top"] = "Projekte". $projectyear."/P".$projectnumber. "/ISP" .$ispnumber. "/Notify/";
$catIds[1]["id"] = $this->ReadPropertyInteger("ParseAlarmCategoryID"); foreach ($catIds as $catId) {
$catIds[1]["top"] = "Projekte". $projectyear."/P".$projectnumber. "/ISP" .$ispnumber. "/Alarm/"; foreach ($this->getCategoryVariables($catId["id"]) as $variable) {
$catIds[2]["id"] = $this->ReadPropertyInteger("ParseAnalogCategoryID"); $topic = $this->getTopicForVariable($catId["top"], $variable, true);
$catIds[2]["top"] = "Projekte". $projectyear."/P".$projectnumber. "/ISP" .$ispnumber. "/Analog/"; $payload = round(GetValue($variable["id"]), 2);
$this->MqttPublish($mqttId, $topic, $payload, false);
}
}
foreach ($catIds as $catId){
$topic = "Projekte" . $projectyear . "/" . $projectnumber . "/ISP" . $ispnumber . "/Name";
$catChilds = IPS_GetChildrenIDs($catId["id"]); $this->MqttPublish($mqttId, $topic, $projectname, false);
}
foreach ($catChilds as $catChild) {
$childids = IPS_GetChildrenIDs($catChild);
$parname = IPS_GetName($catChild);
if (IPS_VariableExists($catChild) != 1 && IPS_LinkExists($catChild) != 1){
foreach ($childids as $childid){
foreach ($childids as $childid){
if (IPS_VariableExists($childid) != 1){
if(IPS_LinkExists($childid) == 1){
$linkInfo = IPS_GetLink($childid);
$linkTarget = $linkInfo["TargetID"];
$childid = $linkTarget;
$varInfo = IPS_GetVariable($childid);
}
}
$varInfo = IPS_GetVariable($childid);
$varname = IPS_GetName($childid);
$topic = $catId["top"]. $parname."_".$varname;
$payload = round(getvalue($childid), 2);
$this->MqttPublish($mqttId, $topic, $payload, false);
// IPS_LogMessage("Monitoring Client", "Force Update with Parent: " . $topic . " mit Payload: " . $payload);
}
}
}else{
$childid = $catChild;
if (IPS_VariableExists($childid) != 1){
if(IPS_LinkExists($childid) == 1){
$linkInfo = IPS_GetLink($childid);
$linkTarget = $linkInfo["TargetID"];
$childid = $linkTarget;
$varInfo = IPS_GetVariable($childid);
}
}
$varInfo = IPS_GetVariable($childid);
$varname = IPS_GetName($childid);
$topic = $catId["top"].$varname;
$payload = round(getvalue($childid), 2);
$this->MqttPublish($mqttId, $topic, $payload, false);
// IPS_LogMessage("Monitoring Client", "Force Update none Parent: " . $topic . " mit Payload: " . $payload);
}
}
}
$topic = "Projekte". $projectyear."/".$projectnumber. "/ISP" .$ispnumber. "/Name";
$this->MqttPublish($mqttId, $topic, $projectname, false);
}
} }
?> ?>
+26 -16
View File
@@ -182,9 +182,8 @@ class MonitoringServer extends IPSModule {
$inalarmcount = 0; $inalarmcount = 0;
$alarmvalues = 0; $alarmvalues = 0;
$catChilds = IPS_GetChildrenIDs($catAlarmId); if (IPS_CategoryExists($catAlarmId)){
$catChilds = IPS_GetChildrenIDs($catAlarmId);
if ($catAlarmId > 0){
foreach ($catChilds as $catChild) { foreach ($catChilds as $catChild) {
@@ -192,6 +191,9 @@ class MonitoringServer extends IPSModule {
$parname = IPS_GetName($catChild); $parname = IPS_GetName($catChild);
foreach ($childids as $childid){ foreach ($childids as $childid){
if (!IPS_VariableExists($childid)) {
continue;
}
$varInfo = IPS_GetVariable($childid); $varInfo = IPS_GetVariable($childid);
$changedtime = $varInfo["VariableChanged"]; $changedtime = $varInfo["VariableChanged"];
@@ -212,11 +214,13 @@ class MonitoringServer extends IPSModule {
} }
SetValueInteger($this->GetIDForIdent("AlarmVarCount"),$alarmvalues); SetValueInteger($this->GetIDForIdent("AlarmVarCount"),$alarmvalues);
SetValueInteger($this->GetIDForIdent("AlarmActiveCount"),$inalarmcount); SetValueInteger($this->GetIDForIdent("AlarmActiveCount"),$inalarmcount);
$rootcat = IPS_GetParent($catAlarmId); if (IPS_CategoryExists($catAlarmId)) {
if ($inalarmcount > 0 && $rootcat >0){ $rootcat = IPS_GetParent($catAlarmId);
IPS_SetIcon($rootcat,"Alert"); if ($inalarmcount > 0 && $rootcat >0){
}else{ IPS_SetIcon($rootcat,"Alert");
IPS_SetIcon($rootcat,""); }else{
IPS_SetIcon($rootcat,"");
}
} }
} }
@@ -256,6 +260,7 @@ class MonitoringServer extends IPSModule {
$catNotifyId = $this->ReadPropertyInteger("ParseNotifyCategoryID"); $catNotifyId = $this->ReadPropertyInteger("ParseNotifyCategoryID");
$catAnalogId = $this->ReadPropertyInteger("ParseAnalogCategoryID"); $catAnalogId = $this->ReadPropertyInteger("ParseAnalogCategoryID");
$catSetpointId = $this->ReadPropertyInteger("ParseSetpointCategoryID");
$projectnumber = $this->ReadPropertyInteger("Projectnumber"); $projectnumber = $this->ReadPropertyInteger("Projectnumber");
$projectname = $this->ReadPropertyString("Projectname"); $projectname = $this->ReadPropertyString("Projectname");
@@ -274,7 +279,7 @@ class MonitoringServer extends IPSModule {
if ($catAnalogId > 0){ if (IPS_CategoryExists($catAnalogId)){
$catChilds = IPS_GetChildrenIDs($catAnalogId); $catChilds = IPS_GetChildrenIDs($catAnalogId);
@@ -285,6 +290,9 @@ class MonitoringServer extends IPSModule {
$parname = IPS_GetName($catChild); $parname = IPS_GetName($catChild);
foreach ($objchildids as $objchildid){ foreach ($objchildids as $objchildid){
if (!IPS_VariableExists($objchildid)) {
continue;
}
$varInfo = IPS_GetVariable($objchildid); $varInfo = IPS_GetVariable($objchildid);
$changedtime = $varInfo["VariableChanged"]; $changedtime = $varInfo["VariableChanged"];
@@ -348,7 +356,7 @@ class MonitoringServer extends IPSModule {
SetValueInteger($this->GetIDForIdent("AnalogVarCount"),$numberofvalues); SetValueInteger($this->GetIDForIdent("AnalogVarCount"),$numberofvalues);
$numberofvalues = 0; $numberofvalues = 0;
if ($catSetpointId > 0){ if (IPS_CategoryExists($catSetpointId)){
$catChilds = IPS_GetChildrenIDs($catSetpointId); $catChilds = IPS_GetChildrenIDs($catSetpointId);
@@ -366,11 +374,10 @@ class MonitoringServer extends IPSModule {
} }
} }
SetValueInteger($this->GetIDForIdent("SetpointVarCount"),$numberofvalues); SetValueInteger($this->GetIDForIdent("SetpointVarCount"),$numberofvalues);
$numberofvalues = 0; $numberofvalues = 0;
if ($catNotifyId > 0){ if (IPS_CategoryExists($catNotifyId)){
$catChilds = IPS_GetChildrenIDs($catNotifyId); $catChilds = IPS_GetChildrenIDs($catNotifyId);
@@ -381,6 +388,9 @@ class MonitoringServer extends IPSModule {
$parname = IPS_GetName($catChild); $parname = IPS_GetName($catChild);
foreach ($objchildids as $objchildid){ foreach ($objchildids as $objchildid){
if (!IPS_VariableExists($objchildid)) {
continue;
}
$varInfo = IPS_GetVariable($objchildid); $varInfo = IPS_GetVariable($objchildid);
$changedtime = $varInfo["VariableChanged"]; $changedtime = $varInfo["VariableChanged"];
@@ -709,7 +719,7 @@ class MonitoringServer extends IPSModule {
$catAnalogId = $this->ReadPropertyInteger("ParseAnalogCategoryID"); $catAnalogId = $this->ReadPropertyInteger("ParseAnalogCategoryID");
$catAlarmId = $this->ReadPropertyInteger("ParseAlarmCategoryID"); $catAlarmId = $this->ReadPropertyInteger("ParseAlarmCategoryID");
if ($catNotifyId > 0){ if (IPS_CategoryExists($catNotifyId)){
$catChilds = IPS_GetChildrenIDs($catNotifyId); $catChilds = IPS_GetChildrenIDs($catNotifyId);
@@ -723,7 +733,7 @@ class MonitoringServer extends IPSModule {
} }
} }
if ($catAnalogId > 0){ if (IPS_CategoryExists($catAnalogId)){
$catChilds = IPS_GetChildrenIDs($catAnalogId); $catChilds = IPS_GetChildrenIDs($catAnalogId);
@@ -737,7 +747,7 @@ class MonitoringServer extends IPSModule {
} }
} }
if ($catAlarmId > 0){ if (IPS_CategoryExists($catAlarmId)){
$catChilds = IPS_GetChildrenIDs($catAlarmId); $catChilds = IPS_GetChildrenIDs($catAlarmId);
@@ -879,4 +889,4 @@ private function generateAndUploadCsv($catId, $ftp, $remotePath, $localPath) {
} }
?> ?>