Menu
Zurück zum Blog
1 min read
Automatisierung

Home Assistant Integration & Automation

Smart Home Automation mit Home Assistant. WebSocket API, Node-RED Integration, Custom Automations und YAML-Konfiguration.

Home AssistantSmart HomeAutomationNode-REDWebSocket APIYAML
Home Assistant Integration & Automation

Home Assistant Integration & Automation

Meta-Description: Smart Home Automation mit Home Assistant. WebSocket API, Node-RED Integration, Custom Automations und YAML-Konfiguration.

Keywords: Home Assistant, Smart Home, Automation, Node-RED, WebSocket API, YAML, IoT Platform


Einführung

Home Assistant ist die führende Open-Source Smart Home Plattform. Mit über 2.800 Integrations, lokaler Kontrolle und einer aktiven Community bietet sie die perfekte Grundlage für jedes Smart Home Projekt.


Home Assistant Architecture

┌─────────────────────────────────────────────────────────────┐
│              HOME ASSISTANT ARCHITECTURE                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐    │
│  │   Frontend  │    │    Core     │    │  Supervisor │    │
│  │   (React)   │←──→│  (Python)   │←──→│  (Docker)   │    │
│  └─────────────┘    └──────┬──────┘    └─────────────┘    │
│                            │                               │
│  ┌─────────────────────────┼─────────────────────────┐    │
│  │                  Integrations                      │    │
│  ├─────────────────────────┼─────────────────────────┤    │
│  │  ┌────────┐  ┌────────┐ │ ┌────────┐  ┌────────┐ │    │
│  │  │ Zigbee │  │ Z-Wave │ │ │  MQTT  │  │  HTTP  │ │    │
│  │  └────────┘  └────────┘ │ └────────┘  └────────┘ │    │
│  │  ┌────────┐  ┌────────┐ │ ┌────────┐  ┌────────┐ │    │
│  │  │ Thread │  │ Matter │ │ │Bluetooth│ │ Cloud  │ │    │
│  │  └────────┘  └────────┘ │ └────────┘  └────────┘ │    │
│  └───────────────────────────────────────────────────┘    │
│                                                             │
│  Protocols: WebSocket, REST API, MQTT                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Installation & Setup

# Docker Installation
docker run -d \
  --name homeassistant \
  --privileged \
  --restart=unless-stopped \
  -e TZ=Europe/Berlin \
  -v /path/to/config:/config \
  -v /run/dbus:/run/dbus:ro \
  --network=host \
  ghcr.io/home-assistant/home-assistant:stable

# Oder mit Docker Compose
# docker-compose.yml
version: '3'
services:
  homeassistant:
    container_name: homeassistant
    image: "ghcr.io/home-assistant/home-assistant:stable"
    volumes:
      - ./config:/config
      - /etc/localtime:/etc/localtime:ro
      - /run/dbus:/run/dbus:ro
    restart: unless-stopped
    privileged: true
    network_mode: host

WebSocket API Integration

// lib/homeassistant-client.ts
import WebSocket from 'ws';

interface HAMessage {
  id?: number;
  type: string;
  [key: string]: any;
}

interface HAState {
  entity_id: string;
  state: string;
  attributes: Record<string, any>;
  last_changed: string;
  last_updated: string;
}

class HomeAssistantClient {
  private ws: WebSocket | null = null;
  private messageId = 1;
  private pendingRequests: Map<number, {
    resolve: (value: any) => void;
    reject: (error: Error) => void;
  }> = new Map();
  private eventSubscribers: Map<string, Set<(data: any) => void>> = new Map();

  constructor(
    private url: string,
    private token: string
  ) {}

