Auf MQTT-Topic publishen leicht gemacht

Ich war im Zuge eines aktuellen Projekts etwas genervt davon, jedes Mal wenn ich auf ein anderes MQTT-Topic etwas publishen wollte, dazu extra eine MQTT-Device-Instanz anlegen muss. Darum habe ich mir diese Funktion geschrieben, welche im Hintergrund eine temporäre Instanz nutzt. Ich brauche nur die ID der MQTT-Server-Instanz anzugeben, das Topic sowie die Payload.

/* Publish a given payload over a MQTT server under
   a given topic without having to manually create a
   MQTT server device */
function MQTT_Publish($server_id, $topic, $payload, $retain = false) {
    // ensure server instance exists
    if(!IPS_InstanceExists($server_id)) {
        return false;
    }

    // convert array structure to json string
    if(is_array($payload)) $payload = json_encode($payload);

    // determine data type
    if(is_string($payload)) {
        $ips_var_type = 3;
    } else if(is_float($payload)) {
        $ips_var_type = 2;
    } else if(is_int($payload)) {
        $ips_var_type = 1;
    } else if(is_bool($payload)) {
        $ips_var_type = 0;
    } else { // unsupported
        return false;
    }

    $module_id = "{01C00ADD-D04E-452E-B66A-D253278743FE}" /* Module ID of MQTT Server Device */;
    $ident = "TempMQTTDevice";

    // enter semaphore to ensure the temporary device gets used by one thread at a time
    if(IPS_SemaphoreEnter($ident, 100)) {
        // get temporary MQTT Server Device or create if needed
        $id = @IPS_GetObjectIDByIdent($ident, $_IPS['SELF']);
        if($id === false) {
            $id = @IPS_CreateInstance($module_id);
            if($id === false) {
                return false;
            }
            IPS_SetParent($id, $_IPS['SELF']);
            IPS_SetIdent($id, $ident);
        }

        // ensure the specified server instance is actually compatible
        if(!IPS_IsInstanceCompatible($id, $server_id)) {
            return false;
        }

        // ensure that the temporary device is actually connected to the correct server instance
        $inst_config = IPS_GetInstance($id);
        if($inst_config["ConnectionID"] != $server_id) {
            IPS_DisconnectInstance($id);
            if(!@IPS_ConnectInstance($id, $server_id)) {
                return false;
            }
        }

        // name object to help with debugging
        IPS_SetName($id, "Temporary MQTT Server Device for topic " . $topic);

        // configure temporary device
        $config_arr = array(
            "Retain" => $retain,
            "Topic" => $topic,
            "Type" => $ips_var_type
        );
        $config_str = json_encode($config_arr);
        IPS_SetConfiguration($id, $config_str);
        IPS_ApplyChanges($id);

        // get Value variable and use it to publish the payload
        $var_id = @IPS_GetObjectIDByIdent("Value", $id);
        RequestAction($var_id, $payload);

        IPS_SemaphoreLeave($ident);
    } else { // semaphore timeout
        return false;
    }

    return true;
} // MQTT_Publish
3 „Gefällt mir“

Sehr cool.
Das hat mich auch schon oft generft für alles und jedes immer eine Instanz mit allem Tralala anlegen zu müssen.

Werd das sicher mal verwenden.
vielen Dank
Bernhard

1 „Gefällt mir“

Perfekt! Das sollte eigentlich Symcon von sich aus können!

Auch ein schöner, internerer Ansatz, ich hatte ja mehrfach

erwähnt, dass ich sehr häufig nutze, um beliebig und einfach zu publishen.

1 „Gefällt mir“

Mit IP-Symcon 6.4 könnt ihr doch genau das machen?

paresy

Es geht mir darum, dass ich überhaupt kein Device anlegen müssen will, um auf ein Topic zu publishen.

(Also „write only“ Fälle)

@paresy
Wie kann ich den mit RequestAction das Thema angeben?
Ich schick ja ein RequestAction mit der ID vom MQTT ServerDevice und dem Payload für die Message. Aber wo stell ich dann den Topic dynamisch ein?

Dann habe ich das falsch verstanden. Ich dachte geht darum ein abweichendes „set“ Thema für ein MQTT zu haben. Für beliebige Topics kann man sicherlich das Skript hier verwenden.

paresy

@paresy, wäre es nicht sogar möglich, der Client bzw. Server Instanz eine Publish Funktion zu geben?

Grüße,
Kai

Definitiv Möglich. Ich wäre dann aber eher für eine MQTT Export Instanz, die beliebige Topics senden kann aus PHP und wie eine RegisterVariable alle Topics empfangen kann. Quasi das was du als Modulentwickler kannst, nur auf User-Level Ebene für die Scripter :slight_smile:

paresy

Erweitere doch die RegVar um RegVar_SendTopic :slight_smile:
Michael

Ich fände eine Funktion für die Server- bzw. Clientinstanz intuitiver. Syntaktisch ähnlich meiner Funktion.

Damit könntest du aber nicht empfangen und somit hätten wir meiner Meinung nach nur 50% abdeckt.

paresy

Die phpMQTT Lib verwende ich in leicht angepasster Form in meinem MQTT Publish Modul intern auch. Den Server via instanceID zu übergeben ist zwar IPS konform, idR. will man jedoch einen externen MQTT Broker erreichen und selbst wenn nicht, könnte man ja auch einfach die IP/Port des internen Servers angegeben

Okay der Empfang Strang müsste dann auch noch um ein paar Felder erweitert werden :wink:

Michael

Du hast mich langsam mit deiner Idee. :sweat_smile:

paresy

… also wenn es so funktioniert wie beschrieben dann reicht das Script von Socke vollkommen.
Mehr muß nicht.

Widmet euch lieber wichtigeren Dingen.
bb

Wobei es ja einen entscheidenden Nachteil gibt :pensive:

Es fehlt der Filter für das topic beim Empfangen.
Somit würde jedes empfangene publish das hinterlegte Script der RegVar starten.
Könnten dann schnell, viele PHP Threads belegt werden.
Michael

Ja, ich finde für die „Empfangsseite“ die Struktur mit den Devices völlig okay. Es muss ja nicht wieder die nächste eierlegende Wollmilchsau werden. Mir persönlich erschiene es einfach nützlich (und relativ simpel gedanklich) wenn ich Publishes auf eine Server- bwz Clientinstanz „draufwerfen“ könnte.

Danke, konnte das auch für ein aktuelles Projekt verwenden. Funktioniert einwandfrei.

1 „Gefällt mir“