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:
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
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:

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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@app.route("/Outlets/api/outlets/<int:groupNumber>/<int:buttonNumber>",methods=["PUT","POST"]) | |
def update_outlet_state(groupNumber, buttonNumber): |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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