Home Assistant: Using Infrared, Motors, and Relays

Monitor Home Assistant Hero

In this article, we are going to combine ODROIDs with a little bit of hardware and Home Assistant, so that we can start converting non-smart appliances to "smart" appliances. We'll be working with wires, doing a bit of soldering, connecting relays, and in some cases working with mains voltage, so be careful!

Converting an air conditioning unit into an IoT AC

If you have an older air conditioning (AC) unit, or if you're planning on buying a new model, you might want to be able to control it from anywhere. For example, you could turn on the AC from your phone when you're leaving work or when returning from a long vacation. You could get a WiFi-enabled AC, but those are about $200 more expensive than a non-WiFi unit. Instead, we're going to turn a non-WiFi AC, LG P12RL.NSB, into a smart one. This will be done by controlling the AC unit through an ODROID-XU4, an infrared (IR) blaster, and Home Assistant. The IR blaster can be used to control any device that has a remote control, not just an AC unit.

Home Assistant : Using Infrared, Motors, and Relays: Figure 1 - Original AC remote (model AKB73456113)
Figure 1 - Original AC remote (model AKB73456113)

The first thing you need to consider is how will the remote control communicate with the AC unit? There are two methods: the first is when pressing a button on the remote the whole state is sent via IR (temperature, fan speed, power state, etc). The other option is pressing a button only the current action is sent (increase temperature, turn on fan, etc). You can check this, for instance, by sending a command to turn on the fan to max speed, then turning off the fan, but with the remote out of range of the device, followed by issuing a different temperature change command. If the fan remains blowing at full speed, then the remote is not sending the full state. In case it is sending the full state, the project at http://bit.ly/2AcpKAW might help you decode the state information being sent. In my case, the IR remote sends only the current key being pressed, with the exception of the power-on message, which sends also the temperature and fan state.

The hardware

Designing and building an IR blaster for an ODROID board is relatively simple and is already documented on the wiki at http://bit.ly/2A6HHmR. I wanted my implementation to work on either an ODROID-XU4 or an ODROID-C2, so I needed to be able to power it from the 5V line and drive a transistor from the GPIO. The final assembly and circuit is shown in Figure 2.

Home Assistant : Using Infrared, Motors, and Relays: Figure 02 - fritzing breadboard view of IR blaster)
Figure 02 - fritzing breadboard view of IR blaster)
Home Assistant : Using Infrared, Motors, and Relays: Figure 03 - Fritzing circuit diagram of IR blaster
Figure 03 - Fritzing circuit diagram of IR blaster

