feat: Home Assistant integration — WebSocket connection, automation conditions, UI

Add full Home Assistant integration via WebSocket API:
- HARuntime: persistent WebSocket client with auth, auto-reconnect, entity state cache
- HAManager: ref-counted runtime pool (like WeatherManager)
- HomeAssistantCondition: new automation trigger type matching entity states
- REST API: CRUD for HA sources + /test, /entities, /status endpoints
- /api/v1/system/integrations-status: combined MQTT + HA dashboard indicators
- Frontend: HA Sources tab in Streams, condition type in automation editor
- Modal editor with host, token, SSL, entity filters
- websockets>=13.0 dependency added
This commit is contained in:
2026-03-27 22:42:48 +03:00
parent f3d07fc47f
commit 2153dde4b7
26 changed files with 1912 additions and 119 deletions
@@ -21,6 +21,8 @@ from wled_controller.api.dependencies import (
get_automation_store,
get_color_strip_store,
get_device_store,
get_ha_manager,
get_ha_store,
get_output_target_store,
get_pattern_template_store,
get_picture_source_store,
@@ -311,3 +313,52 @@ def list_api_keys(_: AuthRequired):
for label, key in config.auth.api_keys.items()
]
return {"keys": keys, "count": len(keys)}
@router.get("/api/v1/system/integrations-status", tags=["System"])
async def get_integrations_status(
_: AuthRequired,
ha_store=Depends(get_ha_store),
ha_manager=Depends(get_ha_manager),
):
"""Return connection status for external integrations (MQTT, Home Assistant).
Used by the dashboard to show connectivity indicators.
"""
from wled_controller.core.devices.mqtt_client import get_mqtt_service
# MQTT status
mqtt_service = get_mqtt_service()
mqtt_config = get_config().mqtt
mqtt_status = {
"enabled": mqtt_config.enabled,
"connected": mqtt_service.is_connected if mqtt_service else False,
"broker": (
f"{mqtt_config.broker_host}:{mqtt_config.broker_port}" if mqtt_config.enabled else None
),
}
# Home Assistant status
ha_sources = ha_store.get_all_sources()
ha_connections = ha_manager.get_connection_status()
ha_status_map = {s["source_id"]: s for s in ha_connections}
ha_items = []
for source in ha_sources:
status = ha_status_map.get(source.id)
ha_items.append(
{
"source_id": source.id,
"name": source.name,
"connected": status["connected"] if status else False,
"entity_count": status["entity_count"] if status else 0,
}
)
return {
"mqtt": mqtt_status,
"home_assistant": {
"sources": ha_items,
"total": len(ha_sources),
"connected": sum(1 for s in ha_items if s["connected"]),
},
}