Skip to content

Commit

Permalink
finish OpenHAB tutorial
Browse files Browse the repository at this point in the history
- add DHT11 example
- revise flow of examples
- improve docs for HomieDevice class attrs
  • Loading branch information
2bndy5 committed Oct 3, 2022
1 parent 2edfa62 commit 1ede9ae
Show file tree
Hide file tree
Showing 14 changed files with 220 additions and 51 deletions.
7 changes: 5 additions & 2 deletions circuitpython_homie/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,13 @@ class HomieDevice:
the given input.
"""

#: The :homie-attr:`implementation` attribute is global to all instantiated devices.
implementation = "CircuitPython on " + uname()[0]
"""The :homie-attr:`implementation` attribute used for all `HomieDevice` instances
(class attribute). The platform specified by default is taken from
:attr:`~os._Uname.sysname`.
"""

#: The base topic used for all Homie devices.
#: The base topic used for all `HomieDevice` instances (class attribute).
base_topic = "homie"

def __init__(self, client: MQTT, name: str, device_id: str):
Expand Down
6 changes: 5 additions & 1 deletion docs/_static/extra_css.css
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ a code.oh {

code.oh-red,
a code.oh-red {
background-color: hsl(14, 100%, 50%);
background-color: hsl(3, 100%, 59%);
}

code.oh-orange {
background-color: hsl(14, 80%, 50%);
}

code.oh-green {
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/_static/tutorial_images/homie_thing-dark.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ Demonstrates using the `PropertyPercent` class to represent a light sensor's dat
:start-after: pylint: disable=import-error
:lineno-match:

DHT Sensor test
-----------------

Demonstrates using the `PropertyPercent` and `PropertyFloat` classes to represent a DHT11 sensor's
humidity and temperature data (respectively).

.. literalinclude:: ../examples/homie_dht_sensor_test.py
:caption: examples/homie_dht_sensor_test.py
:linenos:
:start-after: pylint: disable=import-error
:lineno-match:

Clock test
------------

Expand Down
129 changes: 95 additions & 34 deletions docs/tutorials/openhab.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@
:class: oh-green oh
.. role:: oh-blue(literal)
:class: oh-blue oh
.. role:: oh-orange(literal)
:class: oh-orange oh

.. _MQTT binding: https://www.openhab.org/addons/bindings/mqtt/
.. _MQTT Homie binding: https://www.openhab.org/addons/bindings/mqtt.homie/
.. _Simple test: ../examples.html#simple-test
.. |click| replace:: Click or tap
.. |oh-thing| replace:: OpenHAB Thing
.. |oh-item| replace:: OpenHAB Item
.. |homie-dev| replace:: Homie Device

Using Homie with OpenHAB_
=========================
Expand Down Expand Up @@ -37,10 +47,6 @@ the interface. The rest of this tutorial will assume that you are logged into th
with an OpenHAB administrator account. This should have been covered in the
`OpenHAB Getting Started instructions`_

.. _MQTT binding: https://www.openhab.org/addons/bindings/mqtt/
.. _MQTT Homie binding: https://www.openhab.org/addons/bindings/mqtt.homie/
.. |click| replace:: Click or tap

.. tip::
Some of the images here are hyperlinked to the http://openhabian:8080 domain for quicker access.
If you are using a different hostname or a static IP address, then you can adjust the address in
Expand Down Expand Up @@ -70,38 +76,38 @@ you don't see the `MQTT binding`_.
|click| the :homie-val:`install` button to install the binding and add MQTT capability to your
OpenHAB server.

.. md-admonition::
:class: info

`Installing the MQTT binding`_ in OpenHAB will also install Homie support automatically. More info
about Homie support can be found at the `MQTT Homie binding`_ page.

.. md-admonition::
:class: missing
`Installing the MQTT binding`_ in OpenHAB will also install Homie support automatically. More info
about Homie support can be found at the `MQTT Homie binding`_ page.

The OpenHAB `MQTT Homie binding`_ will say that it supports Homie v3.x specifications. This library
implements Homie v4 specifications. Homie v4 is mostly backward compatible with Homie v3 with
the following exceptions:
.. admonition:: Homie v3 vs Homie v4
:class: missing
:name: v3-vs-v4

- `Node Arrays <https://homieiot.github.io/specification/spec-core-v3_0_1/#arrays>`_
are not supported in Homie v4
- `Device Statistics <https://homieiot.github.io/specification/spec-core-v3_0_1/#device-statistics>`_
are not supported in Homie v4
The OpenHAB `MQTT Homie binding`_ will say that it supports Homie v3.x specifications. This library
implements Homie v4 specifications. Homie v4 is mostly backward compatible with Homie v3 with
the following exceptions:

These missing features are memory and process intensive for microcontrollers. At this time,
there are no plans to add Homie v3 support for this library.
- `Node Arrays <https://homieiot.github.io/specification/spec-core-v3_0_1/#arrays>`_
are not supported in Homie v4. Incidentally, Arrays aren't implemented in OpenHAB's
`MQTT Homie binding`_ because the Homie specification was too vague which is why it was
removed in Homie v4 (see `this HomieIoT thread comment
<https://github.com/homieiot/convention/issues/90#issuecomment-385425001>`_).
- `Device Statistics <https://homieiot.github.io/specification/spec-core-v3_0_1/#device-statistics>`_
are not supported in Homie v4. This was removed in Homie v4 in favor of using nodes' properties
(see `this HomieIoT discussion <https://github.com/homieiot/convention/issues/102>`_).

.. |OH-thing| replace:: OpenHAB Thing
These missing features are memory and process intensive for microcontrollers. At this time,
there is no plan to add Homie v3 support for this library.

.. _add_broker_as_thing:

Adding the MQTT broker as an |OH-thing|
Adding the MQTT broker as an |oh-thing|
***************************************

After `Installing the MQTT binding`_, navigate back to the settings page and open
`the "Things" category <http://openhabian:8080/settings/things/>`_. You may think that installing
the MQTT binding didn't change anything, but automatic discovery of MQTT-capable devices still
requires an |OH-thing| to represent the MQTT broker.
requires an |oh-thing| to represent the MQTT broker.

.. |OH_plus| replace:: :oh-blue:`+`
.. _OH_plus: http://openhabian:8080/settings/things/add
Expand All @@ -116,7 +122,7 @@ requires an |OH-thing| to represent the MQTT broker.
.. image:: ../_static/tutorial_images/mqtt_binding_thing-dark.png
:class: only-dark align-center
:target: http://openhabian:8080/settings/things/mqtt
3. At the top of the list of options that you can add as |OH-thing|\ s, you should see the MQTT broker option.
3. At the top of the list of options that you can add as |oh-thing|\ s, you should see the MQTT broker option.
It will have a badge on it that says :oh-blue:`Bridge`. |click| on the MQTT broker option.

.. image:: ../_static/tutorial_images/mqtt_broker_thing-light.png
Expand Down Expand Up @@ -175,25 +181,80 @@ requires an |OH-thing| to represent the MQTT broker.
Hover your mouse (or tap and hold) over the :oh-red:`ERROR` badge to see a tooltip briefly
explaining the reason for the error.

Adding a Homie Device as an |OH-thing|
Adding a |homie-dev| as an |oh-thing|
-----------------------------------------

Once you have finished :ref:`add_broker_as_thing`, you are now ready to start using OpenHAB's automatic
discovery of Homie Devices. This section should be repeated for any instantiated `HomieDevice`
discovery of |homie-dev|\ s. This section should be repeated for any instantiated `HomieDevice`
object.

.. note::
Once completed, there is no need to repeat these steps again for the same `HomieDevice` object
unless you have changed the ``device_id`` parameter to the `HomieDevice` constructor. Connecting
& disconnecting a Homie Device that are already added as |OH-thing|\ s should be automatically
& disconnecting a |homie-dev| that are already added as |oh-thing|\ s should be automatically
handled by the OpenHAB `MQTT Homie binding`_.

First lets get a library example running on a circuitPython enabled board (with WiFi support).
See the :doc:`../examples` to understand how to run a library example. For this tutorial, we'll be
using the `Simple test <../examples.html#simple-test>`_ example.
using the `Simple test`_ example.

Once you've got an example running on your circuitpython board, The `HomieDevice` must be added to
OpenHAB as an |oh-thing|. The `HomieProperty` values are used in OpenHAB as a |oh-item|, and each
|oh-item| must be "linked" to an |oh-thing|'s "channel"

1. To see any new Homie devices discovered by the MQTT binding, navigate to
`Settings -> Things <http://openhabian:8080/settings/things/>`_. |click| on the notification
titled :oh-red:`Inbox` at the bottom of the screen.
2. You should see your new `HomieDevice` listed by it's ``device-_id`` (a required parameter in the
`HomieDevice` constructor).

.. image:: ../_static/tutorial_images/discovered_thing-light.png
:class: only-light align-center
.. image:: ../_static/tutorial_images/discovered_thing-dark.png
:class: only-dark align-center

|click| on the discovered |homie-dev| and select :homie-dev:`Add as Thing` from the pop-up menu.
It will ask you for a customized name to be assigned to the |oh-thing|. By default it will use
the ``device_id`` if not changed, so this step is optional. |click| the :oh-orange:`OK` button
when done.
3. You should now see the |homie-dev| in your list of |oh-thing|\ s.

.. image:: ../_static/tutorial_images/homie_thing-light.png
:class: only-light align-center
.. image:: ../_static/tutorial_images/homie_thing-dark.png
:class: only-dark align-center

Once you've got an example running, head over to you OpenHAB dashboard: http://openhabian:8080
(you may have to replace the hostname ``openhabian`` with the IP address like ``192.168.1.xxx``).
To see any new Homie devices discovered by the MQTT binding, navigate to "Settings" -> "Things"
(http://openhabian:8080/settings/things/). At the bottom of the screen, you should see badge-like
notification titled "Inbox".
To use this |homie-dev|'s properties in the OpenHAB user interfaces, you need to create an
|oh-item| for each |homie-dev| property (programmatically instantiated with `HomieProperty` or
:doc:`one of its derivatives <../API/recipes>`). Each |oh-item| must be linked to a |homie-dev|
property via an |oh-thing|'s channel(s).

To see the channels, navigate to the configuration of the |oh-thing| that represents your
|homie-dev| (in your list of |oh-thing|\ s). |click| on the tab named ``Channels`` at the top of
the page.

You should now see a list of properties belonging to your |homie-dev|. Using the `Simple test`_
example, this list only has a ``color`` property. There are various ways to create |oh-item|\ s
from the |oh-thing|'s ``Channels`` configuration page. Choosing 1 will depend on how you wish to
craft your OpenHAB User Interface, Dashboard, or Sitemap.

- |click| on an available channel and select ``Add link to Item...``, then select
``Create a new Item``. This will create a single |oh-item|, but the item's ID must be unique in
OpenHAB (cannot reuse the same ID for multiple |oh-item|\ s linked to the same |oh-thing|'s
channel). While this is the most flexible, it can also become the most tedious.
- |click| on the button titled :homie-val:`Add points to Model`. This will create the necessary
|oh-item|\ (s) and link them to the respective property's channel.
- |click| on the button titled :homie-val:`Add Equipment to Model`. This is similar to
:homie-val:`Add points to Model`, however the created |oh-item|\ (s) are put into a group that
represents a category of equipment.

.. admonition:: Going Forward
:class: check

It is important to understand `OpenHAB's Semantic Model
<https://www.openhab.org/docs/tutorial/model.html>`_ and how they can be used when crafting
a User Interface.

This tutorial does not cover how to use OpenHAB in general. The main point of this tutorial
is how to use the CircuitPython_Homie library for automatic discovery of DIY devices in
OpenHAB.
6 changes: 3 additions & 3 deletions examples/homie_button_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ def change_state(client: MQTT, topic: str, message: str):
def on_disconnected(client: MQTT, user_data, rc):
"""Callback invoked when connection to broker is terminated."""
print("Reconnecting to the broker.")
client.connect()
device.begin()
client.reconnect()
device.set_state("ready")


mqtt_client.on_disconnect = on_disconnected
Expand Down Expand Up @@ -111,4 +111,4 @@ def on_disconnected(client: MQTT, user_data, rc):
except KeyboardInterrupt:
device.set_state("disconnected")
mqtt_client.on_disconnect = lambda *args: print("Disconnected from broker")
mqtt_client.disconnect()
mqtt_client.deinit()
6 changes: 3 additions & 3 deletions examples/homie_clock_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@
def on_disconnected(client: MQTT, user_data, rc):
"""Callback invoked when connection to broker is terminated."""
print("Reconnecting to the broker.")
client.connect()
device.begin()
client.reconnect()
device.set_state("ready")


mqtt_client.on_disconnect = on_disconnected
Expand All @@ -101,4 +101,4 @@ def on_disconnected(client: MQTT, user_data, rc):
device.set_state("disconnected")
print() # move cursor to next line
mqtt_client.on_disconnect = lambda *args: print("Disconnected from broker")
mqtt_client.disconnect()
mqtt_client.deinit()
88 changes: 88 additions & 0 deletions examples/homie_dht_sensor_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""A simple example of broadcasting a DHT11 sensor's data.
This was tested on a Adafruit QtPy ESP32-S2.
"""
# pylint: disable=import-error,no-member,unused-argument,invalid-name
import time
import board
import socketpool # type: ignore
import wifi # type: ignore
from adafruit_minimqtt.adafruit_minimqtt import MQTT, MMQTTException
import adafruit_dht
from circuitpython_homie import HomieDevice, HomieNode
from circuitpython_homie.recipes import PropertyFloat, PropertyPercent

