Skip to the content.

MQTT Plug-in Help

Back to Projects Index

Back to MQTT Index

Overview

The MQTT Client Plug-in provides a client interface to MQTT for HomeVision. Its main purposes are to control MQTT enabled devices via the HomeVision Schedule or NetIO and to control HomeVision internal objects (X-10, Flags, etc.) by MQTT sources.

Note: To perform actions on internal objects, this plug-in uses the Actions Plug-in. The Actions Plug-in must be enabled to control internal objects.

While the client plug-in is designed to work easily with Tasmota based devices, using a similar topic structure, other devices that follow different topic structures likely can be accommodated as well. There is a lot of flexibility in the plug-in allowing support for many different situations.

The MQTT interface has three distinct functions:

The way the MQTT plug-in handles internal objects and external devices can be confusing. In fact, from an MQTT perspective, both internal objects and external devices are handled the same. They are both controlled by command (cmnd) topics and report status via status (stat) topics.

Internal devices are “embedded” in HomeVision. So, from the MQTT perspective, they are controlled (via MQTT) with an incoming cmnd message, and they will report their status via an outgoing stat message. But that’s exactly the same as an external device: control them (via MQTT) with a cmnd message, and they will report their status via a stat message.

A cmnd message is used to control an internal object or external device; a stat message is used to track an internal object’s or external device’s state.

A confusing part is that the FLOW of cmnd/stat messages with respect to the MQTT plug-in is reversed for the two types, simply because of where they reside in the architecture of the system.

Another confusing part is that for external devices, it may be desirable for Homevision to do something when it reports its status. Hence the plug-in responds to stat status topics. This looks a lot like it is doing a cmnd type of work, but in fact it is no different from any other home automation system. It is just “responding” to the external device’s change in status.

For example, suppose there is an external light switch (like a Tasmotized Sonoff wall switch), and both HomeVision and Home Assistant. When that switch is turned on, it reports that fact with a stat message, and both systems may respond: HomeVision may take an action, like running a macro, and Home Assistant may show the light is on in the GUI, or even run an automation.

HV-MQTT

Supported MQTT Topics

Standard and Custom Topics

Topics can be assigned to devices either as abbreviated topics (called “Standard” topics in this help) or full topics. Standard topics follow the Tasmota structure, so only the unique sub-topic portion need be entered. Default standard topics are indicated by enclosing the sub-topic with “<” and “>”. If a topic starts with a “<”, the appropriate prefix is automatically added. If a topic ends with a “>”, the appropriate postfix is automatically added. A “>” may be followed by optional index digits. Prefixes and postfixes are defined in the Settings Tab. Most of the examples to follow assume use of the defaults.

To provide additional flexibility in defining a system’s topic structure, standard topics can have the prefix and postfix indicators in any position relative to the sub-topic. The following are possible (where “T” is the sub-topic):

   Template        Example
      <T>       cmnd/T/POWER   --> Tasmota and MQTT Plug-in default
      T<>       T/cmnd/POWER
      <>T       cmnd/POWER/T
      >T<       POWER/T/cmnd
      T><       T/POWER/cmnd
      ><T       POWER/cmnd/T

To accommodate certain Tasmota devices that have multiple relays or switches, one or more digits can follow the “>” postfix index to specify the relay or switch. For example, an external device of this type that sends out state reports like this:

    stat/FloorLamp/POWER2 ON

should be set up with this topic structure:

    <FloorLamp>2

Devices that send out reports for several “relays” should have separate entries in the Ext Devices Tab for each “relay”.

Note 1: While Tasmota indices are in the range of “1” through “32”, the plug-in allows any number of digits. When using one of the templates where the topic follows a postfix with a relay index, care should be taken to avoid topics that start with a number, as the plug-in won’t be able to tell where the index ends and the topic begins.

Note 2: Tasmota considers “POWER1” and “POWER” as the same and interchangeable. However, the plug-in treats them as different, meaning “POWER” won’t match a full topic containing “POWER1”.

If a topic has only a “<” OR a “>”, then only the appropriate prefix OR postfix is automatically added. Otherwise, the topic is used as-is without any additional portions prepended/appended to it. Thus by not putting “<” and “>” in a topic, any topic structure can be used. However, the plug-in does not have built-in processing when receiving MQTT topics that don’t follow the standard prefixes and postfixes. In these cases custom procedures need to be provided. See Custom Processing of Received Messages.

Most of the examples in the rest of this Help follow the Tasmota standard.

Standard Command Topic

