This library implements a MQTT aware Home Assistant
interface for Mongoose OS. It offers a low-level API which allows users to
add entities with command, status and attributes callbacks. For more
out-of-the-box functionality, it adds a higher level API which uses a JSON
configuration file to enable lots of things: GPIO
, I2C
, SPI
sensors to
name but a few.
All types of object are configured automatically in Home Assistant, with zero configuration!
NOTE: This library is a work in progress, and considered alpha. This means that it will change over time, including backwards incompatible (breaking) changes. Until this notice is removed, the author recommends not using the library in production!
NOTE: Documentation for the higher level API and automations are not ready to be published yet, mostly because there will be a few changes to the implementation before it's ready. Please exercise patience until this notice is removed.
This API allows the creation of a node, adding objects to that node, and classes to the created objects. It then allows objects to send their status and receive command and attribute callbacks via MQTT.
Upon library initialization, a global mgos_homeassistant
object is created.
It can be returned using mgos_homeassistant_get_global()
. After adding
object and optionally class entries, (see below), config and status can
be sent for all objects using mgos_homeassistant_send_config()
and
mgos_homeassistant_send_status()
respectively.
To recursively remove all objects and their associated classes, call
mgos_homeassistant_clear()
. A higher level configuration based
construction of objects and classes is described below.
mgos_homeassistant_object_add()
creates a new object with the given name and of type. If additional JSON configuration payload is needed, a pointer to it can be provided or NULL passed. A callback for status calls is provided, and optionally a userdata pointer is passed.mgos_homeassistant_object_search()
searches the structure for an object with the given name. It returns a pointer to the object or NULL if none are found.mgos_homeassistant_object_get_userdata()
returns the provided userdata struct upon creation.mgos_homeassistant_object_add_cmd_cb()
adds a callback function for command MQTT requests, optionally using name as a suffix. Setting a command with name to NULL registers the main/cmd
command, otherwise a command with/cmd/name
is registered.mgos_homeassistant_object_add_attr_cb()
adds a callback function for attribute MQTT requests, optionally using name as a suffix. Setting an attribute with name to NULL registers the main/attr
command, otherwise a attribute with/attr/name
is registered.mgos_homeassistant_object_get_status()
assembles a status JSON structure of the object, including its classes (see below). Status elements themselves are provided by callback functions at object/class creation time.mgos_homeassistant_object_send_status()
assembles and sends an MQTT update with the status of the object. The status itself is provided by the callback given at object creation time.mgos_homeassistant_object_send_config()
sends a MQTT update with the configuration of the object (and its classes, see below).mgos_homeassistant_object_log()
sends a MQTT update with the specified logline, in JSON format. It will be sent to the/log
topic.mgos_homeassistant_object_remove()
removes the object (and its classes, see below) fromha
structure.
After creation of an object, multiple classes can be added that share one status update. For each class added, a unique classname must be provided. Then, when status is sent, each class will be called in turn and their status callback results added to the status of the parent object. This allows for sensors and things with multiple components (like a barometer containing a pressure, thermometer and hygrometer sensor all in one package) to generate three config lines with one status line.
mgos_homeassistant_object_class_add()
creates a new class under the provided object. The classname must be unique. If additional JSON configuration payload is needed, it can be optionally passed. A callback for status is provided, and will be appended to the object's status JSON structure, keyed by classname.mgos_homeassistant_object_class_send_status()
causes the class to request its parent object to send status, including this and all sibling classes.mgos_homeassistant_object_class_send_config()
causes the class to send its own config.mgos_homeassistant_object_class_remove()
removes the class from its parent object.
(TODO)
The library fully implements Home Assistant's MQTT Discovery protocol, which dictates discovery topics as:
<discovery_prefix>/<component>/[<node_id>/]<object_id>/config
Here, we map the fields to objects as follows:
<discovery_prefix>
ismos.yml
'shomeassistant.discovery_prefix
(eg.homeassistant
)<node_id>
(mandatory) ismos.yml
'sdevice_id
(eg.esp8266_C45ADA
).<component>
(mandatory) is one of the Home Assistant discoverable types, likebinary_sensor
orswitch
.<class_id>
(optional) is the<device_class>
, an attribute specific to thecomponent
(eg. classmotion
for componentbinary_sensor
).<provider>
(mandatory) is the Mongoose OS driver that is implementing the object (eg.gpio
,si7021
orbarometer
).<index>
(mandatory) is a number that is used in case multiple instances on the provider exist (eg.0
,2
,12
forgpio
).
Derived from these are:
<object_id>
is a composed string that uniquely identifies the component on the node:<provider>_<index>[_<class_id>]
.<name>
is a composed string that uniquely identifies the object in Home Assistant:<node_id>_<object_id>
. It can also be set explicitly when creating the object.
Examples of a discovery config topic:
homeassistant/switch/esp8266_C45ADA/LED/config
homeassistant/binary_sensor/esp8266_C45ADA/button/config
homeassistant/sensor/esp8266_C45ADA/barometer_0_temperature/config
homeassistant/sensor/esp8266_C45ADA/barometer_0_pressure/config
homeassistant/sensor/esp8266_C45ADA/barometer_0_humidity/config
The first two examples above do not have a device_class
setting.
The third through fifth ones have a device_class
of temperature
, pressure
and humidity
respectively. This is because in Home Assitant, each entity has
to be configured by its own unique discovery topic.
It's worth noting that while discovery topics have to be unique, the state
topics do not, and often are shared between all device_class
on the same
object_id
, for example the barometer
may (and does) combine state updates
of all three sensor readings (humidity, temperature and pressure) in one topic
update. Providers that want to combine status updates should initialize the
objects by setting their topic_prefix_use_class
to false
.
The <name>
of an object is what HA will use to key <entity_id>
off of, and
it is derived from the keys above. In order to create stable, unique, and
predictable IDs, the <node_id>
will have to be a part of the <name>
, too
(see above).
As a result of this decision the <name>
will be a 1:1 mapping to the
<entity_id>
in Home Assistant. The resulting entities for the config topics
described above are thus:
switch.esp8266_C45ADA_gpio_12
binary_sensor.esp8266_C45ADA_gpio_0
sensor.esp8266_C45ADA_barometer_0_temperature
sensor.esp8266_C45ADA_barometer_0_pressure
sensor.esp8266_C45ADA_si7021_0_humidity
For each object, a <topic_prefix>
is derived:
<topic_prefix>
is<node_id>/<component>/<name>[_<class>/]<index>
.<topics>
are then appended, eg./stat
or/cmd
.
Implementations are able to report all data on an object either in multiple messages, one-per-class, or in single JSON messages, one-forall-classes in the object. The latter is preferred. The payloads thus, are either literals or JSON messages.
Examples of MQTT topics and payloads:
esp8266_C45ADA/binary_sensor/pir0 {"motion":false}
esp8266_24538D/sensor/button {"action":"click","count":2}
esp8266_C45ADA/switch/LED {"state":"ON"}
esp8266_C45ADA/sensor/si7021_0 {"temperature":17.58,"humidity":45.5}
esp8266_C45ADA/sensor/barometer_0 {"pressure":974.40,"temperature":17.15}
The reason for using a tree hierarchy with /
delimiters here is to enable
nodes to subscribe to relevant topics like esp8266_C45ADA/switch/#
to receive
and process commands from Home Assistant.
Each object will subscribe to the <topic_prefix>/#
wildcard, and install one
or more handlers:
/stat
-- always installed. Sending an empty message to this topic will make the device send a status update for the object./cmd
-- for some object types, notably switch this topic will accept commands that change the state (for example, setting an LED or Relay on or off)./attr
-- for those objects that implement it, additional JSON attributes for the object can be queried by sending an empty message to this topic.
TODO(pim).