Low-Level Design: Smart Home System — Device Management, Automation Rules, and Real-Time Control

Core Entities

Home: home_id, owner_id, name, address, timezone, created_at. Room: room_id, home_id, name, floor_number. Device: device_id, home_id, room_id, name, type (LIGHT, THERMOSTAT, LOCK, CAMERA, SENSOR, SWITCH, PLUG), manufacturer, model, firmware_version, status (ONLINE, OFFLINE, ERROR), last_seen_at, metadata (JSONB: capabilities, config). DeviceState: device_id, attribute (power, brightness, temperature, lock_state, motion_detected), value (JSON), recorded_at. (Time-series: one row per state change.) AutomationRule: rule_id, home_id, name, is_enabled, trigger (JSON: type, conditions), actions (JSON array: device_id, command, params), created_by. AutomationLog: log_id, rule_id, triggered_at, trigger_data (JSON), actions_taken (JSON), status (SUCCESS, PARTIAL, FAILED). Scene: scene_id, home_id, name, actions (JSON array: device commands to execute together), icon.

Device Communication and Protocol Gateway

IoT devices use various protocols: Z-Wave, Zigbee, Wi-Fi (MQTT, HTTP), Matter (new standard). A Protocol Gateway translates between these protocols and the platform’s internal MQTT broker. Architecture: devices connect to the gateway (local hub or cloud). Gateway publishes device events to MQTT topics: home/{home_id}/device/{device_id}/state. Commands are sent to: home/{home_id}/device/{device_id}/command. The backend subscribes to state topics and unsubscribes/publishes commands. MQTT quality of service: QoS 1 (at-least-once delivery) for device state updates. QoS 2 (exactly-once) for critical commands (lock/unlock). Device shadow: maintain the last known state of each device in Redis even when the device is offline (used to show current state in the UI without querying the device directly).

class DeviceService:
    def send_command(self, device_id: str, command: str,
                     params: dict, actor_id: int) -> CommandResult:
        device = self.db.get_device(device_id)
        if device.status == "OFFLINE":
            return CommandResult(success=False,
                                 error="Device is offline")

        # Validate command against device capabilities
        if not self._is_supported(device, command, params):
            return CommandResult(success=False,
                                 error=f"Command {command} not supported")

        # Publish to MQTT command topic
        payload = json.dumps({
            "command": command,
            "params": params,
            "request_id": str(uuid4()),
            "actor_id": actor_id,
            "timestamp": datetime.utcnow().isoformat()
        })
        self.mqtt.publish(
            topic=f"home/{device.home_id}/device/{device_id}/command",
            payload=payload,
            qos=2 if command in ("lock", "unlock", "alarm") else 1
        )

        # Wait for acknowledgment (with timeout)
        ack = self.ack_store.wait(device_id, timeout_ms=5000)
        return CommandResult(success=ack is not None,
                             error=None if ack else "Command timeout")

Automation Rule Engine

Automation rules: IF trigger_condition THEN execute_actions. Trigger types: Device state change: “when motion sensor detects motion”. Schedule: “at 7:00 AM on weekdays”. Sunrise/sunset: “30 minutes before sunset”. Geofence: “when owner leaves home”. Threshold: “when temperature drops below 65°F”. Rule evaluation: device state changes publish to Kafka. The Rule Engine consumer reads events and evaluates active rules for the home. For each rule: check if the trigger condition matches the event. If yes: execute all actions (send device commands). Scheduling: a cron-based scheduler fires schedule-triggered rules at the configured time. Conflict resolution: if multiple rules try to set conflicting device states (one turns lights off, another turns on), last-write-wins by default. Users can set rule priorities to control conflict resolution.

Real-Time Dashboard and WebSocket Updates

The mobile app/web dashboard shows real-time device states. Architecture: device state changes (from MQTT) → Kafka topic (device_states) → State consumer (updates Redis device shadow + DB) AND WebSocket broadcaster. WebSocket broadcaster: for each device state change, find all WebSocket sessions for users who have access to that home, push the update. Connection management: WebSocket connections are maintained per session. The broadcaster checks Redis for active sessions per home (SET: ws_sessions:{home_id} = {session_ids}). Session cleanup: on WebSocket disconnect, remove from the set. TTL on the set (extend on each message) prevents stale entries. For scale: WebSocket gateway is horizontally scaled; a Redis pub/sub channel (SUBSCRIBE home:{home_id}:updates) broadcasts state changes to all gateway instances, which forward to their connected sessions for that home.

See also: Apple Interview Prep

See also: Atlassian Interview Prep

See also: Databricks Interview Prep

See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering

See also: Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence

See also: Meta Interview Guide 2026: Facebook, Instagram, WhatsApp Engineering

Scroll to Top