The following command topic is supported (using default prefixes and postfixes) with various payloads:

        Full Topic     Payload
    cmnd/topic/POWER  ON
    cmnd/topic/POWER  ON level
    cmnd/topic/POWER  level
    cmnd/topic/POWER  OFF
    cmnd/topic/POWER  TOGGLE
    cmnd/topic/POWER  empty or "?"** 

topic is defined during device configuration.
Payloads are case-insensitive for incoming messages.
level is expressed as a percentage, 0-100. For X-10 devices, it is converted to the next lower discrete level supported. Any status message will report back the discrete level as a whole percentage, so it may not match exactly what was received.
** “?” for those external entities that don’t allow an empty payload.

The plug-in publishes command topics to external devices. Usually, after receiving one of these commands, an external device will publish its state. The plug-in receives this report by subscribing to the state topic. (See next.) A command with an empty payload requests the device to publish its state without changing the state of the device.

HomeVision objects subscribe to command topics so that external entities can control them. For a full list of Actions based on the received topic and payload, see Object Actions.

Standard State Topic

For each external device, the plug-in subscribes to the following topic (assuming <topic> in the “Topic” field):

        Full Topic                     Payload
    stat/topic/POWERx           OFF/ON/ON level/level

When a POWER topic is received, the plug-in looks for a message of “on” or “off” and takes action according to the device’s settings in the Ext Devices Tab. For a full list of Actions based on the received topic and payload, see External Device Actions.

When an internal object changes state, the plug-in will publish a state message to indicate the object’s new state.

RESULT State Topic

Certain devices, i.e., those running Tasmota software, usually produce both at POWER state message and a RESULT state message with the POWER info in a JSON formatted payload. In fact, RESULT state messages can be sent from the device when many other actions occur.

To receive such messages, they must explicitly be entered as a device in the Ext Devices Tab with a custom command defined to process the message.

          Topic                         Payload
    stat/topic/RESULT               a JSON string

Last Will and Testament Topic

For each external device with a standard topic that has “Subscribe to Last Will and Testament” checked, the plug-in automatically subscribes to a Last Will and Testament topic as follows:

        Full Topic     
      tele/topic/LWT

If received, the plug-in looks for a message of “online” or “offline” and attempt to display this message in the Ext Devices Tab.

In fact, for standard topics, the exact LWT subscription will be as defined in the device’s “topic” field, where “tele” is the prefix (<) and “LWT” is the postfix (>).

If the topic is not in the form of a standard topic, the “Subscribe to Last Will and Testament” checkbox has no effect.

LWT is not supported for internal objects.

Special “homevision” Topic

The MQTT Plug-in automatically subscribes to:

        Full Topic
    cmnd/homevision/#

When a message is received in the form:

        Full Topic                           Payload
    cmnd/homevision/object_type/POWER      empty or "?"
    cmnd/homevision/POWER                  empty or "?"

for each defined object matching object_type (one of x10, light, var, flag, hvac, temp, analog, timer, input or output) or for all of these object types if not specified, the plug-in will publish a state message for each object to indicate its current state. Only those objects specified in the Int Objects Tab are reported. Macros, Scheduled Events, Periodic Events and IR are never reported as they have no “state”. This feature can be used by an MQTT entity to quickly sync up with HomeVision object states.

When a message is received in the form:

        Full Topic                           Payload
    cmnd/homevision/action                trigger string

the MQTT Plug-in will execute the trigger string. See Homevision Action Topic for details.

Special Counting Payload

The MQTT Plug-in has special handling to provide a “counting” feature. Certain MQTT messages will increment selected variable(s) each time the message is received. To activate this feature, create either an external device or internal object with either one or two variables selected. Then, in the “Count Payload Text” of the Settings Tab, enter the text that will indicate “Count”. For external devices, receiving a “stat” or “tele” “POWER” message that has its first word in the payload matching “Count Payload Text” will cause the selected variable(s) to increment. For internal objects, a “cmnd” “Power” message will do the same.

For example, if “Count Payload Text” is set to “CYCLE”, and an internal object is created with Var-100 and Var-101 assigned to it but re-named “Watchdog”, then when the following is received:

    cmnd/Watchdog/POWER CYCLE

Var-100 and Var-101 are incremented.

Note: Other messages that would set variables assigned to this topic will continue to do so. This can be used to advantage; sending a “cmnd” “POWER” message with a payload of “0” would “reset” the variables so subsequent “Count” messages would start from zero.

Configuring Devices

Ext Devices Tab