# Get wifi details and more from a secrets.py file
try:
from secrets import wifi_settings, mqtt_settings
except ImportError as exc:
raise RuntimeError(
"WiFi and MQTT secrets are kept in secrets.py, please add them there!"
) from exc

print("Connecting to", wifi_settings["ssid"])
wifi.radio.connect(**wifi_settings)
print("Connected successfully!")
print("My IP address is", wifi.radio.ipv4_address)

print("Using MQTT broker: {}:{}".format(mqtt_settings["broker"], mqtt_settings["port"]))
pool = socketpool.SocketPool(wifi.radio)
mqtt_client = MQTT(**mqtt_settings, socket_pool=pool)

# create a light_sensor object for analog readings
dht_in = board.A3 # change this accordingly
dht_sensor = adafruit_dht.DHT11(dht_in)
dht_sensor.measure() # update current data for Homie init

# create the objects that describe our device
device = HomieDevice(mqtt_client, "my device name", "lib-dht-sensor-test-id")
dht_node = HomieNode("DHT11", "temperature/humidity sensor")
dht_temperature_property = PropertyFloat(
"temperature", init_value=dht_sensor.temperature, unit="°F"
)
dht_humidity_property = PropertyPercent(
"humidity", datatype="integer", init_value=dht_sensor.humidity
)

