Skip to content

mongoose-os-libs/homeassistant

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Home Assistant for Mongoose OS

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.

API

Low Level API

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.

Node API

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.

Object API

  • 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) from ha structure.

Class API

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.

High Level API

(TODO)

MQTT Discovery

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> is mos.yml's homeassistant.discovery_prefix (eg. homeassistant)
  • <node_id> (mandatory) is mos.yml's device_id (eg. esp8266_C45ADA).
  • <component> (mandatory) is one of the Home Assistant discoverable types, like binary_sensor or switch.
  • <class_id> (optional) is the <device_class>, an attribute specific to the component (eg. class motion for component binary_sensor).
  • <provider> (mandatory) is the Mongoose OS driver that is implementing the object (eg. gpio,si7021 or barometer).
  • <index> (mandatory) is a number that is used in case multiple instances on the provider exist (eg. 0, 2, 12 for gpio).

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

MQTT Topics

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.

Supported Drivers

TODO(pim).