This tab contains a list of supported external devices. Device Name is the name of the device for use by serial and NetIO commands. It must be unique among both external and internal device names. See below for Name rules. Topic is the topic used for publishing and subscribing. See below for Topic rules.

New/Edit/Delete External Devices

Int Objects Tab

This tab contains a list of supported internal objects. These can be any of: X-10, Custom Lights, Flags, Variables, Inputs, Outputs, IR, Digital Temperature Sensors, Analog Inputs, Macros, Scheduled Event, Periodic Events, Timers or HVAC. Add only those objects that need to be visible to or acted on by the MQTT network.

New/Edit/Delete Internal Objects

Settings Tab

        Full Topic                     Payload
    stat/topic/POWERx           OFF/ON/ON level/level

“RESULT” enables a response like this, if “Dimming” is unchecked:

        Full Topic                     Payload
    stat/topic/RESULT        {"POWERx":"OFF"/"ON"/"ON level"/level}

or like this if “Dimming” is checked:

        Full Topic                     Payload
    stat/topic/RESULT        {"POWERx":"OFF"/"ON","Dimmer":level}

Pub/Sub Tab

The Pub/Sub Tab allows entry and publication of two topic/payload pairs and entry and subscription to two topics. It can be useful for testing or accessing/monitoring other devices.

Standard or custom topics are allowed. (See Standard and Custom Topics for details.) Subscription topics can make use of MQTT wildcards “#” and “+”. No validation is done on topics. If a topic is not formed correctly, it simply won’t work.

A payload is not required.

The unlabled checkbox before the “Publish” button sets the retain flag to 1 for the message. It should rarely be needed. The most useful reason for setting retain to 1 (checking the box) would be to clear a retained message in the broker. Publishing a topic with a empty payload and retain set to 1 should clear any retained messages for that topic in the broker.

When receiving a message that matches one of the subscribed topics, the topic and payload will appear in the Log sub-window (See below) and in the debug plug-in in blue text if the debug plug-in is enabled.

The “Unsubscribe” button will manually unsubscribe its associated topic.

Changing and Subscribing a topic entry after the previous entry was Subscribed will result in the previous entry being automatically unsubscribed before the new one is subscribed.

When the MQTT Configuration window is closed, subscribed topics remain subscribed. While obviously not showing up in the Log window, incoming subscribed messages will continue to show in the debug window, if enabled.

Each text entry field has a drop-down feature to access recent entries:

Pub/Sub Log Area

The Log window of the Pub/Sub Tab shows results based on publishing and subscribing activity.

Responding to External Device State Changes

Refer to External Device Actions for responses to received messages.

Note that an assigned Flag or Variable is always updated before the macro is run so the macro can take advantage of the new value.

Controlling Devices

Device/Object Display Area

Right-clicking on a line in the Device/Object Display Area will bring up a menu from which “On”, “Off”, “Toggle”, “State”, and “Set to” can be selected. For internal objects, one or more of these items may be grayed out if not appropriate for the object type.

For external devices, none are grayed out as there is no knowledge of the capabilities of the device. Some or all of these items may not work based on what the device can do (or not do).

When selected, for standard topics, the corresponding command topic is published. (See Standard Command Topics above). For other non-standard topics, the right-click items may or may not make sense.

For external devices, this typically will make its way through the MQTT broker to the external device which would then act on the command. For external device topics that are not standard, or at least missing the “<”, the topic will be published “as-is”, with the appropriate payload. Since the plug-in will have subscribed to this topic, the message will be sent right back to the plug-in from the broker.

For internal objects, the corresponding command topic is published in the same manner as external devices. (Some object types “interpret” the selection depending on its capability. For example, clicking “On” for a flag translates to SET, while “Off” translates to CLEAR.) No action is taken directly on the internal object. Only a cmnd messages is sent out. However, since the internal object has subscribed to the command topic, the MQTT broker will send it right back to the plug-in, which will then set the object to the requested state. If the command topic causes an object state change, the plug-in completes the sequence by publishing a state topic showing the new state. Here’s a sequence chart example that might make it more clear:

  HomeVision            Plug-in                     MQTT Broker

                 Right-click X-10 object "Den"
                     and click "Toggle":

                    cmnd/den/POWER TOGGLE -->
                                          <-- cmnd/den/POWER TOGGLE
               <-- HV cmd to toggle Den
  sends X-10 cmd
   to toggle Den
               --> stat/den/POWER {value} -->

Serial Control