I used an existing IR extender from my Samsung TV for the IR leds. After opening it up, I found out it had 5 LEDs wired in parallel, but I had no specifications for the LEDs. I estimated that each diode could pass a peak current of around 30 - 50mA (based on a common datasheet at http://bit.ly/1trG4ZM), so 5 of them could pass between 150 - 250mA. This is why I would need a resistor of about 82Ω to limit the current from the ODROID. If you're using fewer IR LEDs, you will need a larger resistor. Sadly, such low resistors are hard to find, so I wired 4 330Ω resistors, from the C tinkering kit, in parallel instead (R1/R4). The transistor is controlled by pin 24 on the XU4, and will be used to modulate the signal. When the GPIO is turned on, the current flows and the IR LEDs turn on, and when the GPIO is off, the transistor shuts down the circuit. Resistor R5 is there to protect the GPIO.

Once you manage to build this, you can test the hardware by manually toggling GPIO 24 via sysfs and using a phone camera to film the leds. IR light should be visible on the camera and has a blue-ish hue. You can build it without a breadboard and you can even fit the transistor and resistors inside the XU4 case, but it will be a bit of a struggle. I broke the solder points twice while trying to fit it inside the case. The following piece of code can manually toggle GPIO pin 24:

$ sudo su -
# cd /sys/class/gpio/
# echo 24 > export
# cd gpio24
# echo out > direction
# echo 1 > value
# echo 0 > value

LIRC integration

Now, we need to tell LIRC that it can use the IR blaster to send data. To do this, use the following instructions in the wiki: http://bit.ly/2A6HHmR

$ sudo apt-get install lirc
$ sudo vi /etc/lirc/hardware.conf
#Chosen IR Transmitter
TRANSMITTER="ODROID blaster"
TRANSMITTER_MODULES="lirc_odroid lirc_dev"
TRANSMITTER_DRIVER=""
TRANSMITTER_DEVICE="/dev/lirc0"
TRANSMITTER_SOCKET=""
TRANSMITTER_LIRCD_CONF=""
TRANSMITTER_LIRCD_ARGS=""
Before restarting LIRC, we need to pass the correct parameters to the lirc_ODROID module. We can do this by creating the file /etc/modprobe.d/lirc.conf with the following contents:
options lirc_odroid gpio_out_pin=24 softcarrier=1 invert=0
Try restarting LIRC and monitor dmesg for any errors:
$ sudo systemctl enable lirc
$ sudo service lirc restart
In case dmesg shows errors like below, it means that some other driver has claimed GPIO PIN 24.
[   25.322482] lirc_dev: IR Remote Control driver registered, major 245
[   25.336230] lirc_odroid: module is from the staging directory, the quality is unknown, you have been warned.
[   25.346335] lirc_odroid: cant claim gpio pin 24
[   25.350461] lirc_odroid: init port fail!
[   25.353337] lirc_odroid[lirc_ODROID_exit]
In my case, it was the 1wire module, which can be disabled by adding the following lines to /etc/modprobe.d/blacklist-odroid.conf and rebooting:
# 1 wire
blacklist w1_gpio
blacklist wire

Getting the remote control codes

Once the transmitter seems to be ready, it's time to get the remote control codes for your remote. The codes consist of a series of intervals when the signal is on or off measured in microseconds. You can get these codes from various online sources, or you can record them with LIRC and an IR receiver. I used the IR receiver on a C2 to record each code to a different file, pressed CTRL+C to exit mode2, and cleaned it up by deleting the first row. When I was done, I added entries to lircd.conf:

c2$ sudo apt-get install lirc
c2$ sudo service lirc stop
c2$ sudo mode2 -m -d /dev/lirc0 | tee power-on
c2$ sudo sed -i '1d' power-on
This can get tedious, because I had to record the codes for power-on/off, temperature-18 through temperature-30, fan-low, fan-med, fan-high, swing-on/off, jet-on/off, ionizer-on/off. However, once I did, I merged them to a config file (/etc/lirc/lircd.conf) as demonstrated at http://bit.ly/2A9MK3r. You can restart LIRC and test that you are able to send the codes with irsend:
$ sudo service lirc restart
$ irsend LIST lgirplus.conf ""
$ irsend SEND_ONCE lgirplus.conf power-on
$ irsend SEND_ONCE lgirplus.conf power-off
In case the last irsend command fails/times-out, you may have run into a LIRC/driver bug. The quick fix is to restart LIRC before injecting each command.

Integration in Home Assistant

If the LIRC blaster is connected to the same device where you have Home Assistant installed, you could use the Shell Command component (http://bit.ly/2vOFnhe) to issue IR commands from HA. In my case, LIRC was running on a different system than HA, so I developed a Python script that talks to HA through MQTT and issues the irsend commands.

The complete code for this MQTT agent is available at http://bit.ly/2Ax8SYz. The code gets configuration data from /etc/ir-ac-mqtt-agent.yaml, then connects with MQTT to the broker. For a guide on setting up the broker and Home Assistant, refer to the ODROID magazine article at http://bit.ly/2A6ql9I. It also defines two callback functions:

  • on_connect - registers to a list of MQTT topics to listen for commands
  • on_message - gets called each time a MQTT message is received for the registered topics.

The script also keeps an internal dictionary with the current state, as such power-on and temperature. This allows it to better react to incoming commands. For instance, if it receives a power off command, but the power is already off, it will ignore the command to avoid the AC unit from beeping.

The logic inside tries to simulate what the physical remote does by ignoring commands unless the power is on, and also setting the temperature to 18C and fan to full when enabling "Jet Mode". There were some simplifications done as well, such as when sending the power-on command, the temperature is set to 21C and the fan to high because the power-on signal encodes some of the state of the remote. The agent script listens to commands issued on topics such as ha/lg_ac/ionizer/set and sends feedback on ha/lg_ac/ionizer/get, so that the web interface has feedback that the command was received.

To install the MQTT agent, you can use these commands:

$ sudo wget -O /usr/local/bin/ir-ac-mqtt-agent.py \
https://raw.githubusercontent.com/mad-ady/home-assistant-customizations/master/external-scripts/ir-ac-mqtt-agent.py
$ sudo chmod a+x /usr/local/bin/ir-ac-mqtt-agent.py
$ sudo apt-get install python-pip python-yaml
$ sudo pip install paho-mqtt
$ sudo wget -O /etc/ir-ac-mqtt-agent.yaml \
https://github.com/mad-ady/home-assistant-customizations/blob/master/external-scripts/ir-ac-mqtt-agent.yaml
$ sudo wget -O /etc/systemd/system/ir-ac-mqtt-agent.service \
https://github.com/mad-ady/home-assistant-customizations/blob/master/external-scripts/ir-ac-mqtt-agent.service
Take your time to make the necessary changes to /etc/ir-ac-mqtt-agent.yaml, then enable and start the service:
$ sudo systemctl enable ir-ac-mqtt-agent
$ sudo systemctl start ir-ac-mqtt-agent
On the Home Assistant side, we will configure several MQTT switches (http://bit.ly/2AwtDUd) to handle Power, Jet mode, Ionizer, Swing, an input_select (http://bit.ly/2zEfgNA), to select fan speed mode and an input_number (http://bit.ly/2k0vfOY), to hold the desired temperature. The switches communicate their state with the backend script via MQTT directly, while the other components make use of automation to trigger MQTT messages on changes. Here is the component configuration:
switch:
  - platform: mqtt
    command_topic: 'ha/lg_ac/power/set'
    state_topic: 'ha/lg_ac/power/get'
    payload_on: 'ON'
    payload_off: 'OFF'
    name: 'AC Power'
    retain: false
  - platform: mqtt
    command_topic: 'ha/lg_ac/ionizer/set'
    state_topic: 'ha/lg_ac/ionizer/get'
    payload_on: 'ON'
    payload_off: 'OFF'
    name: 'AC Ionizer'
    retain: false
  - platform: mqtt
    command_topic: 'ha/lg_ac/jet/set'
    state_topic: 'ha/lg_ac/jet/get'
    payload_on: 'ON'
    payload_off: 'OFF'
    name: 'AC Jet'
    retain: false
  - platform: mqtt
    command_topic: 'ha/lg_ac/swing/set'
    state_topic: 'ha/lg_ac/swing/get'
    payload_on: 'ON'
    payload_off: 'OFF'
    name: 'AC Swing'
    retain: false

input_select:
  lg_ac_fan_mode:
    name: Fan mode
    options:
      - cycle
      - low
      - med
      - high
    initial: 'low'

input_number:
  lg_ac_temperature:
    name: AC Temperature
    initial: 22
    min: 18
    max: 30
    step: 1
We can group all of these elements in a separate view:
group:
…
 lg_ac:
    name: Air Conditioning
    view: yes
    icon: mdi:snowflake
    entities:
      - group.lg_ac_group
 lg_ac_group:
    name: LG AC
    entities:
      - switch.ac_power
      - input_number.lg_ac_temperature
      - input_select.lg_ac_fan_mode
      - switch.ac_jet
      - switch.ac_ionizer
      - switch.ac_swing
And after restarting Home Assistant, it should look like Figure 4.
Home Assistant : Using Infrared, Motors, and Relays: Figure 4 - Basic support for Air Conditioning
Figure 4 - Basic support for Air Conditioning
- action:
  - alias: LG AC MQTT Set Temperature
    data:
      payload_template: '{{ states.input_number.lg_ac_temperature.state }}'
      qos: 0
      retain: true
      topic: ha/lg_ac/temperature/set
    service: mqtt.publish
  alias: LG AC Set IR temperature
  id: '1499081218012'
  trigger:
  - entity_id: input_number.lg_ac_temperature
    platform: state
- action:
  - alias: LG AC MQTT Set Fan
    data:
      payload_template: '{{ states.input_select.lg_ac_fan_mode.state }}'
      qos: 0
      retain: true
      topic: ha/lg_ac/fan/set
    service: mqtt.publish
  alias: LG AC Set IR Fan
  id: '1499152161'
  trigger:
  - entity_id: input_select.lg_ac_fan_mode
    platform: state
- action:
  - alias: LG AC Set temperature slider
    service: input_number.set_value
    data_template:
      entity_id: input_number.lg_ac_temperature
      value: '{{trigger.payload}}'
  alias: LG AC Read temperature via MQTT
  id: '1499423002'
  trigger:
  - platform: mqtt
    topic: ha/lg_ac/temperature/get
- action:
  - alias: LG AC Set fan combo box
    service: input_select.select_option
    data_template:
      entity_id: input_select.lg_ac_fan_mode
      option: '{{trigger.payload}}'
  alias: LG AC Read temperature via MQTT
  id: '1499423003'
  trigger:
  - platform: mqtt
    topic: ha/lg_ac/fan/get
The first two automations push the values for the temperature and fan components via MQTT on state change, while the last two automations receive temperature and fan data through MQTT to update the web interface. Restarting again should give you a functional AC system controlled by Home Assistant. Here's a video of an early prototype of it in action: https://youtu.be/zGRlhILVRCQ.

Adding a start & stop timer

The original AC remote has an option to start and stop the AC on a timer. We can also model that inside Home Assistant with a few automations and some extra components. Ideally you would use the input_datetime component (http://bit.ly/2A8Mmoc), to allow the user to select the start and stop times, but at the time of writing this article, the component is not fully working. On HA 0.56, so we will be using input_text instead (http://bit.ly/2i8lcXI), with a regular expression that allows the typing of a time. There are also two input_booleans (http://bit.ly/2Bnd4Yj), that look like switches and allows the user to enable/disable the functionality. Here is the configuration that goes into configuration.yaml:

input_text:
  lg_ac_on_timer:
    name: LG AC on timer
    initial: '16:00'
    pattern: '^[0-9]{1,2}:[0-9]{1,2}$'
  lg_ac_off_timer:
    name: LG AC off timer
    initial: '18:00'
    pattern: '^[0-9]{1,2}:[0-9]{1,2}$'

input_boolean:
  ac_on_timer_active:
    name: Activate AC On timer
    initial: off
    icon: mdi:calendar
  ac_off_timer_active:
    name: Activate AC Off timer
    initial: off
    icon: mdi:calendar

group:
…
  lg_ac:
…
    entities:
…
      - group.lg_ac_timer
  lg_ac_timer:
    name: AC Timer
    entities:
      - input_boolean.ac_on_timer_active
      - input_text.lg_ac_on_timer
      - input_boolean.ac_off_timer_active
      - input_text.lg_ac_off_timer
The end result after restarting Home Assistant looks like Figure 5.
Home Assistant : Using Infrared, Motors, and Relays: Figure 5 - AC controls with timers
Figure 5 - AC controls with timers
To make it work, you will need to add the following two automations in automations.yaml. The first automation turns on AC, Jet mode and also turns off the Activate AC On timer, so that it is a one-shot event. It checks every minute to see if the ac_on_timer_active is on and if the current time is the same as the string inside lg_ac_on_timer. The second automation does a similar thing for the off timer, but with different actions.
- action:
  - service: switch.turn_on
    entity_id:
    - switch.ac_power
  - service: switch.turn_on
    entity_id:
    - switch.ac_jet
  - service: input_boolean.turn_off
    entity_id:
    - input_boolean.ac_on_timer_active
  alias: Turn On AC on timer
  id: '1502194970'
  trigger:
  - platform: time
    minutes: /1
    seconds: 0
  condition:
  - condition: state
    entity_id: input_boolean.ac_on_timer_active
    state: 'on'
  - condition: template
    value_template: '{{ now().strftime("%H:%M") == states.input_text.lg_ac_on_timer.state
      }}'
- action:
  - service: switch.turn_off
    entity_id:
    - switch.ac_power
  - service: input_boolean.turn_off
    entity_id:
    - input_boolean.ac_off_timer_active
  alias: Turn Off AC on timer
  id: '1502194971'
  trigger:
  - platform: time
    minutes: /1
    seconds: 0
  condition:
  - condition: state
    entity_id: input_boolean.ac_off_timer_active
    state: 'on'
  - condition: template
    value_template: '{{ now().strftime("%H:%M") == states.input_text.lg_ac_off_timer.state
      }}'
