python-openhab-eventbus 1.0.0
An MQTT Event Bus for openHAB.
License: MIT License
Requires Python: >=3.5
Keywords: openHAB

# Python openHAB Event Bus

An MQTT Event Bus for openHAB. This Event Bus mirrors the [Item Events](https://www.openhab.org/docs/developer/utils/events.html#item-events) `ItemStateEvent` and `ItemCommandEvent` via MQTT. An `ItemStateEvent` is received when the state of an item is updated. This can be a `postUpdate` by a [Rule](https://www.openhab.org/docs/configuration/rules-dsl.html) or when something is operated on the device and the state is changed via the corresponding [Binding](https://www.openhab.org/addons/#binding). An `ItemCommandEvent` is received when a command is sent to an item via a channel. This can be a `sendCommand` in a [Rule](https://www.openhab.org/docs/configuration/rules-dsl.html) or when serving an element in the [Sitemap](https://www.openhab.org/docs/ui/sitemaps.html).

## What is an Event Bus

Generally speaking, an event bus is a design pattern for how devices can be operated. The state indicates the status of an item. This item can be, for example, a switch that is set to ON. If I switch off this device accordingly, the state will also change to OFF. The device therefore publishes the state of this switch item via a topic. To operate the device, however, a command is used. With the command OFF I give the device the command to change the state to OFF. If this device is ON, it will be switched off accordingly. With the command ON I would switch the device on again. For this the device must be able to subscribe via a topic.

A command therefore requires the change of a state. However, it should be noted that a command is not the same as a state. Commands and states are often called differently. For example, I can imagine the commands louder or quieter at a loudspeaker and as a state I get a number that indicates how loud this loudspeaker is.

The openHAB Event Bus allows to apply this design pattern to any [Item](https://www.openhab.org/docs/configuration/items.html) in openHAB. So I can operate an item via other programs or even other openHAB instances. But I can only configure that either `State` is published and `Command` is subscribed or `Command` is published and `State` is subscribed.

## Installation

You can install it by using pip:

```
python3 -m pip install python-openhab-eventbus
```

## Usage

You have to import the `EventBus` from `openhab`:

```
from openhab import EventBus
```

In the next step you have to create an `EventBus` object instance:

```
# Main function.
if __name__ == "__main__":
    event_bus = EventBus("http://<openhab_ip>:8080", "<openhab_username>", "<openhab_password>", "<mqtt_ip", <mqtt_port>, "<mqtt_username>, "<mqtt_password>", "<statePublishTopic>", "<commandPublishTopic>", "<stateSubscribeTopic>", "<commandSubscribeTopic>")
```

The program must be able to access the [REST API](https://www.openhab.org/docs/configuration/restdocs.html) of openHAB as well as the MQTT broker to be used. Both openHAB and the MQTT broker do not have to run on the same computer as the program itself. Also, openHAB and the MQTT Broker may run on different computers. However, the computers must be in the same network. As MQTT broker I use [Mosquitto](https://mosquitto.org/) myself. However, the program should be compatible with all brokers.

You have to replace `<openhab_ip>` with your openHAB IP address. `<openhab_username>` and `<openhab_password>` is needed to connect to the `REST API` of openHAB. Depending on your configuration you may not need to use a username and password. Instead of one `str` each for the username and password of your openHAB user, you have to pass `None` accordingly.

To connect to the MQTT broker, an MQTT client is required. The program uses `paho-mqtt` for this purpose. The client therefore needs an IP address to this broker and the port under which this broker can be reached. For this you have to replace `<mqtt_ip>` and `<mqtt_port>` accordingly. `<mqtt_ip>` is a `str` and `<mqtt_port>` is an `int`. `Mosquitto` as example uses the port `1883` by default.

It is possible for an MQTT broker to allow `anonymous` access. This means that one does not have to specify a user name and password. In this case `<mqtt_username>` and `<mqtt_password>` must be replaced by None. Otherwise, a `str` must be used accordingly.

The `REST API` uses `server-sent events` (`SSE`) to check whether an `ItemStateEvent` or an `ItemCommandEvent` has been triggered. If this is the case, the `State` is queried for an `ItemStateEvent` via a `GET` request for this `Item` and this is published, or the `command` for this `Item` is published for an `ItemCommandEvent`.

Whether `ItemStateEvent`'s or `ItemCommandEvent`'s are considered is determined when setting the topics for the MQTT client.

The topic `statePublishTopic` specifies a topic to be used to publish `ItemStateEvent`'s. If it is set, no `commandPublishTopic` may be specified. A `commandPublishTopic`, on the other hand, specifies a topic that publishes `ItemCommandEvent`'s. Both can be passed via a `str`. The other topic should be set to `None`.

The topic `stateSubscribeTopic` specifies a topic to be used to subscribe `State`'s of an `Item`. If it is set, no `commandSubscribeTopic` may be specified. A `commandSubscribeTopic`, on the other hand, specifies a topic that subscribes `Command`'s. Both can be passed via a `str`. The other topic should be set to `None`.

So in the end you have the following two configuration options:

```
from openhab import EventBus

# Main function.
if __name__ == "__main__":
    event_bus = EventBus("http://192.168.0.5:8080", None, None, "192.168.0.5", 1883, None, None, "openhab/${item}/state", None, None, "openhab/${item}/command")
```

And:

```
from openhab import EventBus

# Main function.
if __name__ == "__main__":
    event_bus = EventBus("http://192.168.0.11:8080", None, None, "192.168.0.5", 1883, None, None, None, "openhab/${item}/command", "openhab/${item}/state", None)
```

What would happen in this example? The openHAB instance 192.168.0.5 publishes the `State`'s of its `Item`'s. The openHAB instance 192.168.0.11 subscribes the `State`'s for its `Item`'s. If the same `Item`'s are present, both instances have the same `State`'s.

If you execute a `Command` on 192.168.0.11, this is published and also triggers a `Command` on 192.168.0.5, which means that the `State`'s will be changed and would be published from there again.

Now we have seen something else extremely important: `${item}`. This is a placeholder for the items. For each topic, this placeholder is replaced with the name of the item accordingly. It is important to pass this placeholder. Otherwise the program will not work.

How the topics look like, you can define yourself:

```
openhab/${item}/state
openhab/${item}/command
/openhab/${item}/state
/openhab/${item}/command
openhab/out/${item}/state
openhab/out/${item}/command
openhab/in/${item}/state
openhab/in/${item}/command
messages/states/${item}
messages/commands/${item}
/messages/states/${item}
/messages/commands/${item}
smarthome/${item}/state
smarthome/${item}/command
...
```

You still have to give the program a name and save it as a Python file. Let's say you name it `eventbus.py`, then you can call it like this:

```
python3 eventbus.py
```

A better suggestion is to run the program at system startup and start, stop or restart depending on openHAB. A `service` is recommended for this. Let's assume that we name the service `<event_bus.service>` and put it in `/etc/system/system/`:

```
[Unit]
Description=Python openHAB MQTT Event Bus
Requires=openhab.service
After=openhab.service
BindsTo=openhab.service
PartOf=openhab.service

[Service]
Type=simple
User=openhab
Group=openhab
UMask=002
ExecStart=/usr/bin/python3 </path/to/>eventbus.py
Restart=on-failure
RestartSec=30s

[Install]
WantedBy=openhab.service
```

Please replace `</path/to/>` with the path to your file and if you does not name your file `eventbus.py` you have to replace this too. As example it could be `/home/ubuntu/eventbus.py` If your `username` is `ubuntu` and you have placed the file in the home directory of your `ubuntu` user.

At least you have to run and enable your service:

```
sudo systemctl start event_bus.service
sudo systemctl enable event_bus.service
```