MQTT devices can be controlled within a schedule via serial commands which take one of these forms:



    mqtt: {-log|-nolog -retain|-noretain} device_name/object_name command; 
     - command is one of: [0|off|1|on|2|toggle|on {level}|state]. 
         "state" requests status from device/object.
     - Logs and retains according to option(s) if present (overrides configuration settings);
     - Default: log and retain according to named device/object's config settings.

    mqtt: {-log|-nolog -retain|-noretain} pub topic {payload {retain}};
     - logs and retains according to option(s) if present;
     -   "-retain|-noretain" removes "retain as last word" restriction.
         I.e., payload can actually include "retain" as last word;
     - default: always logs and retains if last word is "retain".

    mqtt: {-log} [sub|unsub] topic <topic>|topic callback;
     - logs if option is present, never retains;
     - default: never logs, never retains.

    "mqtt:" is whatever the "Serial string prefix" is defined as.
    ";' is whatever the "Serial string terminator character(s)" is defined as.

Device/Object Form

This is the simplest form and can be used for any device or object alredy configured in the Ext Devices or Int Objects tabs.

For example, to toggle a device named “sonoff1”, the serial command would be:

    mqtt: sonoff1 toggle;

X-10 and Custom Lights can be set to a level by using “on level”. E.g.,

    mqtt: den on 50;

The device name is case-insensitive when matching a device name in the Device list. The command portion is sent as-is, case-wise.

Note: Device names are limited to alphas, numbers and the underscore.
Note: While serial commands in the schedule can change the state of internal objects as well as external devices, it may make more sense to just set the internal object directly in the schedule. In either case, the plug-in will publish a state change topic if the state actually changed.

Generic Form

In addition to the external and internal device/object specific MQTT messages, the plug-in allows generic MQTT messages that may or may not be related to any configured device. This gives freedom to send anything necessary without tying messages to specific configured devices.

For “pub” commands, if the last word in the payload is “retain”, the command is sent with the MQTT retain flag set. However, if either -retain or -noretain is present, it removes the “retain as last word” restriction. I.e., payload can actually include and send “retain” as the last word.

Examples:

    mqtt: pub "cmnd/living room/POWER" some payload info;
	      logs; sends "some payload info" to "cmnd/living room/POWER"; no retain.
    mqtt: -nolog pub "cmnd/living room/POWER" some payload info;
	      no log; sends "some payload info" to "cmnd/living room/POWER"; no retain.

    mqtt: pub "cmnd/living room/POWER" some payload info retain;
	      logs; sends "some payload info" to "cmnd/living room/POWER"; retains.
    mqtt: -nolog -noretain pub "cmnd/living room/POWER" some payload info retain;
	      no log; sends "some payload info retain" to "cmnd/living room/POWER"; no retain.
	

For sub and unsub commands, callback is a public callback procedure that must exist in a plug-in.

Examples:

    mqtt: sub tele/somedevice/state mycb;
    mqtt: unsub tele/somedevice/state mycb;

Formatting Rules

NetIO

NOTE: While the NetIO application is no longer supported by its developers, it still works and the NetIO plug-in may still be useful to those still using the app.

MQTT devices can be controlled via NetIO using a “netioaction” command in the NetIO application. All forms of the serial commands are available for NetIO as well.

For example, to have a button set up to toggle a device named “sonoff1”, the button’s sends attribute would be set to:

    sends: netioaction mqtt sonoff1 toggle

“mqtt” is whatever the “Netio string” is defined as. “toggle” or “2”, “on” or “1”, “off” or “0”, “on {level}” and “state” are allowed.

The device name is case-insensitive when matching a device name in the Device list. The command portion is sent as-is, case-wise.

The state of the device can get retrieved by:

    reads: get mqtt sonoff1

The get command will return the object’s state string, depending on the state of the device.

Note: This is a “convenience” command, since it actually just reads the state of the Flag or Variable associated with an external device, or the state of an internal object. For external devices, if no flag or variable are associated, an empty string is returned. If the device’s state has not yet been reported, the last value of the Flag or Variable will be returned.

Also note that this command is essentially the same as the more direct gets:

    reads:  get var state var#

or

    reads:  get flag state flag#

or

    reads:  get x10 state id#

except that no NetIO Custom Returns processing is done. So the direct object gets are probably better to use than the MQTT versions.

Generic MQTT messages can be sent via NetIO using a “netioaction” command in the NetIO application in a similar way as serial commands.

