There is now a Remote openHAB binding which replaces this capability. See https://github.com/openhab/openhab-addons/tree/main/bundles/org.openhab.binding.remoteopenhab
Update: Corrected a mistake in the table that describes the publish/subscribe topics in a typical set up.
Update: Added installation and configuration instructions for MQTT 2 binding.
Update: A significant rewrite of the text of this tutorial. Note that the code itself has not changed. The purpose of the rewrite is to make it more clear how to avoid infinite loops.
Update: Moved the code to a new repo and rewrote it to make it easier to configure and add much more error checking and reporting.
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 itâs Items synchronized across all the instances. An Event Bus is a way to synchronize the Items, also called federation.
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.
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. If you need to support this use case, the Event Bus code presented here would need to be modified to prevent the loop, which is outside the scope of this tutorial.
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 openHAB instance are published that should be reflected in <openHAB name> . |
<openHAB name>/in/<Item>/state |
Topic where state updates that occur in another openHAB instance 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.
Thus, 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. 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âs remoteâs topics that are used.
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.
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. See the readme at the github repo (linked below) for others.
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. Do not use the embedded MQTT Broker (found under the Misc tab in PaperUI). The embedded broker has some problems that some users experience and appears to no longer be maintained by the upstream provider.
There only needs to be one MQTT broker installed. 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.
Installtion: 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. The following instructions will assume no password was supplied.
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 https://mosquitto.org/download/
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
These instructions will only show setup and configuration through PaperUI. If you are using text configurations, the assumption is you know enough to translate these instructions to text configs.
In PaperUI, navigate to Add-ons and select the Bindings tab. Search for âMQTTâ and install âMQTT Bindingâ.
Once installed we need to create a Thing that implements the connection to the broker. Navigate to the Inbox and click the + Icon.
Select MQTT from the list and then select âAdd Manuallyâ. Finally, select âMQTT Brokerâ from the list.
Fill in the following fields. Any field not in the list should be left blank or set to the default value shown. Data that is in
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
There are Python and Rules DSL versions presented here. You only need follow one of these subsections.
Python
A complete implementation of the event bus is hosted at https://github.com/rkoshak/openhab-rules-tools.
Installation
Clone or download the openhab-rules-tools repo and change directories to the openhab-rules-tools folder.
At a minimum execute the following two commands.
cp -r rules_utils/automation $OH_CONF
cp -r mqtt_eb/automation $OH_CONF
where $OH_CONF
is the path to your openHAB conf folder (when installed thatâs /etc/openhab2
). Note that two new Items will be created if they are not already present:
-
Reload_MQTT_PUB
: when an âONâ command is received, the MQTT event bus publisher will be refreshed with the updates made to the configuration -
Reload_MQTT_SUB
: when anON
command is received, the MQTT event bus publisher will be refreshed with the updates to the configuration.
Configuration
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
Example Topologies
The following assume the Python library described above is installed. You should be able to translate it to the Rules DSL version if desired.
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. All the Item 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. Therefore we will configure the event bus to use leader/out/*
and follower/out/*
as the topics.
Leaderâs configuration
The Event Channel on the broker subscribes to follower/out/*
configuration.py
:
mqtt_eb_in_chan = "mqtt:broker:broker:eventbus" // Event Channel ID that subscribes to follower/out/*
mqtt_eb_name = "leader"
mqtt_eb_broker = "mqtt:broker:mosquitto"
mqtt_eb_puball = False
All the Items that proxy those on follower need to be tagged with eb_command
.
Followerâs configuration
The Event Channel on the broker subscribes to leader/out/*
.
configuration.py
:
mqtt_eb_in_chan = "mqtt:broker:broker:eventbus" // Event Channel ID that subscribes to leader/out/*
mqtt_eb_name = "follower"
mqtt_eb_broker = "mqtt:broker:mosquitto"
mqtt_eb_puball = True
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 tagged properly with the real Item tagged with eb_command
only and the proxy tagged with eb_update
only.
Instance Oneâs configuration
The Event Channel on the broker subscribes to two/out/*
configuration.py
:
mqtt_eb_in_chan = "mqtt:broker:broker:eventbus" // Event Channel ID that subscribes to two/out/*
mqtt_eb_name = "one"
mqtt_eb_broker = "mqtt:broker:mosquitto"
mqtt_eb_puball = False
Each Item owned by one is tagged with eb_update
. Each Item owned by two that is proxied in one is tagged with eb_command
.
Instance Twoâs configuration
The Event Channel on the broker subscribes to one/out/*
configuration.py
:
mqtt_eb_in_chan = "mqtt:broker:broker:eventbus" // Event Channel ID that subscribes to one/out/*
mqtt_eb_name = "two"
mqtt_eb_broker = "mqtt:broker:mosquitto"
mqtt_eb_puball = False
Each Item owned by two is tagged with eb_update
. Each Item owned by one that is proxied in two is tagged with eb_command
.
Star Topology
In this topology there is one central main openHAB instance with multiple satelite openHAB instances. The ârealâ Items are all owned by the satelite instnaces and are proxied in main. Note, all the Items in the satelites need to have a unique name.
Mainâs configuration
The Event Channel on the broker subscribes to satelites/out/*
configuration.py
:
mqtt_eb_in_chan = "mqtt:broker:broker:eventbus" // Event Channel ID that subscribes to satelites/out/*
mqtt_eb_name = "main"
mqtt_eb_broker = "mqtt:broker:mosquitto"
mqtt_eb_puball = False
Each Item owned by one of the satelites is tagged using eb_command
.
Sateliteâs configuration
The Event Channel on the broker subscribes to main/out/*
configuration.py
:
mqtt_eb_in_chan = "mqtt:broker:broker:eventbus" // Event Channel ID that subscribes to main/out/*
mqtt_eb_name = "satelites"
mqtt_eb_broker = "mqtt:broker:mosquitto"
mqtt_eb_puball = True
All satelite instances will use the same configuration and therefore publish to the same root topics. All satelite Items are published.
Note, the Online rules will not work with this configuration as is.