Writing a custom RF gateway component for Home Assistant

In a previous post, we built a RF to MQTT gateway using an Arduino and OpenMQTTGateway. Now it’s time to fully integrate this gateway into Home Assistant using a custom Python component.

To get more familiar with writing custom components, I found the Home assistant (Development 101) documentation very useful. It gives examples of very basic components that demonstrate simple concepts such as events, states, services and configs. I encourage you to try them all out and understand how they function. Once you got the basics, check out the Basic MQTT Example upon which my custom component is based.

It’s always easier to use a working program that does some of the required functions and adapt it to your needs. My component had to use MQTT and so adapting the MQTT Example was a good idea. Don’t waste time writing things from scratch. Just copy, adapt and get stuff working!

Motivation

I think it’s always important to make motivations clear. Why create a custom component when we could just use the built-in MQTT trigger (example below)?

- alias: Wall Switch Button 1
  trigger:
        platform: mqtt
        topic: home/433toMQTT
        payload: 5842324
  action:
    - service: notify.pushbullet
      data:
        message: Button 1 pressed!

From a software design view point, its more elegant—and kinda beautiful—to isolate the implementation detail of RF codes from the rest of the home automation system. Without this encapsulation, each automation relying on RF triggers would have to specify the RF payload.1

Using the built-in mqtt trigger causes lots of unnecessary duplication of RF codes (an implementation detail of the entity). A cleaner way of integrating this RF device is by specifying RF codes in a single location, and then using an alias representing the RF entity in automation triggers. This way, RF codes are not duplicated in triggers throughout our Home Assistant configuration.

Requirements

  • Pick up MQTT signals sent on a configurable topic and convert them to events representing a RF entity.
  • Ability to configure RF entities and their corresponding RF payloads using Home Assistant’s configuration files (not hard coded into Python component).

Example Configuration

We want to achieve the following configuration interface

configuration.yaml excerpt

mqtt_interpreter:
  topic: home/433toMQTT
  entities: !include rf_codes.yaml

rf_codes.yaml

# Wall Panel
- name: wp-btn-1
  payload: 5842324
- name: wp-btn-2
  payload: 5842322
- name: wp-btn-3
  payload: 5842321

# Door Sensors
- name: ds-front-open
  payload: 10179082
- name: ds-front-close
  payload: 10179086
- name: ds-front-tamper
  payload: 10179079

# Motion sensors
- name: mtn-living-room
  payload: 11871499

Example Automation Trigger Interface

trigger:
    platform: event
    event_type: mqtt_code
    event_data:
      name: wp-btn-1

Component Implementation

This component is based on the Basic MQTT Example component. The component listens on a configured MQTT topic for incoming messages. When a message is received, we check whether the supplied RF code is in the configuration (i.e. it is a device we care about). If it is, we retrieve the device’s alias name from the config and fire an event using the event bus, assigning the alias name to the event’s name property.

Example Our component receives a MQTT message containing the 5842324 payload. This is part of the Wall Panel switch config of wp-btn-1 (Wall panel, Button 1). The component fires an event with name: wp-btn-1, which can be picked up in automation triggers using the alias.

Python Component Code

import homeassistant.loader as loader
import logging
# The domain of your component. Should be equal to the name of your component.
DOMAIN = 'mqtt_interpreter'

# List of component names (string) your component depends upon.
DEPENDENCIES = ['mqtt']
CONF_TOPIC = 'topic'
DEFAULT_TOPIC = 'home-assistant/hello_mqtt'

def setup(hass, config):
    CONFIG = config[DOMAIN]
    _LOGGER = logging.getLogger(__name__)

    _LOGGER.info(CONFIG)
    """Set up the Hello MQTT component."""
    mqtt = loader.get_component('mqtt')
    topic = CONFIG.get('topic', DEFAULT_TOPIC)
    entity_id = 'mqtt_interpreter.last_message'
    #hass.states.set('mqtt_interpreter.config', CONFIG)
    # Listener to be called when we receive a message.
    def message_received(topic, payload, qos):
        """Handle new MQTT messages."""
#        hass.states.set(entity_id, topic+" " + payload)
        # find name of button from config
        for v in CONFIG['entities']:
            #_LOGGER.info(v['name'])
            if int(v['payload']) == int(payload):
                hass.bus.fire('mqtt_code', {
                    'name': v['name']
                })
                hass.states.set('mqtt_interpreter.button_pressed_name', v['name'])
                break
            else:
                _LOGGER.info('no match')

    # Subscribe our listener to a topic.
    mqtt.subscribe(hass, topic, message_received)

    # Set the initial state.
    hass.states.set(entity_id, 'No messages')

    # Service to publish a message on MQTT.
    def set_state_service(call):
        """Service to send a message."""
        mqtt.publish(hass, topic, call.data.get('new_state'))

    # Register our service with Home Assistant.
    hass.services.register(DOMAIN, 'set_state', set_state_service)

    # Return boolean to indicate that initialization was successfully.
    return True

Keep an eye on your Home Assistant log when restarting. Change the log level to debug so you don’t miss any information relating to your new component.

A better automation trigger

It would be nice to have a dedicated trigger that would allow the use of the following syntax in automations:

  trigger:
    platform: rf_event
    name: wp-btn-1

I played with this idea in my code but could not get th syntax to work. If you know of a way to achieve this, please let me know in the comments. This trigger platform is better in encapsulating the functionality of an rf_event. The event platform that I am using adds unnecessary configuration bulk with the event_type and event_data tags that are required.

  1. The notion of RF codes is specific to radio-frequency communication and RF as a transport layer. A good analogy would be having to specify your Wifi password in all automations triggered by WiFi enabled devices. It’s unnecessary duplication of information, that is sure to come back and bite you when you change your password! ↩︎