Examples:

    sends:  netioaction mqtt sub tele/somedevice/state mycb
    sends:  netioaction mqtt unsub tele/somedevice/state mycb
    sends:  netioaction mqtt pub {cmnd/living room/POWER} some payload info
    sends:  netioaction mqtt pub {cmnd/living room/POWER} some payload info retain

Note: For NetIO, a topic with spaces must be enclosed by braces {}. Double-quotes are not allowed due to the way NetIO handles arguments of the netioaction command.

Custom Processing of Received Messages

Sometimes a topic may not fit the standard forms supported by the plug-in, or the actions taken (setting flags, variables and running macros) may not be powerful enough. There are four methods that provide more advanced processing, listed here in increasing independance from the MQTT Plug-in’s configuration:



Triggers and Homevision Action Topics are relatively simple to construct. Custom Commands and Independant Plug-ins require some knowladge of the TCL programming language.

Triggers

Triggers are a way to get a little more processing power without coding. Works well for sequential actions that don’t require decision making.

Instead of a command procedure, enter serial trigger strings to perform actions. A serial trigger string can be one or more “built-in” HomeVisionXL strings and/or serial strings supported by existing plug-ins.

Note: Like “Command”, the “Flag/Var”, “On Macro” and “Off Macro” fields are ignored. However, use the action command in trigger(s) to manipulate flags, vars, macros, etc..

To use Triggers, select “Trigger”. Once selected, the “Topic Type” options are available.

Selecting “Standard” is used with standard topics (generally, those of the form <topic>), so “stat” messages with payloads of “on”, “off”,”on {level}” and “{level}” are available. If “All” is selected, then only one trigger entry is provided, and this is run regardless of the payload. If “On/Off” is selected, there are separate entries for the “on” and “off” states. However, one of the two fields can be empty, which means that nothing will happen for messages received with that state. A payload of “0” or “on 0” implies “off”.

Selecting “Custom” should be used for topics that don’t adhere to the “standard” topic model, and hence can’t be guaranteed an “on/off” payload. Only one trigger entry is available and is run regardless of the payload contents.

The following special character strings will cause substitutions for every occurrence in a trigger string(s). (Note: %X, %P, %E, %L don’t make sense for “Custom” trigger strings, but, if used, will result in a “0” being substituted.)


Examples:

Play a sound file and run a program when the topic is received. This example shows how two separate “built-in” triggers can work together in one trigger string. (Assumes that the sound and program functions are working.)

Options: Custom. 

Trigger:
    Play wav file "alarm"  Run program "sendtext"


Write a daily log file. (This may be redundant, as the MQTT Plug-in has logging capability in other forms, but shows what can be done.)

Options: Standard, All.
 
Trigger:
    write to file "filename%D" "%T: %O:%M"


Write a daily log file, where the payload may have double quotes in it (i.e., a JSON string). In that case, the double quotes must be converted to single quotes to avoid confusing the “write to file” processing.

Options: Standard, All.
 
Trigger:
    write to file "filename%D" "%T: %O:%m"


The following examples require action plug-in version 1.3 or greater.

Turn on/off other lights to same level as triggering device.

Options: Standard, On/Off. 

On Trigger:
    action: x10 pcslevel c12 %P, x10 level a3 %X;

Off Trigger:
    action: x10 off c12, x10 off a3;


Tune a TV to a channel using IR. (Select cable input to TV, then tune a channel with a .5 second delay between digits.)

Options: Standard, On/Off. 

On Trigger:
    action: ir transmit 28 1,ir transmit 114 1,wait for 500,ir transmit 116 1;

Off Trigger:
    {empty}


Tune a TV to a channel on a streaming service. (Turn on TV, 0.5 second delay for TV to turn on, select streaming device input, then select streaming channel.) This example shows how two separate plug-in triggers (action and roku) can work together in one trigger string.

Options: Standard, On/Off. 

On Trigger:
    action: ir transmit 2 1,wait for 500,ir transmit 26 1; roku: 13;

Off Trigger:
    {empty}

Note: roku is a custom plug-in. See it Here.

Custom Commands

To have custom processing of received messages for a topic, click “Command” and enter a procedure name in the “Command” field. The procedure becomes the callback procedure for this topic.

Note: If “Command” is selected, the “Flag/Var”, “On Macro” and “Off Macro” fields are ignored. However, the action command can be used in the procedure to manipulate flags, vars, macros, etc..

Log settings are also ignored. Any logging should be done in the command procedure using the mqttLog command (See below).