  async connect(): Promise<void> {
    return new Promise((resolve, reject) => {
      this.ws = new WebSocket(this.url);

      this.ws.on('open', () => {
        console.log('Connected to Home Assistant');
      });

      this.ws.on('message', async (data) => {
        const message = JSON.parse(data.toString());
        await this.handleMessage(message, resolve);
      });

      this.ws.on('error', (error) => {
        console.error('WebSocket error:', error);
        reject(error);
      });

      this.ws.on('close', () => {
        console.log('Connection closed');
        this.reconnect();
      });
    });
  }

  private async handleMessage(message: any, onAuth?: (value: void) => void) {
    switch (message.type) {
      case 'auth_required':
        this.send({ type: 'auth', access_token: this.token });
        break;

      case 'auth_ok':
        console.log('Authenticated with Home Assistant');
        onAuth?.();
        break;

      case 'auth_invalid':
        throw new Error('Invalid authentication');

      case 'result':
        const pending = this.pendingRequests.get(message.id);
        if (pending) {
          if (message.success) {
            pending.resolve(message.result);
          } else {
            pending.reject(new Error(message.error?.message));
          }
          this.pendingRequests.delete(message.id);
        }
        break;

      case 'event':
        this.handleEvent(message.event);
        break;
    }
  }

  private handleEvent(event: any) {
    const eventType = event.event_type;
    const subscribers = this.eventSubscribers.get(eventType);

    if (subscribers) {
      subscribers.forEach(callback => callback(event.data));
    }

    // Wildcard subscribers
    const allSubscribers = this.eventSubscribers.get('*');
    if (allSubscribers) {
      allSubscribers.forEach(callback => callback(event));
    }
  }

  private send(message: HAMessage) {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(message));
    }
  }

  private async sendCommand<T>(message: Omit<HAMessage, 'id'>): Promise<T> {
    return new Promise((resolve, reject) => {
      const id = this.messageId++;
      this.pendingRequests.set(id, { resolve, reject });
      this.send({ ...message, id });

      // Timeout nach 30 Sekunden
      setTimeout(() => {
        if (this.pendingRequests.has(id)) {
          this.pendingRequests.delete(id);
          reject(new Error('Request timeout'));
        }
      }, 30000);
    });
  }

  // States abrufen
  async getStates(): Promise<HAState[]> {
    return this.sendCommand({ type: 'get_states' });
  }

  async getState(entityId: string): Promise<HAState | undefined> {
    const states = await this.getStates();
    return states.find(s => s.entity_id === entityId);
  }

  // Services aufrufen
  async callService(
    domain: string,
    service: string,
    serviceData?: Record<string, any>,
    target?: { entity_id?: string | string[] }
  ): Promise<void> {
    await this.sendCommand({
      type: 'call_service',
      domain,
      service,
      service_data: serviceData,
      target
    });
  }

  // Events abonnieren
  async subscribeEvents(
    eventType: string,
    callback: (data: any) => void
  ): Promise<() => void> {
    if (!this.eventSubscribers.has(eventType)) {
      this.eventSubscribers.set(eventType, new Set());

      // Bei HA registrieren
      await this.sendCommand({
        type: 'subscribe_events',
        event_type: eventType === '*' ? undefined : eventType
      });
    }

    this.eventSubscribers.get(eventType)!.add(callback);

    // Unsubscribe Function
    return () => {
      this.eventSubscribers.get(eventType)?.delete(callback);
    };
  }

  // Trigger State Change
  async subscribeStateChanges(
    entityId: string,
    callback: (newState: HAState, oldState: HAState) => void
  ): Promise<() => void> {
    return this.subscribeEvents('state_changed', (data) => {
      if (data.entity_id === entityId) {
        callback(data.new_state, data.old_state);
      }
    });
  }

  private reconnect() {
    setTimeout(() => {
      console.log('Reconnecting...');
      this.connect();
    }, 5000);
  }

  disconnect() {
    this.ws?.close();
  }
}

// Verwendung
const ha = new HomeAssistantClient(
  'ws://homeassistant.local:8123/api/websocket',
  process.env.HA_TOKEN!
);

await ha.connect();

