Home Assistant – integrating RESTful switches

I have started integrating my IoT-devices and services with the Home Assistant platform. See my previous post for details on getting started with Home Assistant and subscribing to MQTT messages:

Home Assistant – getting started and using MQTT sensors

My next attempt is to configure RESTful switches in HA for interacting with an existing web service that I use for controlling 433 MHz outlets. I will also add automation rules for the switches and test the voice command in Home Assistant.

My existing remote control web app / service

I use a homemade web app for controlling light sources via 433 MHz remote controlled power outlets. The setup is described in these posts:

RCSwitch revisited – control RC outlets with a web app

Preparing the remote control app for Christmas

My inexpensive and simple outlets are controlled with one-way communication, i.e. you can send an on/off signal to an outlet but you don’t get feedback on the current state of the outlet relay. The original hardware control has as set of push buttons for this purpose and I have simulated that in my app with on/off buttons in a grid:

remote_and_iphoneapp

The web app calls a web service (REST API) on a Raspberry Pi that sends control messages with a 433 MHz transmitter based on the endpoint and payload defined in the http PUT request. The REST API can be called from other applications as well, e.g. curl on the command line (I use this for scheduling on/off events with crontab).

An alternative solution

If Home Assistant was running on the same Raspberry Pi that has the physical RF transmitter, the RPI RF Switch in HA could be used. I prefer to integrate with my existing home made solution as it is already up-and-running, it runs on a different RPi than HA, I have an alternative web app for it and it can be made to return the state (though by cheating, see below) of the switch.

RESTful switches in Home Assistant

Home Assistant has a RESTful switch component that can be used for defining switches in the configuration.yaml file:

https://home-assistant.io/components/switch.rest/

For the switch, you define the REST end point and the body payload that should be sent as a POST request (body_on, body_off in the yaml file) when the switch is toggled. Home Assistant gets the current state of the switch with a GET request to the defined endpoint. By default, the state is considered on if the response data from the GET matches body_on and off if it matches body_off. I use this configuration:


switch:
– platform: rest
name: 'Window lights upstairs'
resource: http://192.168.1.16:5000/Outlets/api/outlets/1/1
body_on: 'on'
body_off: 'off'
– platform: rest
name: 'Floor light upstairs'
resource: http://192.168.1.16:5000/Outlets/api/outlets/1/4
body_on: 'on'
body_off: 'off'
– platform: rest
name: 'Window lights downstairs'
resource: http://192.168.1.16:5000/Outlets/api/outlets/1/2
body_on: 'on'
body_off: 'off'
– platform: rest
name: 'Wall lights downstairs'
resource: http://192.168.1.16:5000/Outlets/api/outlets/1/3
body_on: 'on'
body_off: 'off'
– platform: rest
name: 'Garden lights'
resource: http://192.168.1.16:5000/Outlets/api/outlets/2/1
body_on: 'on'
body_off: 'off'

I use group configurations for combining switches and sensors for the different floors of the house and I define a default_view so that only these groups appear on the first page in Home Assistant:


group:
groundfloor:
name: Ground floor
entities:
– switch.window_lights_downstairs
– switch.wall_lights_downstairs
– sensor.ground_floor_temp
– sensor.ground_floor_humidity
topfloor:
name: Top floor
entities:
– switch.window_lights_upstairs
– switch.floor_light_upstairs
– sensor.top_floor_temp
– sensor.top_floor_pressure
default_view:
view: yes
entities:
– group.groundfloor
– group.topfloor

My example configuration creates this GUI in Home Assistant:

ha_switches_small

It’s pretty cool that a group switch appears automagically if the group has switches. This switch turns all switches in the group on/off.

By using the customize option in the configuration, the default icons can be changed and friendly_names/display names can be used:

ha_sw_sens_small
Group sensor and switches with customized icons and display names

Modifying the REST API

My existing REST API does not fit Home Assistant’s RESTful switch schema. I need to change the following:

  • Accept POST requests (current only accepts PUT requests)
  • Accept a raw string data payload (‘on’ or ‘off’ instead of a json object)
  • Return the state of a specified outlet with GET
  • Store the state of each outlet

Accept POST requests

Accepting POST and well as PUT requests is as simple as adding POST to the @app.route definition in Flask:


@app.route("/Outlets/api/outlets/<int:groupNumber>/<int:buttonNumber>",methods=["PUT","POST"])
def update_outlet_state(groupNumber, buttonNumber):

view raw

putandpost.py

hosted with ❤ by GitHub

Accept raw string payloads

Home Assistant will send ‘on’ and ‘off’ as a raw string payload in the POST requests. My existing API only accepted state data as a json object. I modify the PUT/POST method to handle both json and raw data:


@app.route("/Outlets/api/outlets/<int:groupNumber>/<int:buttonNumber>",methods=["PUT","POST"])
def update_outlet_state(groupNumber, buttonNumber):
state=None
if request.json is not None:
state=request.json.get("state")
else:
state=request.data
if (state is None):
abort(400)
if (state.lower() != 'on' and state.lower() != 'off'):
abort(400)

