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
+13
View File
@@ -42,6 +42,8 @@ from wled_controller.storage.weather_source_store import WeatherSourceStore
from wled_controller.storage.asset_store import AssetStore
from wled_controller.core.processing.sync_clock_manager import SyncClockManager
from wled_controller.core.weather.weather_manager import WeatherManager
from wled_controller.storage.home_assistant_store import HomeAssistantStore
from wled_controller.core.home_assistant.ha_manager import HomeAssistantManager
from wled_controller.core.automations.automation_engine import AutomationEngine
from wled_controller.core.mqtt.mqtt_service import MQTTService
from wled_controller.core.devices.mqtt_client import set_mqtt_service
@@ -95,6 +97,8 @@ asset_store.import_prebuilt_sounds(_prebuilt_sounds_dir)
sync_clock_manager = SyncClockManager(sync_clock_store)
weather_manager = WeatherManager(weather_source_store)
ha_store = HomeAssistantStore(db)
ha_manager = HomeAssistantManager(ha_store)
processor_manager = ProcessorManager(
ProcessorDependencies(
@@ -160,6 +164,7 @@ async def lifespan(app: FastAPI):
scene_preset_store=scene_preset_store,
target_store=output_target_store,
device_store=device_store,
ha_manager=ha_manager,
)
# Create auto-backup engine — derive paths from database location so that
@@ -208,6 +213,8 @@ async def lifespan(app: FastAPI):
weather_manager=weather_manager,
update_service=update_service,
asset_store=asset_store,
ha_store=ha_store,
ha_manager=ha_manager,
)
# Register devices in processor manager for health monitoring
@@ -274,6 +281,12 @@ async def lifespan(app: FastAPI):
# where no CRUD happened during the session.
_save_all_stores()
# Stop Home Assistant manager
try:
await ha_manager.shutdown()
except Exception as e:
logger.error(f"Error stopping Home Assistant manager: {e}")
# Stop weather manager
try:
weather_manager.shutdown()