// Alle States abrufen
const states = await ha.getStates();
console.log(`${states.length} entities found`);

// Licht einschalten
await ha.callService('light', 'turn_on', {
  brightness: 255,
  color_temp: 300
}, {
  entity_id: 'light.wohnzimmer'
});

// State Changes beobachten
ha.subscribeStateChanges('sensor.temperature', (newState, oldState) => {
  console.log(`Temperature changed: ${oldState.state} → ${newState.state}`);
});

REST API Integration

// lib/homeassistant-rest.ts
interface HARestConfig {
  baseUrl: string;
  token: string;
}

class HomeAssistantREST {
  private baseUrl: string;
  private headers: HeadersInit;

  constructor(config: HARestConfig) {
    this.baseUrl = config.baseUrl.replace(/\/$/, '');
    this.headers = {
      'Authorization': `Bearer ${config.token}`,
      'Content-Type': 'application/json'
    };
  }

  private async request<T>(
    method: string,
    endpoint: string,
    body?: any
  ): Promise<T> {
    const response = await fetch(`${this.baseUrl}/api${endpoint}`, {
      method,
      headers: this.headers,
      body: body ? JSON.stringify(body) : undefined
    });

    if (!response.ok) {
      throw new Error(`HA API Error: ${response.status}`);
    }

    return response.json();
  }

  // API Status
  async getApiStatus(): Promise<{ message: string }> {
    return this.request('GET', '/');
  }

  // Config
  async getConfig(): Promise<any> {
    return this.request('GET', '/config');
  }

  // States
  async getStates(): Promise<HAState[]> {
    return this.request('GET', '/states');
  }

  async getState(entityId: string): Promise<HAState> {
    return this.request('GET', `/states/${entityId}`);
  }

  async setState(entityId: string, state: string, attributes?: Record<string, any>): Promise<HAState> {
    return this.request('POST', `/states/${entityId}`, {
      state,
      attributes
    });
  }

  // Services
  async getServices(): Promise<Record<string, any>> {
    return this.request('GET', '/services');
  }

  async callService(
    domain: string,
    service: string,
    data?: Record<string, any>
  ): Promise<HAState[]> {
    return this.request('POST', `/services/${domain}/${service}`, data);
  }

  // History
  async getHistory(
    entityId: string,
    startTime?: Date,
    endTime?: Date
  ): Promise<HAState[][]> {
    const params = new URLSearchParams();
    if (startTime) params.set('start_time', startTime.toISOString());
    if (endTime) params.set('end_time', endTime.toISOString());
    params.set('filter_entity_id', entityId);

    return this.request('GET', `/history/period?${params}`);
  }

  // Logbook
  async getLogbook(
    entityId?: string,
    startTime?: Date,
    endTime?: Date
  ): Promise<any[]> {
    const params = new URLSearchParams();
    if (entityId) params.set('entity', entityId);
    if (startTime) params.set('start_time', startTime.toISOString());
    if (endTime) params.set('end_time', endTime.toISOString());

    return this.request('GET', `/logbook?${params}`);
  }

  // Template Rendering
  async renderTemplate(template: string): Promise<string> {
    const result = await this.request<{ result: string }>('POST', '/template', { template });
    return result.result;
  }
}

// API Route für Next.js
// app/api/home/lights/route.ts
import { NextRequest, NextResponse } from 'next/server';

const ha = new HomeAssistantREST({
  baseUrl: process.env.HA_URL!,
  token: process.env.HA_TOKEN!
});

export async function GET() {
  const states = await ha.getStates();
  const lights = states.filter(s => s.entity_id.startsWith('light.'));

  return NextResponse.json(lights.map(light => ({
    id: light.entity_id,
    name: light.attributes.friendly_name,
    state: light.state,
    brightness: light.attributes.brightness,
    color_temp: light.attributes.color_temp
  })));
}

export async function POST(request: NextRequest) {
  const { entityId, action, ...params } = await request.json();

  await ha.callService('light', action, {
    entity_id: entityId,
    ...params
  });

  return NextResponse.json({ success: true });
}