After restarting Home Assistant once again, you should have a complete "Smart" AC system which you can enjoy during heat waves. Although the automations above are pretty basic, you can add more logic - like adding a thermostat (http://bit.ly/2Ay6kJN), and monitor the outside weather either from a sensor, or from the weather forecast, so you can turn on or of your AC when the outside weather is over a threshold. In fact, we will play with such a thermostat for our next project.

Controlling a gas heater with a Home Assistant Thermostat

Since for many readers it's winter, you may be interested in having a smart heater. We are going to control a natural gas boiler connected to the central heating, a Viessman Vitopend 100 (http://bit.ly/2Bd9X4d). The heater, if turned on, will try to keep a constant water temperature for the water that flows through the heating elements in the house, but this can be wasteful if nobody is home or if the house is well insulated. Instead, you should use a thermostat to turn the heater on or off based on the ambient temperature. My gas heater had a thermostat, Salus 091FLRF (http://bit.ly/2AbcnkC). My thermostat does the job, but it has some big drawbacks. For starters, it would reset in the middle of the night and lose the configuration, leaving me in the cold. An equivalent Internet-enabled thermostat is at least $100, so a DIY approach pays off.

The advantage of the external thermostat is that it had a relay connected to the gas heater and I didn't have to open it up. If you do have to open up your heater, make sure you call a specialized technician, or you could get in trouble with your natural gas provider.

After analyzing the thermostat schematics, it became apparent that communication with the heater is done by closing or opening a relay on a 220V line. If we add a second relay in parallel with the first one I can close either of them to turn the heater on or off. Why do you need a relay? So that you don't fry your ODROID board! The plan is to use a Sainsmart 2 Channel relay (http://bit.ly/2A5WEFF), to connect the ODROID to the 220V line that goes into the heater. Even if the relay is rated for 5V, it can be safely used with an ODROID-C1 or C2's 3.3V GPIOs.

Home Assistant : Using Infrared, Motors, and Relays: Figure 6 - SainSmart 2 Channel Relay
Figure 6 - SainSmart 2 Channel Relay
Since you will be working with mains voltage, make sure you either get a certified electrician or comply to the laws in your country. Also always disconnect all appliances from mains when working with these lines. The schematic we're going to implement is pretty simple as shown in Figure 7.
Home Assistant : Using Infrared, Motors, and Relays: Figure 7 - Implementation details
Figure 7 - Implementation details
There is only one complication with this setup. Instead of using the 40 pin GPIO connectors on the C2 (J2 header), I used some pins from the I2S header (J7) instead. The reason is, in my case the J2 header was used by HardKernel's 3.5" display shield (http://www.hardkernel.com/main/products/prdt_info.php?g_code=G147435282441).

In order to convert the I2S pins into GPIO pins, you will need to edit the DTB loaded with the kernel. Follow the instructions below to edit the DTB, and also make the changes persistent when you upgrade the kernel through apt, as discussed at http://bit.ly/2AyrWWz.

$ sudo apt-get install device-tree-compiler
$ sudo fdtput /media/boot/meson64_ODROIDc2.dtb /I2S status disabled
$ sudo fdtput /media/boot/meson64_ODROIDc2.dtb /i2s_platform status disabled
$ sudo vi /etc/kernel/postinst.d/i2s-disable
#!/bin/bash

echo "Disabling I2S support from the device tree"
/usr/bin/fdtput /media/boot/meson64_ODROIDc2.dtb /I2S status disabled
/usr/bin/fdtput /media/boot/meson64_ODROIDc2.dtb /i2s_platform status disabled
Once you reboot, the J7 header will have only GPIO pins that you can use.

Controlling the relay through MQTT

Since it is not the ODROID that runs Home Assistant, we will need to be able to control it over the network, with MQTT. The agent code listens for ON/OFF messages on a specific topic and turns the GPIO on or off. Normally, we would have used wiringPI (http://bit.ly/2zBMm0F), but in this case the J7 connector is not mapped to wiringPI, so we will be using sysfs instead. The methods pinMode and digitalWrite emulate their wiringPI counterparts to make the code easier to understand. The code is available at http://bit.ly/2jofKfN. For brevity the code is not included here. To install it on your system, follow these steps:

$ sudo wget -O /usr/local/bin/heater-mqtt-agent.py \
https://raw.githubusercontent.com/mad-ady/home-assistant-customizations/master/external-scripts/heater-mqtt-agent.py
$ sudo chown a+x /usr/local/bin/heater-mqtt-agent.py
$ sudo wget -O /etc/heater-mqtt-agent.yaml \
https://github.com/mad-ady/home-assistant-customizations/blob/master/external-scripts/heater-mqtt-agent.yaml
$ sudo wget -O /etc/systemd/system/heater-mqtt-agent.service \
https://github.com/mad-ady/home-assistant-customizations/blob/master/external-scripts/heater-mqtt-agent.service
$ sudo apt-get install python-pip python-yaml
$ sudo pip install paho-mqtt
Edit the configuration file at /etc/heater-mqtt-agent.yaml, set your MQTT details, then start the agent with:
$ sudo systemctl enable heater-mqtt-agent
$ sudo systemctl start heater-mqtt-agent
You can monitor messages from the agent with the following command:
$ sudo journalctl -f -u heater-mqtt-agent

Home Assistant integration

To make use of this agent inside Home Assistant, you can configure a MQTT Switch and add it inside its own group:

switch:
…
 - platform: mqtt
    command_topic: 'ha/heater/set'
    state_topic: 'ha/heater/get'
    payload_on: 'ON'
    payload_off: 'OFF'
    name: 'Heater'
    retain: true

group:
...
  heater:
    name: Gas heater
    view: yes
    icon: mdi:fire
    entities:
      - switch.heater
Home Assistant : Using Infrared, Motors, and Relays: Figure 8 - A switch for your heater
Figure 8 - A switch for your heater
Having a switch that you can toggle is nice, but you probably want a thermostat. A thermostat tries to keep a temperature between certain levels, and Home Assistant requires a temperature sensor and a switch (http://bit.ly/2Ay6kJN). The temperature sensor could be any sensor, including the outside temperature, but in our case we may want to use data from sensors in multiple rooms. I have two DS18b20 temperature sensors in two rooms already integrated into Home Assistant, as described in my previous ODROID article at http://bit.ly/2A6ql9I. However, the thermostat can act only on one temperature sensor. We will need to combine the two sensors into one sensor, which returns the minimum temperature between the two. This can be easily extended to multiple sensors, so that the thermostat heats the coldest room to the desired temperature. We can do this with a template sensor (http://bit.ly/2wPQLeY) inside configuration.yaml:
sensor:
…
  - platform: template
    sensors:
…
     house_temperature:
        friendly_name: Minimum house temperature
        unit_of_measurement: '_C'
        value_template: '{{ (states.sensor.temperature_rest_python.state, states.sensor.temperature_via_mqtt.state) | min }}'

group:
…
  weather:
…
    entities:
…
      - sensor.house_temperature
Adding the thermostat is now an easy task (configuration.yaml):
climate:
  - platform: generic_thermostat
    name: Heater thermostat
    heater: switch.heater
    target_sensor: sensor.house_temperature
    min_temp: 15
    max_temp: 30
    target_temp: 24
    min_cycle_duration:
      minutes: 5
    tolerance: 0.3

group:
…
  heater:
…
    entities:
…
      - climate.heater_thermostat
Home Assistant : Using Infrared, Motors, and Relays: Figure 9 - Thermostat
Figure 9 - Thermostat
The thermostat configuration will turn on the heat when the temperature is below target_temp - tolerance, will keep the heater on for at least min_cycle_duration, then will turn it off when the temperature is above target_temp + tolerance. You can use a similar thermostat with the AC we've built before, but you need to configure it with ac_mode: true so that it is a cooling device.

You can manually control/set the thermostat, but the fun part is that you can use automations to control the thermostat. A simple use case is to set different target temperatures based on presence detection or time of day. The following automations set the target temperature to 23C during the day and 25.5C during the night (automations.yaml):

- action:
  - alias: climate.set_temperature
    data:
      entity_id: climate.heater_thermostat
      temperature: 23
    service: climate.set_temperature
  alias: Thermostat set low
  condition: []
  id: '1506943539788'
  trigger:
  - at: 07:00
    platform: time
- action:
  - alias: climate.set_temperature
    data:
      entity_id: climate.heater_thermostat
      temperature: 25.5
    service: climate.set_temperature
  alias: Thermostat set high
  condition: []
  id: '1506943638481'
  trigger:
  - at: '19:00'
    platform: time

Controlling a window blind with Home Assistant

One more physical item we can integrate into Home Assistant is a motorized window blind. In my case, the motors were controlled by a three-way physical switch. When in the up position, the blind raises. In the middle position, the motors are off, and in the down position it lowers. I wanted to use a relay and take over the slide operation when the physical switch is in the middle position, but also to allow manual operation with the regular switch. After a long discussion on the forum (http://bit.ly/2AwFCBb), I settled on the physical implementation shown in Figure 10.

Home Assistant : Using Infrared, Motors, and Relays: Figure 10 Controlling a window blind motor
Figure 10 - Controlling a window blind motor
Apart from the ODROID-C1+ and the 2-module relay board, we also need to use a couple of snubbers. The snubbers are RC circuits which have to discharge the reactive energy that builds up in the motors during switching, otherwise transient power spikes will brown-out your relay contacts when they try to discharge, I damaged a relay before realizing this. I used the types of snubbers described at http://bit.ly/2A58tf4 in my build.

Please note that while my design minimizes the number of components used, it's more risky because it operates at 220V. Forum user @Jojo proposed an alternate design which changes the physical switch's voltage to 5V and uses digital circuits to drive the relay, which should be safer (http://bit.ly/2Bf2txo).

MQTT agent for blind operation

As you've grown accustomed already, we need a script running on the ODROID that listens to commands for the blinds, sent through MQTT, then executes the commands by triggering the relay and reports back the status. The difference from previous code shown is that this time we will have to use threads, more specifically timers. The simple way the code can operate is:

A message to OPEN or CLOSE the blind is sent from Home Assistant The script turns on the first relay and assumes control of the motors, then uses the second relay to control the direction the blind operates (up or down) The script waits for a set number of seconds so that the blind can fully open or close, which takes 17 seconds in my case When the time expires, the relay is reset to manual mode and a status update is sent via MQTT back to Home Assistant

The complication comes if you want to stop the blind midway, or at an arbitrary location. To do this, Home Assistant can send a STOP command, or a desired position, but if the script is single-threaded, it is stuck waiting for the motor to finish before processing the STOP command. By using timers, after the motor is started, a timer is scheduled to stop the motor in 17 seconds, and the code goes back to listening for MQTT messages. If a STOP message comes before the timer expires, it will cancel the timer and run the stopBlinds() command immediately. Additionally, the script keeps an internal state of the blind position and the direction of the motor, so that if the blind is midway, and you want to open it 70%, it will be able to calculate for how long it needs to run the motor and in which direction. The code is available at http://bit.ly/2A88Ohk and can be installed with:

$ sudo wget -O /usr/local/bin/blind-cover-mqtt-agent.py \
https://raw.githubusercontent.com/mad-ady/home-assistant-customizations/master/external-scripts/blind-cover-mqtt-agent.py
$ sudo chown a+x /usr/local/bin/blind-cover-mqtt-agent.py
$ sudo wget -O /etc/systemd/system/blind-cover-mqtt-agent.service \
https://raw.githubusercontent.com/mad-ady/home-assistant-customizations/master/external-scripts/blind-cover-mqtt-agent.service
$ sudo wget -O /etc/blind-cover-mqtt-agent.yaml \
https://raw.githubusercontent.com/mad-ady/home-assistant-customizations/master/external-scripts/blind-cover-mqtt-agent.yaml
$ sudo apt-get install python-pip python-yaml
$ sudo pip install paho-mqtt
In addition to this, you will need to install wiringPi library and its Python bindings (http://bit.ly/2AwVQui). You will need to edit /etc/blind-cover-mqtt-agent.yaml and input your MQTT details, then you can enable and start the agent:
$ sudo systemctl enable blind-cover-mqtt-agent
$ sudo systemctl start blind-cover-mqtt-agent
You will be able to view debug messages with the following command:
$ sudo journalctl -f -u blind-cover-mqtt-agent

Home Assistant configuration

Home Assistant comes with a MQTT cover component (http://bit.ly/2jmAIf7), which provides a basic interface for the cover. You can configure it with this configuration inside configuration.yaml:

cover:
  - platform: mqtt
    name: "Blinds"
    state_topic: "ha/blind_cover/get"
    command_topic: "ha/blind_cover/set"
    set_position_topic: "ha/blind_cover/position"
    assumed_state: true
    payload_open: 'OPEN'
    payload_close: 'CLOSE'
    payload_stop: 'STOP'
    state_open: 'open'
    state_close: 'closed'
group:
…
 blinds:
    name: Blinds
    view: yes
    icon: mdi:blinds
    entities:
      - cover.blinds
After restarting Home Assistant, the blind control will look like Figure 11:
Home Assistant : Using Infrared, Motors, and Relays: Figure 11 Standard Blind Controls
Figure 11 - Standard blind controls
At https://youtu.be/MIhuELv1244, you can see a demo of the blinds. As I mentioned before, is there a way to set an arbitrary position for the blind? There is a way to tweak the blind component to display a slider next to the blind component that allows you to control its position. To do this, you will need to install the Home Assistant custom UI, downloaded from http://bit.ly/2AcqDJE, and type the following commands:
$ sudo su - homeassistant
$ cd .homeassistant/
$ curl -o update-custom-ui.sh \
"https://raw.githubusercontent.com/andrey-git/home-assistant-custom-ui/master/update.sh?raw=true"
$ chmod a+x update-custom-ui.sh
$ ./update-custom-ui.sh
The configuration needs to be tweaked a bit to load the customized blind control. First, you will need to activate the custom UI controls by making these changes to configuration.yaml:
customizer:
  custom_ui: local

Use the customize section to specify that all covers should use the new UI (inside configuration.yaml):

homeassistant:
  customize_glob:
    cover.*:
      custom_ui_state_card: state-card-custom-ui
By default, the cover will look the same as the old one, so we need to manually enable the slider (under the customize section in configuration.yaml):
homeassistant:
  customize:
    cover.blinds:
      state_card_mode: break-slider
      stretch_slider: true
After restarting Home Assistant, the user interface will look like Figure 12.
Home Assistant : Using Infrared, Motors, and Relays: Figure 12 Cover with Slider
Figure 12 - Cover with slider
As you can see, with a bit of work you can turn your ordinary house into a smart house with the help of ODROIDs and Home Assistant. For feedback and discussions, please visit the original thread at http://bit.ly/2s13GbB.

Be the first to comment

Leave a Reply