CAUTION: There is little validation of the name of the custom procedure. Care should be taken to avoid any standard TCL procedure names as inadvertently using an existing procedure name may result in abnormal behavior!

The procedure must be defined in another enabled plug-in, and must be made public via the hvPublic command. A plug-in can contain several different procedures that are called by different messages.

The procedure will be called like this:

    procedure_name topic payload retain {other args}

For most procedures, retain can be ignored. {other args} may be present in future versions of the MQTT client and likely not be important for typical procedures. For those interested, details of retain and any future other args can be found here

Example:

Suppose we have a device (named BathHumidity) running Tasmota software with a AM2301 temperature/humidity sensor attached , and we want to turn on a ventilator fan (named BathFan) when the humidity gets high (>70) and turn it off when it is low (<55). We would get periodic MQTT tele reports like this:

    tele/BathHumidity/SENSOR {
        "Time":"2020-05-20T15:13:25",
        "Switch2":"OFF",
        "AM2301": {
              "Temperature": 75.0,
              "Humidity": 82.0
        },
        "TempUnit": "F"
    }

(The JSON payload is expanded here for readability.)

In the Ext Devices Tab, add an external device for the humidity sensor with a full topic of

    tele/BathHumidity/SENSOR

The MQTT Plug-in will explicitly subscribe to this topic instead of the normal standard state topics. Click “Command” and set the Command entry field to “humid”. Click “OK” to save this entry.

Add another external device for the fan with a standard topic

    <BathFan>

so that it will report status in the standard way, like this:

    stat/BathFan/POWER ON

Click “Command” and set the Command entry field to “bathfan”. Click “OK” to save this entry.

Click “Done” for changes to be effective!

Create a plug-in containing the following:

    tcl::tm::path add [file dirname [info script]]
    package require json 1.0

    hvImport mqttComm
    
    hvPublic humid
    proc humid {topic payload args} {
        global fanStatus
        if {$payload eq ""} return
        if {[catch {::json::json2dict $payload} status]} return

        set humidity [dict get $status AM2301 Humidity]
        if {$humidity > 70 && !$fanStatus} {
            mqttComm pub "cmnd/BathFan/POWER" on
        }
        if {$humidity < 55 && $fanStatus} {
            mqttComm pub "cmnd/BathFan/POWER" off
        }
    }
    
    hvPublic bathfan
    proc bathfan {topic payload args} {
        global fanStatus
        if {[lindex [split $topic "/"] end] eq "POWER"} {
            if {$payload eq "ON"} {
                set fanStatus 1
            } elseif {$payload eq "OFF"} {
                set fanStatus 0
            }
        }
    }

(See next Section for a description of mqttComm.)

Procedure humid is called whenever a humidity status message is received. It processes the JSON status message to get the humidity and turns the fan on or off depending on the humidity level. Since we only care about the topic and payload, we can lump the rest into args and ignore that.

Procedure bathfan is called whenever a state message from the fan switch is received. It tracks the state of the fan so humid only turns the fan on/off if it is not already. In reality, bathfan isn’t necessary, as turning on the fan while it is already on does no harm. It’s here mainly as an example of how to set up a command.

Homevision Action Topic

This Topic can be used to send a set of commands just like those allowed in an external device trigger. Since this method is not supported in Home Assistant’s MQTT discovery, it would need to be used in configuration.yaml or in a GUI-based construction, like a button.

However, it is powerful enough that, in some cases, using this method could eliminate the need for “virtual” external devices and the corresponding manual configuration.yaml changes if using Home Assistant!

For example, the previous external device trigger can be done like this:

    Topic:    cmnd/homevision/action
    Payload:  action: ir transmit 2 1,wait for 500,ir transmit 26 1; roku: 13;

In response to an action command, the actions will be returned in a status message:

    Topic:    stat/homevision/action
    Payload:  action: ir transmit 2 1,wait for 500,ir transmit 26 1; roku: 13;

This message may be returned before all the actions in the trigger are completed, especially if the trigger contains “waits”.

Trigger % substitutions are NOT performed on the payload using this method.

mqttComm - Sending/Receiving MQTT Messages from/to Another Plug-in

When total control for MQTT message processing is needed, this method takes “Custom Commands” a step farther. A plug-in can be essentially independent of processing that is done in the MQTT Plug-in, except for using it as a MQTT transport mechanism.

Other plug-ins can interface with the MQTT Plug-in via the mqttComm command. The calling plug-in should import the command via:

    hvImport mqttComm

