Smart plugs are the gateway drug. Then you want motion sensors. Then you’re running a Kubernetes cluster in your basement to control your lights.
Here’s how to do home automation the developer way.
The Stack# Forget cloud-dependent apps. Build infrastructure you control:
┌ │ │ ├ │ │ ├ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ S ─ ─ ─ ─ e ─ ─ ─ M ( ─ n ─ ─ ─ Q M ─ s ─ ─ ─ T o ─ o ─ ─ ─ T s ─ r ─ ─ ( ─ q ─ s ─ ─ C ─ B u ─ ─ ─ e ─ r i ─ ─ ─ n ─ o t ─ ─ ─ t ─ k t ─ │ ─ ─ H r ─ e o ─ ─ ─ o a ─ r ) ─ ─ ─ m l ─ ─ ─ ─ e ─ ─ S ─ ─ A ─ ─ w ─ ─ A u ─ ─ i ─ ─ s t ─ ─ t ─ ─ s o ─ │ │ ─ c ─ ─ i m ─ ─ h ─ ─ s a ─ ─ e ─ ─ t t ─ ─ s ─ ─ a i ─ ─ ─ ─ n o ─ Z ( ─ ─ ─ t n ─ i Z ─ ─ ─ ─ g i ─ │ ─ ─ H ─ b g ─ ─ ─ u ─ e b ─ ─ ─ b ─ e e ─ ─ ─ ) ─ / e ─ C ─ ─ ─ Z 2 ─ a ─ ─ ─ - M ─ m ─ ─ ─ W Q ─ e ─ ─ ─ a T ─ r ─ ─ ─ v T ─ a ─ ─ ─ e ) ─ s ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┤ │ │ ┤ │ ┘ │ │
Why Home Assistant?# Local control (no cloud dependency) Integrates with everything Automations in YAML (or visual editor) Active open-source community Docker deployment:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# docker-compose.yml
services :
homeassistant :
image : ghcr.io/home-assistant/home-assistant:stable
container_name : homeassistant
volumes :
- ./config:/config
- /etc/localtime:/etc/localtime:ro
restart : unless-stopped
network_mode : host
mosquitto :
image : eclipse-mosquitto
container_name : mosquitto
ports :
- "1883:1883"
volumes :
- ./mosquitto/config:/mosquitto/config
- ./mosquitto/data:/mosquitto/data
- ./mosquitto/log:/mosquitto/log
zigbee2mqtt :
image : koenkk/zigbee2mqtt
container_name : zigbee2mqtt
volumes :
- ./zigbee2mqtt:/app/data
- /run/udev:/run/udev:ro
devices :
- /dev/ttyUSB0:/dev/ttyUSB0
environment :
- TZ=America/New_York
MQTT: The Backbone# MQTT is pub/sub for IoT. Lightweight, fast, perfect for sensors.
Publish sensor data:
1
2
3
4
5
6
7
8
9
10
11
12
13
import paho.mqtt.client as mqtt
import json
client = mqtt . Client ()
client . connect ( "192.168.1.100" , 1883 )
# Publish temperature
payload = {
"temperature" : 72.5 ,
"humidity" : 45 ,
"battery" : 98
}
client . publish ( "home/office/sensor" , json . dumps ( payload ))
Subscribe to commands:
1
2
3
4
5
6
7
8
9
10
11
def on_message ( client , userdata , msg ):
payload = json . loads ( msg . payload )
if msg . topic == "home/office/light/set" :
if payload . get ( "state" ) == "ON" :
turn_on_light ()
else :
turn_off_light ()
client . subscribe ( "home/office/light/set" )
client . on_message = on_message
client . loop_forever ()
Topic structure convention:
h E h h h h o x o o o o m a m m m m e m e e e e / p / / { l k g r e f f i a o s f f t r o : i i c a m c c h g } e e e e / / / n { l l d d i i m o e g g o o v h h t r i t t i c / / s e s b n t } t r / a / a i o t { t g c e p e h c r t u o n p p e a e s n r s c t y y } → → → → O 0 t o N - r p / 2 u e O 5 e n F 5 / / F f c a l l o s s e e d Automations# Home Assistant automations are just YAML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# automations.yaml
# Turn on lights when motion detected
- alias : "Office lights on motion"
trigger :
- platform : state
entity_id : binary_sensor.office_motion
to : "on"
condition :
- condition : sun
after : sunset
action :
- service : light.turn_on
target :
entity_id : light.office
data :
brightness_pct : 80
transition : 2
# Turn off after 10 minutes of no motion
- alias : "Office lights off no motion"
trigger :
- platform : state
entity_id : binary_sensor.office_motion
to : "off"
for :
minutes : 10
action :
- service : light.turn_off
target :
entity_id : light.office
Time-based automation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- alias : "Morning routine"
trigger :
- platform : time
at : "06:30:00"
condition :
- condition : state
entity_id : person.rob
state : "home"
- condition : time
weekday :
- mon
- tue
- wed
- thu
- fri
action :
- service : light.turn_on
target :
entity_id : light.bedroom
data :
brightness_pct : 30
color_temp : 400 # warm
- delay : "00:15:00"
- service : light.turn_on
data :
brightness_pct : 100
color_temp : 250 # cooler
Custom Sensors with ESPHome# Turn $5 ESP32 boards into smart sensors:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# esphome/office-sensor.yaml
esphome :
name : office-sensor
platform : ESP32
board : esp32dev
wifi :
ssid : !secret wifi_ssid
password : !secret wifi_password
mqtt :
broker : 192.168.1.100
sensor :
- platform : dht
pin : GPIO4
model : DHT22
temperature :
name : "Office Temperature"
humidity :
name : "Office Humidity"
update_interval : 60s
- platform : adc
pin : GPIO34
name : "Office Light Level"
update_interval : 30s
binary_sensor :
- platform : gpio
pin : GPIO5
name : "Office Motion"
device_class : motion
Flash and deploy:
1
esphome run office-sensor.yaml
API Integration# Control everything via REST:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import requests
HASS_URL = "http://192.168.1.100:8123"
TOKEN = "your_long_lived_token"
headers = {
"Authorization" : f "Bearer { TOKEN } " ,
"Content-Type" : "application/json"
}
# Turn on a light
def turn_on_light ( entity_id , brightness = 255 ):
requests . post (
f " { HASS_URL } /api/services/light/turn_on" ,
headers = headers ,
json = {
"entity_id" : entity_id ,
"brightness" : brightness
}
)
# Get sensor state
def get_state ( entity_id ):
response = requests . get (
f " { HASS_URL } /api/states/ { entity_id } " ,
headers = headers
)
return response . json ()
# Check if anyone's home
state = get_state ( "person.rob" )
if state [ "state" ] == "home" :
turn_on_light ( "light.living_room" , brightness = 200 )
Presence Detection# Know who’s home without creepy tracking:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# configuration.yaml
device_tracker :
- platform : nmap_tracker
hosts : 192.168.1.0 /24
home_interval : 10
- platform : ping
hosts :
rob_phone : 192.168.1.50
person :
- name : Rob
id : rob
device_trackers :
- device_tracker.rob_phone
Router-based detection (more reliable):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Poll UniFi controller
import requests
def get_connected_devices ():
response = requests . get (
"https://unifi.local:8443/api/s/default/stat/sta" ,
headers = { "Cookie" : f "unifises= { session } " },
verify = False
)
return [ d [ "mac" ] for d in response . json ()[ "data" ]]
KNOWN_DEVICES = {
"aa:bb:cc:dd:ee:ff" : "rob_phone" ,
"11:22:33:44:55:66" : "guest_phone"
}
Voice Control Without Cloud# Use local speech recognition with Whisper:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Local voice assistant
import whisper
import pvporcupine
# Wake word detection
porcupine = pvporcupine . create ( keywords = [ "jarvis" ])
# Speech to text
model = whisper . load_model ( "base" )
def process_audio ( audio_file ):
result = model . transcribe ( audio_file )
command = result [ "text" ] . lower ()
if "turn on" in command and "lights" in command :
turn_on_light ( "light.living_room" )
return "Lights on"
if "temperature" in command :
state = get_state ( "sensor.living_room_temperature" )
return f "It's { state [ 'state' ] } degrees"
The Dashboard# Create a wall-mounted control panel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# lovelace dashboard
views :
- title : Home
cards :
- type : weather-forecast
entity : weather.home
- type : entities
title : Lights
entities :
- light.living_room
- light.office
- light.bedroom
- type : thermostat
entity : climate.ecobee
- type : glance
title : Security
entities :
- binary_sensor.front_door
- binary_sensor.garage_door
- binary_sensor.motion_driveway
Security Considerations# Home automation is infrastructure. Treat it like infrastructure:
1
2
3
4
5
6
7
8
9
10
# Separate VLAN for IoT
# In your router/firewall:
# VLAN 10: Trusted (computers, phones)
# VLAN 20: IoT (sensors, smart devices)
# VLAN 30: Guest
# IoT can talk to MQTT broker only
# IoT cannot reach internet (except updates)
# IoT cannot reach trusted VLAN
Secure MQTT:
1
2
3
4
5
6
7
8
# mosquitto.conf
listener 1883 localhost
listener 8883
certfile /certs/server.crt
keyfile /certs/server.key
require_certificate false
allow_anonymous false
password_file /mosquitto/config/passwd
Start Simple# Week 1: Install Home Assistant, add a few smart plugsWeek 2: Add motion sensors, basic automationsWeek 3: Set up MQTT, add custom sensorsMonth 2: Presence detection, voice controlMonth 3: Dashboard on a wall tabletThe rabbit hole is deep. Pace yourself.
Building a smart home? Share your setup on Twitter .