# append the objects to the device's attributes
dht_node.properties.extend([dht_temperature_property, dht_humidity_property])
device.nodes.append(dht_node)


def on_disconnected(client: MQTT, user_data, rc):
"""Callback invoked when connection to broker is terminated."""
print("Reconnecting to the broker.")
client.reconnect()
device.set_state("ready")


mqtt_client.on_disconnect = on_disconnected
mqtt_client.on_connect = lambda *args: print("Connected to the MQTT broker!")

# connect to the broker and publish/subscribe the device's topics
device.begin()

# a forever loop
try:
refresh_last = time.time()
while True:
try:
now = time.time()
if now - refresh_last > 2: # refresh every 2 seconds
refresh_last = now
mqtt_client.loop()
dht_sensor.measure() # update current data
temp = device.set_property(
dht_temperature_property, dht_sensor.temperature
)
print("Temp:", temp, dht_temperature_property.unit, end=" ")
humid = device.set_property(dht_humidity_property, dht_sensor.humidity)
print("Humidity:", humid, dht_humidity_property.unit, end="\r")
except MMQTTException:
print("\n!!! Connection with broker is lost.")
except KeyboardInterrupt:
device.set_state("disconnected")
print() # move cursor to next line
mqtt_client.on_disconnect = lambda *args: print("Disconnected from broker")
mqtt_client.deinit()
dht_sensor.exit()
10 changes: 5 additions & 5 deletions examples/homie_light_sensor_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,20 @@
ambient_light_node.properties.append(ambient_light_property)
device.nodes.append(ambient_light_node)

