diff --git a/gateway/platforms/homeassistant.py b/gateway/platforms/homeassistant.py index 749cdf1e..08dfa099 100644 --- a/gateway/platforms/homeassistant.py +++ b/gateway/platforms/homeassistant.py @@ -17,6 +17,7 @@ import json import logging import os import time +import uuid from datetime import datetime from typing import Any, Dict, List, Optional, Set @@ -228,6 +229,8 @@ class HomeAssistantAdapter(BasePlatformAdapter): async def _read_events(self) -> None: """Read events from WebSocket until disconnected.""" + if self._ws is None or self._ws.closed: + return async for ws_msg in self._ws: if ws_msg.type == aiohttp.WSMsgType.TEXT: try: @@ -390,7 +393,7 @@ class HomeAssistantAdapter(BasePlatformAdapter): timeout=aiohttp.ClientTimeout(total=10), ) as resp: if resp.status < 300: - return SendResult(success=True, message_id=str(self._next_id())) + return SendResult(success=True, message_id=uuid.uuid4().hex[:12]) else: body = await resp.text() return SendResult(success=False, error=f"HTTP {resp.status}: {body}") diff --git a/gateway/run.py b/gateway/run.py index 76ed3666..198629ce 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -490,6 +490,12 @@ class GatewayRunner: 4. Global allow-all (GATEWAY_ALLOW_ALL_USERS=true) 5. Default: deny """ + # Home Assistant events are system-generated (state changes), not + # user-initiated messages. The HASS_TOKEN already authenticates the + # connection, so HA events are always authorized. + if source.platform == Platform.HOMEASSISTANT: + return True + user_id = source.user_id if not user_id: return False diff --git a/tests/tools/test_homeassistant_tool.py b/tests/tools/test_homeassistant_tool.py index 6235474e..b57df069 100644 --- a/tests/tools/test_homeassistant_tool.py +++ b/tests/tools/test_homeassistant_tool.py @@ -130,13 +130,13 @@ class TestBuildServicePayload: payload = _build_service_payload() assert payload == {} - def test_data_does_not_overwrite_entity_id(self): + def test_entity_id_param_takes_precedence_over_data(self): payload = _build_service_payload( entity_id="light.a", data={"entity_id": "light.b"}, ) - # data.update overwrites entity_id set earlier - assert payload["entity_id"] == "light.b" + # explicit entity_id parameter wins over data["entity_id"] + assert payload["entity_id"] == "light.a" # --------------------------------------------------------------------------- diff --git a/tools/homeassistant_tool.py b/tools/homeassistant_tool.py index 4a01382f..b351cfec 100644 --- a/tools/homeassistant_tool.py +++ b/tools/homeassistant_tool.py @@ -106,10 +106,11 @@ def _build_service_payload( ) -> Dict[str, Any]: """Build the JSON payload for a HA service call.""" payload: Dict[str, Any] = {} - if entity_id: - payload["entity_id"] = entity_id if data: payload.update(data) + # entity_id parameter takes precedence over data["entity_id"] + if entity_id: + payload["entity_id"] = entity_id return payload