Update: Complete rewrite to use the rule templates published to the Marketplace. There is no need to code this or copy/paste code to make this work.
There is a Remote openHAB binding which replaces this capability in most cases. See openhab-addons/bundles/org.openhab.binding.remoteopenhab at main · openhab/openhab-addons · GitHub . However there may be use cases where MQTT is preferred, such as integration with a non-openHAB service.
Introduction
What’s an Event Bus?
There are times when one may have more than one OH instance that needs to have some or all of its Items synchronized across all the instances of OH. Another use is using MQTT to integrate openHAB with some other service such as a custom UI or external rule engine.
An Event Bus is a way to synchronize the Items command and updates across multiple instances of OH or other services.
When to use Event Bus
Let’s assume we have a Switch Item on a remote OH named Foo. We want to know the state of that Item on a local OH and we want to be able to send commands to that Item from local OH. This is the perfect use case for the Event Bus. To set this up all updates to Foo on remote OH need to be published to local OH and all commands on Foo’s proxy on local OH need to be published to remote OH.
Remote Foo updates → Event Bus → Local Foo updates
Remote Foo commands ← Event Bus ← Local Foo commands
When not to use the Event Bus
When you want to be able to process commands on both instances of an Item on both instances of OH. For example, if we have Item Foo exposed on the event bus and when a command is issued on Foo on remote OH, local OH needs to receive that command too and when a command is issued on Foo on local OH, Foo on remote OH needs to receive that command too. In that case, a straight forward configuration of the Event Bus will result in an infinite loop.
Remote Foo commands → Event bus → Local Foo commands → Event Bus → Remote Foo commands …
Parts of the Event Bus and General Flow
The event bus will have three parts, the subscription, the publishing, and online status reporting.
We will use the following conventions:
topic | purpose |
---|---|
<openHAB name>/out/<Item>/command |
Topic where all commands that occur on <openHAB name> are published |
<openHAB name>/out/<Item>/state |
Topic where all state updates that occur on <openHAB name> are published |
<openHAB name>/in/<Item>/command |
Topic where commands that occur in another service are published that should be reflected in <openHAB name> . |
<openHAB name>/in/<Item>/state |
Topic where state updates that occur in another service are published that should be reflected in <openHAB name>.
|
<openHAB name>
is a name you chose to uniquely identify each openHAB instance. <Item>
will be replaced with the Item name.
Note that you will want to use the same name for all Items that need to be synchronized across the OH instances. In other words, if you have an Item Foo linked to a device on a remote openHAB instance, you need an Item of the same type named Foo on your local openHAB instance.
When integrating multiuple openHAB instances each openHAB instance has four topics for each Item, but you do not use all four topics all of the time. Typically you will only use one or two of the topics at most for any given openHAB instance. In short, an openHAB instance will publish to it’s own out topics but subscribe to another openHAB instance’s out topics. The in topics are only used when integrating with a third party service.
Take the first scenario above where remote OH has an Item named Foo that we want to proxy on local OH. One approach that one might use is:
Instance | Event | Pub/Sub | Topic |
---|---|---|---|
local | command | publish | local/out/Foo/command |
local | update | subscribe | remote/out/Foo/state |
remote | command | subscribe | local/out/Foo/command |
remote | update | publish | remote/out/Foo/state |
Let’s assume there is a command on Foo on local. The data flow will be:
- command received by Foo on local OH
- command is published to
local/out/Foo/command
- command is received by remote OH and triggers the event bus subscription Rule (see below)
- remote OH Rule calls
sendCommand("Foo", "CMD")
- eventually Foo may receive and update as a result of the command
- remote OH publish Rule triggers as a result of the update and publishes the update to
remote/out/Foo/state
- local OH receives the update and triggers the event bus subscription Rule (see below)
- local OH calls
postUpdate("Foo", "UPD")
This will be the typical configuration when one wants to proxy one or more Items from one OH instance to another. The topics published to and subscribed from for an Item will be those topics associated with the OH instance that has the “real” Item, e.g. the Item linked to the device. In this case, remote owns Foo so it is remote’s topics that are used to get updates.
Another common configuration would be where the remote OH instance only has sensors that it publishes to other OH instances. In this case the recommended approach is to follow the same overall approach as above and just exclude the command topic, i.e. steps 5-8 above. In other words, remote OH publishes updates to remote/out/Foo/state
.
Note that these two use cases are not mutually exclusive. It’s to be expected that there will be some Items that will follow the first scenario and other Items which follow the second in one deployment.
Also note that the concept of “leader/follower” is not being used here. There is no leader (a.k.a. master) or follower (a.k.a. slave) instance of OH. There is the Item owner, in this case remote, and the proxy, in this case local. And the role is determined on an Item by Item basis. So some Items might be owned by local and others might be owned by remote.
Finally, these are not the only configurations one can create with the Event Bus.
-
One can set up a start configuration where there is one main OH instance that is subscribing to events from remote OH and publishing commands to all the remote OH instances. In that case the recommended approach is to use the main openHAB instance’s Event Bus topics. All the remotes will publish to
mainopenhab/in/<item>/state
and all will subscribe tomainopenhab/out/<item>/command
. -
When integrating with an external service, that service will subscribe to the out topics and publish to the in topics.
MQTT Installation and Configuration
MQTT Broker
To work with MQTT one must install an MQTT Broker (the middle bubble in the data flow diagram above). Publishers send messages to topics on the broker and subscribers receive messages published to topics on the broker.
There are a number of options for brokers. For this tutorial the recommendation is to use Mosquitto.
There only needs to be one MQTT broker installed on your LAN. All openHAB instances must be able to see the machine and MQTT broker port over the network.
Mosquitto is very light weight and runs very well on an RPi along side openHAB.
Installation: openHABian
Mosquitto can be installed from openhabian-config
→ 20 Optional Components → 23 Mosquitto. openhabian-config will ask if you want to secure connectivity to the broker with a password.
Installation: Direct
Mosquitto is included with most Linux Distros. Use the package manager for your distro (e.g. apt
, yum
, etc.) to install mosquitto and
mosquitto-clients`.
sudo apt install mosquitto mosquitto-clients
On Windows you can download an installer from Download | Eclipse Mosquitto
On Mac mosquitto is available through Homebrew. See https://brew.sh/.
For those distros that support snap
, Mosquitto is available as a a snap.
There are other ways to install Mosquitto that are beyond the scope of this tutorial, such as Docker. Please see the Mosquitto docs for details.
MQTT Broker Configuration
These instructions assume the default configuration of Mosquitto so no additional configuration is required. If desired, a username/password can be set.
-
run
mosquitto_passwd -b /etc/mosquitto/passwd <user> <password>
-
Add the following lines to
/etc/mosquitto/mosquitto.conf
:password_file /etc/mosquitto/passwd
allow_anonymous false
On openHABian this is done for you when you supply a password when asked. Where relevant, these instructions will assume openhabian
as the user and password
as the password.
openHAB Binding Installation and Base Configuration
See Adding Things - Advanced | openHAB for instructions on installing the MQTT binding and creating the MQTT Broker Thing.
Before saving the MQTT Broker Thing, fill in the following fields. Any field not in the list should be left blank or set to the default value shown. You may need to check the “Show Advanced” checkbox to see all the fields.
Field | Value | Notes |
---|---|---|
Name | <Something Meaningful> |
This is the name that the Thing will appear as in the List of Things, e.g. Mosquitto Broker
|
Thing ID | <Something Meaningful> |
E.g. mosquitto
|
Broker Hostname/IP | <Hostname or IP address> |
The machine running the MQTT broker; can be localhost or 127.0.0.1 if installed on the same machine as this instance of openHAB |
Client ID | <client id> |
Uniquely identifies the OH instance to the MQTT broker, each client must have a unique client ID. If left blank, a random string is generated. This is not the same as the username. Should be set to the name for this OH instance. In the above discussion remote and local are used. |
Last Will Message | OFFLINE |
Message sent to the Last Will Topic when this instance of OH loses it’s connection to the MQTT broker |
Last Will Topic | <client id>/status |
Where the ONLINE/OFFLINE status gets’ published to. <client id> will be the name of this instance of openHAB and be the root of the MQTT topic hierarchy. |
Last Will Retain | ON | This causes the (LWT) message to be retained so any client that subscribes to the topic will always know the ONLINE/OFFLINE status of this OH instance |
Username | openhabian |
Username configured on the broker, blank if one was not set |
Password | password |
Password configured on the broker, blank if one was not set |
Repeat these steps on all openHAB instances that will participate with the Event Bus. Use a unique <client id>
for each.
Rules
The rules for the MQTT Event Bus are published to the Marketplace (available on OH 3.2 M2 or later).
There are text based Python versions of these rules published at GitHub - rkoshak/openhab-rules-tools: Library functions, classes, and examples to reuse in the development of new Rules.. This code has not been tested with OH 3 but is known to work with OH 2.5.
Installation
In MainUI navigate to Settings → Automation.
Find and install “MQTT Online Status”, “MQTT Event Bus Subscription”, and “MQTT Event Bus Publication”. You may need to click “Show All” to see them all.
Configuration
What you have installed are rule templates. Templates can be instantiated one or more times to become rules. You will want to instantiate at least two of these rules, depending on the scenario.
Online Status
In MainUI from Settings → Rules click the + icon to create a new rule.
Fill in reasonable values for Unique ID, Name, and Description.
Select “Publishes ONLINE to LWT Topic” as the template to use for this rule.
Select your Broker Thing (created above) from the list for “MQTT Broker Thing”
Type in the LWT topic configured on the MQTT Broker Thing (see above).
Click “Save” and run the rule manually by clicking the triangle play button to get that first “ONLINE” message posted and test out the new rule.
Subscribe
In MainUI from Settings → Things open your MQTT Broker Thing and click on the “Channels” tab.
Click on “Add Channel” and configure it with a wild card subscription for the root topics on the event bus where updates and commands will come from. The specific topic will depend on the scenario this event bus is being used for.
In the example in the screen shot below I’m subscribing to sensor Items on an openHAB instance running at my dad’s house. So dadsoh
“owns” the sensors meaning my “main” openHAB subscribes for updates.
Put a #
into the “Separator Character” field.
Now navigate to Settings → Rules and click the + icon to create a new rule.
Enter reasonable information for the Unique ID, Name, and Description.
Select “MQTT Event Bus Subscribe” from the list of templates.
Select the MQTT Broker Thing (created above) from the list for the “MQTT Broker Thing” and the Event Channel created above for the “MQTT Event Channel”.
Click “Save”.
If one is creating a complicated deployment where there are multiple event bus topics to subscribe to, create multiple Event Channels on the MQTT Broker Thing and add that as a new trigger for the subscription rule.
Publish
Create a Group with a reasonable name which will have as members all those Items that should have their commands published to the event bus. I used EB_Command
. Add all the proxy Items that are “owned” by another instance of OH to this Group.
Create another Group with a reasonable name which will have as members all those Items that should have their updates published to the event bus. I used EB_Update`. Add all the Items owned by this instance of openHAB that should have their updates published to the event bus to this Group.
No Items should be members of both Groups.
Now navigate to Settings → Rules and click the + icon to create a new rule.
Enter reasonable information for Unique ID, Name, and Description.
Select “MQTT Event Bus Publisher” from the list of templates.
Select the MQTT Broker thing for “MQTT Broker Thing” (created above).
Enter the name for this openHAB instance which is used as the root of the MQTT topic.
Select the command Item Group created above for “Command Group” and the updates Item Group created above for the “Update Group”.
Click “Save”.
Do I need all three?
In very simple cases you may only need two of the three rules instantiated. For example, I’ve only got some sensors deployed to my dad’s house so that instance of openHAB has the online status rule and the publisher rule instantiated. My main openHAB only has the subscription rule instantiated and the broker is configured to subscribe to my dad’s event bus out topics.
Example Topologies
This section presents some example topologies that are possible with the MQTT Event Bus. The topologies described here is not exhaustive and are presented for illustration purposes.
Leader/Follower
In this topology there are two openHAB instances, one of which is a leader and the other is the follower. The leader has all the rules and is the primary openHAB instance. The follower “owns” the Items and implements the links to the actual devices. All the Items from the follower exist as proxies in the leader.
In this topology, all commands to the proxied Items in the leader need to be published to the follower. All updates to the “real” Items in the follower need to be published to the leader.
openHAB Instance | Subscribe topics | Publish topics | Command Group | Update Group |
---|---|---|---|---|
leader | follower/out/# |
leader/out/# |
All proxy Items that can be commanded. | None |
follower | leader/out/# |
follower/out/# |
None | All Items proxied to the leader. |
One-to-One
This scenario is more complicated than Leader/Follower in that which instance of openHAB owns the real Item versus the proxy Item can be either openHAB instance. Therefore the Items need to be individually Grouped properly.
openHAB Instance | Subscribe topics | Publish topics | Command Group | Update Group |
---|---|---|---|---|
one | two/out/# |
one/out/# |
All Items owned by “two” and proxied by “one” and can be commanded. | All Items owned by “one” that are to be published to the event bus. |
two | one/out/# |
two/out/# |
All Items owned by “one” and proxied by “two” and can be commanded. | All Items owned by “two” that are to be published to the event bus. |
Star Topology
In this topology there is one central main openHAB instance with multiple satellite openHAB instances. The “real” Items are all owned by the satellite instances and are proxied in main. Note that all the Items in the satellites need to have a unique name across all the satellite instances.
openHAB Instance | Subscribe topics | Publish topics | Command Group | Update Group |
---|---|---|---|---|
main | main/in/# |
main/out/# |
All Items proxied from the satellite instances that can be commanded from main. | None |
sat_one | main/out/# |
main/in/# |
None | All Items proxied to the event bus. |
sat_two | main/out/# |
main/in/# |
None | All Items proxied to the event bus. |
sat_three | main/out/# |
main/in/# |
None | All Items proxied to the event bus. |
Notice how the satellite instances are all configured the same. No commands ever come from the satellite instances. No updates come from the main instance.
The code below is deprecated. Once the Marketplace hits the main release version I will be removing everything below this point. It will be preserved at my repo for some time after that but the Rule Templates will be the only code that is maintained by the author of this tutorial going forward.
These instructions assume the MQTT binding configuration discussed above has already been performed. With the MQTT Broker Thing configured with the proper event Channel, edit $OH_CONF/automation/lib/python/configuration.py
and add the following new variables:
Variable | Purpose |
---|---|
mqtt_eb_name |
Name for the instance of openHAB, will be used as the root of the MQTT topic hierarchy for the Publisher. |
mqtt_eb_broker |
Thing ID for the MQTT Broker Thing. |
mqtt_eb_puball |
Optional variable, when absent or set to True indicates that all command and updates to all Items are to be published to the event bus. When False , only those Items with the eb_update and eb_command tags are published. |
mqtt_eb_in_chan |
The Channel ID for the Event Channel on the Broker Thing. |
For example (also contained in configuration.py.mqtt_eb_example
):
# The Channel ID of the MQTT Event Bus subscription channel
mqtt_eb_in_chan = "mqtt:broker:broker:eventbus"
# The name to use for this instance of openHAB, forms the root of the MQTT topic
# hierarchy.
mqtt_eb_name = "remote-openhab"
# Thing ID for the MQTT broker Thing.
mqtt_eb_broker = "mqtt:broker:mosquitto"
# Optional flag, when True all Item commands and updates are puiblished.
# Defaults to True.
mqtt_eb_puball = True
If the mqtt_eb_puball
variable is left out or set to False
, the next step is to add the following tags to only those Items that should publish to the event bus.
Tag | Purpose |
---|---|
eb_update |
Items tagged with this will have all their updates published to the event bus. |
eb_command |
Items tagged with this will have all ther command published to the event bus. |
For example:
Switch Foo [eb_command] // publishes only commands to the event bus
String Bar [eb_update] // publishes only updates to the event bus
Number Baz [eb_command,eb_update] // publishes both command and updates to the event bus
Rules DSL
The Event Bus requires the installation of a few short Rules. These Rules do not need to be customized. They work as is so they only need to be copied to the right location in the openHAB installation.
Conventions
For the code below, we will be using the following variables:
Variable | Value | Purpose |
---|---|---|
eb_cmd_gr |
PubItems_CMD |
Group containing all the Items whose commands are published to the bus. |
eb_upd_gr |
PubItems_UPD |
Group containing all the Items whose updates are published to the bus. |
eb_name |
remote |
Name for the publishing OH instance, will be the root of the topics. |
eb_br |
mqtt:broker:mosquitto |
Thing ID for the MQTT Broker Thing created above |
eb_in_chan |
mqtt:broker:mosquitto:eventbus |
Channel ID for the Trigger Channel defined on the MQTT Broker Thing for the OH instance that subscribes to the event bus (see below) |
Publishing to the Event Bus
Items and Groups
The first step to publish commands and updates to the out topics is to create a Group for each. Be sure to give the Group a Type. It doesn’t matter what type we choose because we will not be aggregating the states of it’s members, but without a Type the Group will not receive events that are necessary to drive Rules.
Group:String PubItems_CMD
Group:String PubItems_UPD
All Items that need to be published to the “event bus” need to be a member of these Groups. For example, on the remote
openHAB instance, all of the “real” Items (those linked to devices) must be members of PubItems_UPD
so all updates on the Items get shared with the local
openHAB instance. On the local
openHAB instance where the proxies reside, the Proxy Items must be members of PubItems_CMD
so all commands issued on the proxy Item get published out and acted upon by the remote
openHAB instance.
Take care when adding the Items to the Groups to follow this convention.
Rules
Next we will need Rules. Copy these to $OH_CONF/rules/eb_pub.rules
and modify at a minimum eb_name
. If a custom value was used for anything else modify the code as appropriate.
eventbus_pub.rules
val eb_br = "mqtt:broker:mosquitto" // Thing ID of the broker connection
val eb_name = "remote" // <client ID>
rule "Publish commands to the event bus"
when
Member of PubItems_CMD received command // eb_cmd_gr
then
val mqttActions = getActions("mqtt",eb_br)
mqttActions.publishMQTT(eb_name+"/out/"+triggeringItem.name+"/command",receivedCommand.toString)
end
rule "Publish updates to the event bus"
when
Member of PubItems_UPD received update // eb_upd_gr
then
val mqttActions = getActions("mqtt",eb_br)
mqttActions.publishMQTT(eb_name+"/out/"+triggeringItem.name+"/state",triggeringItem.state.toString)
end
Repeat for all OH instances.
Summary
That’s all there is to it. All commands to all members of PubItems_CMD get published to the command topic and all updates get published to the state topic.
Subscribing from the Event Bus
Items and Groups
For every Item that needs to be proxied the subscriber must have an Item of the same name and type defined. For example, if there is a Switch Foo
linked to a device on remote, there needs to be a Switch Foo
defined. Linking an incoming message to an Item is done based on Item name.
Setting up the Subscriptions
For subscription we need to create a publish trigger Channel on the MQTT Broker Thing. This subscription will be subscribing to the desired out
topics on the publishing instance of OH. MQTT and configure it to subscribe to the output topics from the other openHAB. You can use the wild card subscription to use this one Channel for all the Items. Be sure to define a separator character so we can get the MQTT topic from the event in our Rule.
In PaperUI navigate to the MQTT Broker Thing. Click on the + icon to add a Channel and enter:
Field | Value | Notes |
---|---|---|
Channel Type | Publish Trigger | The only choice |
Channel ID |
<client-ID>cmd , upd |
Choose a meaningful name indicating what OH instance the Channel subscribes from (e.g. in this case on remote we subscribe to commands from local so use local-cmd . |
Label | Human readable name for the Channel | Spell out what this Channel does, e.g. “Local’s Commands” |
MQTT Topic | On the local openHAB subscribe to remote/out/+/state , On the remote openHAB subscribe to local/out/+/command
|
If you want to subscribe to both commands and updates from remote subscribe to remote/out/# . |
Separator Character | # |
Triggers the Rule with and event consisting of topic#message . |
If you have more than one “remote” OH instances, create a Channel for each instance. Alternatively, if this openHAB instance doesn’t publish anything to the Event Bus (e.g. the remote OH instances only publish sensor readings) you can subscribe to +/out/#
which will subscribe to all events from all other instances of OH. If you do that and do publish for the local instance of OH, an infinite loop will result.
Rules
With the Channel in place we only need a single Rule.
If more than one trigger Channel was created above, add those additional triggers as rule triggers to the Rules.
eventbus_sub.rules
rule "Subscribe for commands and updates from the event bus"
when
Channel "mqtt:broker:broker:remote-updates" triggered
then
var topic = receivedEvent.toString.split("#").get(0)
var state = receivedEvent.toString.split("#").get(1)
val itemName = topic.split("/").get(2)
val type = topic.split("/").get(3)
if(type == "command") sendCommand(itemName, state)
else postUpdate(itemName, state)
end
The Rules DSL version has one limitation that doesn’t exist for the Python version. If the Item doesn’t exist an error will be generated on the sendCommand or postUpdate. To avoid this error make sure to define a proxy for all the Items that are published to the event bus.
Online Status
It is also desirable to know when one of the OH instances goes offline. To handle this, we configured the LWT on the MQTT Broker Thing (see above).
When the OH instance loses it’s connection to the MQTT Broker, the Broker will publish “OFFLINE” to <client ID>/status
. Since the message is retained, even if the subscribers are not online, they will get the OFFLINE message when they reconnect.
Next we need a Rule triggered at Systems started to publish “ONLINE” as a retained message when the OH instance comes back online.
eventbus_online.rules
rule "Eventbus Online"
when
System started or
Thing mqtt:broker:mosquitto changed to ONLINE
then
logInfo("mqtt-eb", "Reporting eventbus as online")
getActions("mqtt", "mqtt:broker:mosquitto").publishMQTT("openhab-remote/status", "ONLINE", true)
end