YAML Automations

# automations.yaml

# Licht bei Bewegung einschalten
- id: 'motion_light_on'
  alias: 'Flur Licht bei Bewegung'
  description: 'Schaltet das Flur-Licht bei Bewegung ein'
  trigger:
    - platform: state
      entity_id: binary_sensor.flur_motion
      to: 'on'
  condition:
    - condition: state
      entity_id: sun.sun
      state: 'below_horizon'
    - condition: state
      entity_id: input_boolean.guest_mode
      state: 'off'
  action:
    - service: light.turn_on
      target:
        entity_id: light.flur
      data:
        brightness_pct: 80
        transition: 2
    - delay: '00:05:00'
    - service: light.turn_off
      target:
        entity_id: light.flur
      data:
        transition: 5
  mode: restart

# Temperatur-basierte Heizungssteuerung
- id: 'heating_control'
  alias: 'Heizung Temperatur Kontrolle'
  trigger:
    - platform: numeric_state
      entity_id: sensor.wohnzimmer_temperature
      below: 20
  condition:
    - condition: state
      entity_id: input_boolean.heating_enabled
      state: 'on'
    - condition: time
      after: '06:00:00'
      before: '22:00:00'
  action:
    - service: climate.set_temperature
      target:
        entity_id: climate.wohnzimmer
      data:
        temperature: 22
        hvac_mode: heat

# Guten Morgen Routine
- id: 'morning_routine'
  alias: 'Guten Morgen Routine'
  trigger:
    - platform: time
      at: input_datetime.wakeup_time
  condition:
    - condition: state
      entity_id: input_boolean.alarm_enabled
      state: 'on'
    - condition: state
      entity_id: person.user
      state: 'home'
  action:
    - service: light.turn_on
      target:
        entity_id: light.schlafzimmer
      data:
        brightness_pct: 10
        color_temp: 500
    - delay: '00:05:00'
    - service: light.turn_on
      data:
        brightness_pct: 50
        color_temp: 370
        transition: 300
    - service: media_player.play_media
      target:
        entity_id: media_player.schlafzimmer_speaker
      data:
        media_content_type: music
        media_content_id: 'spotify:playlist:morning'
    - service: notify.mobile_app
      data:
        message: "Guten Morgen! Es sind {{ states('sensor.outside_temperature') }}°C draußen."

Node-RED Integration

// Node-RED Flow für Home Assistant
[
  {
    "id": "ha-server",
    "type": "server",
    "name": "Home Assistant",
    "addon": true
  },
  {
    "id": "motion-trigger",
    "type": "server-state-changed",
    "name": "Motion Detected",
    "server": "ha-server",
    "entityId": "binary_sensor.motion",
    "entityIdType": "exact",
    "outputInitially": false,
    "stateType": "str",
    "ifState": "on",
    "ifStateType": "str",
    "outputOnlyOnStateChange": true,
    "wires": [["check-conditions"]]
  },
  {
    "id": "check-conditions",
    "type": "api-current-state",
    "name": "Check Sun State",
    "server": "ha-server",
    "entityId": "sun.sun",
    "outputProperties": [
      {
        "property": "payload",
        "propertyType": "msg",
        "value": "",
        "valueType": "entityState"
      }
    ],
    "wires": [["filter-night"]]
  },
  {
    "id": "filter-night",
    "type": "switch",
    "name": "Night Only",
    "property": "payload",
    "rules": [
      { "t": "eq", "v": "below_horizon", "vt": "str" }
    ],
    "wires": [["turn-on-light"]]
  },
  {
    "id": "turn-on-light",
    "type": "api-call-service",
    "name": "Turn On Light",
    "server": "ha-server",
    "service_domain": "light",
    "service": "turn_on",
    "entityId": "light.hallway",
    "data": "{\"brightness_pct\": 80, \"transition\": 2}",
    "wires": [[]]
  }
]
// Custom Node-RED Node
import { Red, Node, NodeProperties } from 'node-red';