# connect to the broker and publish/subscribe the device's topics
device.begin()


def on_disconnected(client: MQTT, user_data, rc):
"""Callback invoked when connection to broker is terminated."""
print("Reconnecting to the broker.")
client.connect()
device.begin()
client.reconnect()
device.set_state("ready")


mqtt_client.on_disconnect = on_disconnected
mqtt_client.on_connect = lambda *args: print("Connected to the MQTT broker!")

# connect to the broker and publish/subscribe the device's topics
device.begin()

# a forever loop
try:
refresh_last = time.time()
Expand Down
6 changes: 3 additions & 3 deletions examples/homie_simpletest.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ def change_color(client: MQTT, topic: str, message: str):
def on_disconnected(client: MQTT, user_data, rc):
"""Callback invoked when connection to broker is terminated."""
print("Reconnecting to the broker.")
client.connect()
device.begin()
client.reconnect()
device.set_state("ready")


mqtt_client.on_disconnect = on_disconnected
Expand All @@ -93,4 +93,4 @@ def on_disconnected(client: MQTT, user_data, rc):
except KeyboardInterrupt:
device.set_state("disconnected")
mqtt_client.on_disconnect = lambda *args: print("Disconnected from broker")
mqtt_client.disconnect()
mqtt_client.deinit()

0 comments on commit 1ede9ae

Please sign in to comment.