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:

SeM(nQMsTooTsr(qsCBuerinottktHreooar)mleSAwAuisttsocimhsaettsainoZ(tniZgiHbguebbee)/eCZ2a-MmWQeaTrvTae)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:

hEhhhhoxoooomammmmemeeee/p//{lkgreffiaosfftro:iicamcchg}eeee///n{llddiimoeggoovhhtrittic//sesbnt}tr/a/aiot{tgcepehcrtuonppeaesnrsctyy}O0toN-rp/2ueO5enF5//Ffcallosseed

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

  1. Week 1: Install Home Assistant, add a few smart plugs
  2. Week 2: Add motion sensors, basic automations
  3. Week 3: Set up MQTT, add custom sensors
  4. Month 2: Presence detection, voice control
  5. Month 3: Dashboard on a wall tablet

The rabbit hole is deep. Pace yourself.


Building a smart home? Share your setup on Twitter.