The mqttComm command has the following formats:

    mqttComm {-log} sub|unsub <topic>|topic callback 
    mqttComm {-exactstat -nodim -log -retain} stat|cmnd <topic>|topic {payload}
    mqttComm {-log -retain} pub topic {payload}


A “stat” or “cmnd”, when used with a topic with neither “<” nor “>”, ignores the “stat” or “cmnd” and is the same as using “pub”.

A “pub” or “cmnd”, when used with a standard topic, is the same as using “cmnd”.

When the standard form is used with “sub” or “unsub”, a “cmnd” pre-fix is assumed. To subscribe or unsubscribe to a “stat” type topic, use the topic without the “<”. Example: To subscribe to a stat message:

    mqttComm sub stat/uroom> cascallback

which will subscribe to

    stat/uroom/POWER

with “cascallback” as the callback procedure.

Caution: Make sure when subscribing to a topic that the topic is unique, especially compared to external devices and internal objects. The MQTT client allows multiple, different callback procs to be assigned to the same topic (via different subscriptions) resulting in all of the procs being called when the common topic arrives. This could be useful, but normally it will cause unexpected behavior.

For the MQTT client to successfully execute the callback procedure, an “hvPublic” command should be added to the plug-in. For example,

    hvPublic cascallback
    proc cascallback {topic payload retain} {
        ...
    }



The mqttComm procedure returns an empty string if MQTT is not ready. This can be used to trigger retries until MQTT is ready.

The mqttComm procedure returns 0 if the type, action or topic is missing or NULL, or if there is no payload for a cmnd or stat command.

The mqttComm procedure returns 1 for a successful operation.

Examples:

    mqttComm sub <uroom> cascallback
        subscribes to cmnd/uroom/POWER with callback cascallback

    mqttComm unsub stat/uroom/power mycallback
        unsubscribes to stat/uroom/power with callback mycallback

    mqttComm cmnd <uroom> on
        publishes cmnd/uroom/POWER on

    mqttComm stat <uroom> on
        publishes stat/uroom/POWER on and/or
                  stat/uroom/RESULT {"POWER":"ON","Dimming":100}

    mqttComm -nodim stat <uroom> on
        publishes stat/uroom/POWER on and/or
                  stat/uroom/RESULT {"POWER":"ON"}

    mqttComm -exactstat stat <uroom> on
        publishes stat/uroom/POWER on 

    mqttComm stat "cmnd/room 2/state" on
        publishes cmnd/room 2/state on

    mqttComm pub "cmnd/room 2/state" on
        publishes cmnd/room 2/state on

Callbacks with the “sub” form of mqttComm

When something subscribes to a topic, the assumption is that some action should be taken when a message with that topic arrives. So how is that handled when a plug-in uses mqttComm to subscribe to a topic? This is where callbacks come in. When an incoming MQTT message arrives, the MQTT Plug-in will call the callback procedure associated with the previous subscription for that topic. It will be called like this:

    callback fulltopic payload retain


This is best explained by example. Let’s assume a plug-in wants to subscribe to a topic:

    mqttComm sub <uroom> cascb

This results in a subscription to

    cmnd/uroom/POWER

When received, this topic means either report back the current device state (with an empty or “?” payload), or change the device state based on the payload. That means that when the MQTT Plug-in receives the message, it must communicate with the subscribing plug-in to complete the response. It does this by calling the callback procedure. Let’s say some external entity wants to know the state of uroom. When the MQTT Plug-in received the appropriate message, it calls the callback procedure like this:

    cascb cmnd/uroom/POWER "?" 0

A procedure “cascb” must be defined and made public (via hvPublic cascb) in the calling plug-in to handle the message. In this case, it should eventually use another mqttComm command to send back status, maybe like this:

    mqttComm stat <uroom> On 50
        --->   stat/uroom/POWER ON 50

There are a couple of different approaches to handling subscribed topics. Which is better depends on the goals of the plug-in.

If the plug-in is going to handle a few very specific fulltopics, a separate callback for each might be the best way to handle them.

If it needs to handle a number of very similar fulltopics, one callback that parses the fulltopic might be best.

Either approach will work.

Other Public Procedures Supplied/Called by the MQTT Plug-in

topicTemplate

To help with parsing full topics, the MQTT Plug-in makes public a helper procedure that will split a full topic (the topic returned to the callback) into its parts and provide other info about the full topic.

The calling plug-in should import the command via:

    hvImport topicTemplate

The topicTemplate command should be used like this:

    set template {*}[topicTemplate topic]