interface SmartLightConfig extends NodeProperties {
  server: string;
  entityId: string;
  brightness: number;
  colorTemp: number;
}

module.exports = function(RED: Red) {
  function SmartLightNode(this: Node, config: SmartLightConfig) {
    RED.nodes.createNode(this, config);

    const server = RED.nodes.getNode(config.server) as any;

    this.on('input', async (msg: any) => {
      try {
        const brightness = msg.brightness || config.brightness;
        const colorTemp = msg.colorTemp || config.colorTemp;

        await server.websocket.callService('light', 'turn_on', {
          entity_id: config.entityId,
          brightness_pct: brightness,
          color_temp: colorTemp
        });

        this.status({ fill: 'green', shape: 'dot', text: 'Light on' });
        this.send({ ...msg, payload: { success: true } });
      } catch (error) {
        this.status({ fill: 'red', shape: 'ring', text: 'Error' });
        this.error(error);
      }
    });
  }

  RED.nodes.registerType('smart-light', SmartLightNode);
};

Custom Integration Development

# custom_components/my_sensor/sensor.py
"""Custom sensor integration for Home Assistant."""
from homeassistant.components.sensor import (
    SensorEntity,
    SensorDeviceClass,
    SensorStateClass,
)
from homeassistant.const import UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback

async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up the sensor platform."""
    async_add_entities([
        MySensor(config_entry),
    ])

class MySensor(SensorEntity):
    """Representation of a custom sensor."""

    _attr_name = "My Custom Sensor"
    _attr_device_class = SensorDeviceClass.TEMPERATURE
    _attr_state_class = SensorStateClass.MEASUREMENT
    _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS

    def __init__(self, config_entry: ConfigEntry) -> None:
        """Initialize the sensor."""
        self._attr_unique_id = f"{config_entry.entry_id}_temperature"
        self._attr_native_value = None

    async def async_update(self) -> None:
        """Fetch new state data for the sensor."""
        # API Call oder Sensor-Abfrage
        self._attr_native_value = await self._fetch_temperature()

    async def _fetch_temperature(self) -> float:
        """Fetch temperature from external source."""
        # Implementierung
        return 21.5

Dashboard Configuration

# ui-lovelace.yaml
title: Smart Home
views:
  - title: Übersicht
    path: overview
    icon: mdi:home
    cards:
      - type: weather-forecast
        entity: weather.home

      - type: grid
        columns: 3
        cards:
          - type: button
            entity: light.wohnzimmer
            name: Wohnzimmer
            icon: mdi:sofa
            tap_action:
              action: toggle

          - type: button
            entity: light.schlafzimmer
            name: Schlafzimmer
            icon: mdi:bed

          - type: button
            entity: script.movie_mode
            name: Kino Modus
            icon: mdi:movie

      - type: entities
        title: Temperaturen
        entities:
          - entity: sensor.wohnzimmer_temperature
          - entity: sensor.schlafzimmer_temperature
          - entity: sensor.aussen_temperature

      - type: history-graph
        title: Temperaturverlauf
        hours_to_show: 24
        entities:
          - entity: sensor.wohnzimmer_temperature
          - entity: sensor.aussen_temperature

Fazit

Home Assistant bietet:

  1. Lokale Kontrolle: Keine Cloud-Abhängigkeit
  2. 2800+ Integrations: Alle Smart Home Protokolle
  3. Flexible APIs: WebSocket, REST, MQTT
  4. Node-RED: Visual Automation Flows

Die perfekte Plattform für Smart Home Projekte.


Bildprompts

  1. "Smart home dashboard with device controls, Home Assistant interface"
  2. "Automation flow connecting sensors and actuators, Node-RED visual programming"
  3. "Home network diagram with IoT devices, local smart home server"

Quellen