diff --git a/CHANGELOG.md b/CHANGELOG.md index d53b212..26e1075 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.7.8 - 2026-06-15 +- Home-Assistant-WebSocket-Listener deaktiviert den clientseitigen Keepalive- + Ping, damit stabile HA-Verbindungen nicht durch Ping-Timeouts ständig neu + aufgebaut werden +- Fallback-Auswertung läuft bei getrenntem WebSocket kurzfristig alle 5 Sekunden, + damit übernommene Aktoren nicht ohne Steuerung bleiben + ## 0.7.7 - 2026-06-15 - WebSocket-State-Changes lesen jetzt das echte Home-Assistant-Eventformat (`event.data.entity_id`), damit Kontextwechsel wie Türsensoren sofort diff --git a/addon/config.yaml b/addon/config.yaml index f8b6d49..878b0ba 100644 --- a/addon/config.yaml +++ b/addon/config.yaml @@ -1,5 +1,5 @@ name: SillyHome Next -version: "0.7.7" +version: "0.7.8" slug: sillyhome_next description: Lernt automatisch aus deinem Verhalten und steuert freigegebene Aktoren url: http://192.168.6.31:3000/pino/sillyhome-next diff --git a/app/main.py b/app/main.py index 6fd94a3..848702e 100644 --- a/app/main.py +++ b/app/main.py @@ -100,7 +100,7 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]: app = FastAPI( title="SillyHome Next API", description="Lokales Smart-Home-Intelligenzsystem für Home Assistant.", - version="0.7.7", + version="0.7.8", lifespan=lifespan, ) app.state.settings = load_settings() @@ -171,7 +171,7 @@ async def _ha_event_listener(app: FastAPI, client: HaClient) -> None: if ws_status is not None: ws_status.status = "connecting" try: - async with websockets.connect(ws_url) as websocket: + async with websockets.connect(ws_url, ping_interval=None) as websocket: auth_required_msg = await websocket.recv() auth_required_data = json.loads(auth_required_msg) if auth_required_data.get("type") != "auth_required": @@ -252,7 +252,13 @@ async def _fallback_prediction(app: FastAPI) -> None: Dies verhindert kompletten Ausfall der Vorhersagen bei Netzwerkproblemen. """ while True: - await asyncio.sleep(app.state.settings.prediction_interval_seconds) + ws_status = getattr(app.state, "ws_status", None) + websocket_connected = ws_status is not None and ws_status.status == "connected" + await asyncio.sleep( + app.state.settings.prediction_interval_seconds + if websocket_connected + else min(5, app.state.settings.prediction_interval_seconds) + ) # Nur ausführen, wenn WebSocket nicht verbunden ist ws_status = getattr(app.state, "ws_status", None) if ws_status is None or ws_status.status != "connected": diff --git a/pyproject.toml b/pyproject.toml index d5c8fbe..2eb8876 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "sillyhome-next" -version = "0.7.7" +version = "0.7.8" description = "Lokales Smart-Home-Intelligenzsystem für Home Assistant" requires-python = ">=3.11" dependencies = [ diff --git a/tests/test_main.py b/tests/test_main.py index e5ac343..b46bab4 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -61,12 +61,16 @@ def test_ha_event_listener_processes_state_change(tmp_path: Path) -> None: ] ) - with patch("websockets.connect", return_value=fake_ws): + with patch("websockets.connect", return_value=fake_ws) as connect: try: await _ha_event_listener(mock_app, mock_client) except asyncio.CancelledError: pass + connect.assert_called_once_with( + "ws://homeassistant:8123/api/websocket", + ping_interval=None, + ) assert fake_ws.sent == [ {"type": "auth", "access_token": "test-token"}, {"id": 1, "type": "subscribe_events", "event_type": "state_changed"},