Key(s)Value
templateOne of <T>, >T<, <>T, T<>, ><T, T><, <T, >T, T>, T<, T.
pre posPrefix position 1-3, -1 if no prefix.
pre typePrefix string, "" if no prefix.
topic posSub-topic position 1-3
topic nameSub-topic string
topic type"s" (standard: contains both < and >), "ns" (non-standard) otherwise.
post posPostfix position 1-3, -1 if no postfix.
post typePostfix string, "" if no prefix.
post ntypePost type with "Relay" number striped off.
post index"Relay" number or "[0-9]*" if none, or "" if no postfix.
matchRegexp expression to match the full topic.

The resulting dict returned by topicTemplate can be used like this:

    set template {*}[topicTemplate topic]

    set subtop [dict get $template topic name]
    set pre [dict get $template pre type]
    set post [dict get $template post type]
    if {$subtop ne "mytopic"} {return}
    if {$pre eq "cmnd"} {
        switch $post {
            "command_type_1" {
                ...
            }
            "command_type_2" {
                ...
            }
            ...
        }
    }

mqttLog

Puts an entry into the current MQTT log file.

The calling plug-in should import the command via:

    hvImport mqttLog

The mqttLog command has the following format:

    mqttLog string {color}

mqttReady

This procedure is defined by the using plug-in and is called by the MQTT Plug-in whenever it connects to or disconnects from the MQTT broker (and hence ready (or not) to take mqttComm calls). It is used to detect transitions in MQTT status.

Using this procedure is only necessary to make sure the plug-in’s subscriptions are re-done automatically in the case the connection to the broker going down and then recovers (with a {state connected} return).

The using plug-in should make the command public via:

    hvPublic mqttReady

When the MQTT connection changes, the MQTT Plug-in calls mqttReady like this:

    mqttReady status

status is a dict of either {state connected} or {state disconnected reason reason}. Possible values for reason are:

    0 Normal disconnect
    1 Unacceptable protocol version
    2 Identifier rejected
    3 Server unavailable
    4 Bad user name or password



Reasons “1”, “2”, and “4” are fatal and need to be corrected before a connection can be made.

To use, define a mqttReady procedure to respond to the connected and/or disconnected states.

mqttStatus

This procedure is provided by the MQTT Plug-in and returns current MQTT status. (Compare to mqttReady.)

Its use is to make sure MQTT is ready before subscribing to topics.

    mqttStatus blank|state|session|reason|all

mqttStatus takes as an argument: “state”, “session”, “reason”, “all” or no arg - which defaults to “state”, the most likely arg to use.

If called with “All” as an argument, it returns a dict with the following keys:

state
The new connection state. Possible values are: "connected", or "disconnected". This item is always present in the returned dict.
session
Whether the broker has a session present for the client. The value will always be '0' for this version of MQTT.
reason
The reason for a disconnection or failed connection. Possible values for reason are:
    0 Normal disconnect
    1 Unacceptable protocol version
    2 Identifier rejected
    3 Server unavailable
    4 Bad user name or password

If called with “state”, “session”, “reason”, or no argument (default “state”), the procedure returns values as described above for that argument type.

mqttStatus and mqttReady Usage

Typical use:

    hvImport debug
    
    hvPublic samplecb
    proc samplecb {topic payload retain} {
        # do something 
    }
    
    hvImport mqttComm
    hvImport mqttStatus
    proc subscribe { {type sub} } {

        if {$type ni {sub unsub}} {return}
        if {[mqttStatus] ne "connected"} {
           debug "MQTT not connected" red
           # Try again after 5 seconds
           after 5000 subscribe
           return
        }
        # cancel any after command if got here via mqttReady
        after cancel subscribe
        # subscribe 
        mqttComm $type <sample_topic> samplecb
        ...
    }
    
    hvPublic mqttReady
    proc mqttReady {status} {
        if {[dict get $status state] eq "connected"} {
            # Code to run when MQTT is ready.
            # Typically a "subscribe" or "init" procedure.
            subscribe
        } else {
            # Code to run when MQTT is not ready.
        }
    }

    hvEventHook ready [list subscribe sub]
    hvEventHook exit [list subscribe unsub]

A sample plug-in using mqttComm, mqttReady and mqttStatus that does its own subscribing and needs no entries in the MQTT Plug-in’s device lists can be downloaded from here.

MQTT Discovery for Home Assistant

Tips for Interfacing HomeVision with Home Assistant
How to Use the MQTT Plug-in’s Home-Assistant Auto Discovery

Acknowledgements