Return the state of a specified outlet

Home Assistant will regularly make a GET request for each RESTful switch to get the current state into HA. For supporting this, I add a GET route in flask that returns the state of a specified outlet as a raw string.


@app.route("/Outlets/api/outlets/<int:groupNumber>/<int:buttonNumber>",methods=["GET"])
def get_outlet_state(groupNumber, buttonNumber):
return statestorage.get_state(groupNumber, buttonNumber)

Store the state of each outlet

As my hardware outlets can not return their current state, I let the web service store the last POSTed state for each outlet. As I don’t want to implement a database storage for these rudimentary data, I store the data in a global dictionary protected from threading issues with a lock pattern. As long as the web service is not restarted and the outlets don’t miss a signal, this will reflect the true state of the outlets. It is not perfect but is acceptable in my solution.


import threading
states = {}
lock = threading.Lock()
def get_state(groupNumber, buttonNumber):
key = str(groupNumber) + '_' + str(buttonNumber)
state = 'off'
lock.acquire()
try:
if key in states:
state = states[key]
finally:
lock.release()
return state
def set_state(groupNumber, buttonNumber, state):
key = str(groupNumber) + '_' + str(buttonNumber)
lock.acquire()
try:
states[key] = state
finally:
lock.release()

view raw

statestorage.py

hosted with ❤ by GitHub

Adding some automation

With the switches connected and working for manual interaction, the next step is to add some automation. I want to turn on the garden lights in the evening and turn them off in the morning. As Home Assistant has access to my home’s coordinates, it can calculate the sunset- and sunrise times. The sun component in HA triggers events for sunset and sunrise and these can be used in automation rules. My rules will use these events and include actions that calls the switch.turn_on/turn_off service that operate on a defined entity:


automation:
– alias: Turn on garden lights when sun sets
trigger:
platform: sun
event: sunset
action:
service: switch.turn_on
entity_id: switch.garden_lights
– alias: Turn off garden lights when sun rises
trigger:
platform: sun
event: sunrise
action:
service: switch.turn_off
entity_id: switch.garden_lights

view raw

automation.yaml

hosted with ❤ by GitHub

An offset can be added to the sunset/sunrise event to get triggering before or after the actual event:

event: sunset
offset: '-00:30:00'

I want to create another automation rule that turns off all indoor lights at night. For this, the time platform can be used. I have grouped all my indoor lights so that they can be jointly switched:


group:
indoor_lights:
name: Indoor lights
entities:
– switch.window_lights_downstairs
– switch.wall_lights_downstairs
– switch.window_lights_upstairs
– switch.floor_light_upstairs
automation:
– alias: Turn off indoor lights at night
initial_state: True
hide_entity: False
trigger:
platform: time
at: '23:00:00'
action:
service: switch.turn_off
entity_id: group.indoor_lights

Automation rules can also have conditions that checks the current state of the system. This can be useful for limiting when the action should be applied. E.g. “Turn on the lights at sunset but only when someone is home”.

Voice commands

Another cool feature in Home Assistant is the conversation command that can be used for triggering actions via voice commands from an enabled web browser. It can only parse sentences in the form “Turn” <friendly name of entity> “on” or “off”. Perhaps just a gimmick, but it works quite well (in Chrome desktop browsers at least).

You need to enable the component in the configuration:

# Allows you to issue voice commands from the frontend in enabled browsers
conversation:

Splitting up the configuration

When you keep adding more configurations to Home Assistant, configuration.yaml will grow and soon get hard to maintain. It will also contain a mix of public data and private information (like passwords). Home Assistants yaml-parser provides ways for handling this though:

Using !include

!include filename.yaml can be used for referencing one yaml-file from another. For example, this statement in one file:

automation: !include automation.yaml

will include the detailed automation settings from one file into another file.

Using secrets.yaml

!secrets KEYNAME will import the value of a specific key from a file called secrets.yaml that contains key-value pairs. For example:

api_password: !secret ha_pwd

in configuration.yaml and

ha_pwd: MYPASSWORD

in secrets.yaml will result in

api_password: MYPASSWORD

Now you can share your configuration files but keep your secrets by leaving out the secrets.yaml file.

Using !env_var

Another option for keeping private information out of the main configuration is to refer to environment variables on the OS where Home Assistant is running. The previous example would look like this:

api_password: !env_var ha_pwd

Conclusions

With a slightly modified web service it was easy to connected it to RESTful switches in Home Assistant. You get an acceptable GUI automatically but with some customizations, the GUI can be adapted for a better look-and-feel. My simplex 433 MHz outlets requires that I store the outlet states on the Raspberry – not perfect but acceptable for me.

The automation configurations can be very powerful – I will add more of these while integrating additional devices and services.

The code for my updated outlet API can be found in GitHub:

https://github.com/LarsBergqvist/RemoteControlled_Outlets

and the configurations for my on-going Home Assistant project can be found here:

https://github.com/LarsBergqvist/Home-Assistant_configuration

 

 

 

 

Leave a comment