Initial commit: Portfolio Website
Vollständige Next.js 15 Portfolio-Website mit: - Blog-System mit 100+ Artikeln - Supabase-Integration - Responsive Design mit Tailwind CSS - TypeScript-Konfiguration - Testing-Setup mit Vitest und Playwright Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,786 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```json
|
||||
// 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": [[]]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
- [Home Assistant Documentation](https://www.home-assistant.io/)
|
||||
- [Home Assistant 2026.1 Release](https://www.home-assistant.io/blog/2026/01/07/release-20261)
|
||||
- [Node-RED Home Assistant](https://flows.nodered.org/node/node-red-contrib-home-assistant)
|
||||
- [Home Assistant WebSocket API](https://developers.home-assistant.io/docs/api/websocket)
|
||||
Reference in New Issue
Block a user