fix: resolve 4 bugs found in HA integration code review
- Auto-authorize HA events in gateway (system-generated, not user messages) - Guard _read_events against None/closed WebSocket after failed reconnect - Use UUID for send() message_id instead of polluting WS sequence counter - entity_id parameter now takes precedence over data["entity_id"]
This commit is contained in:
parent
b32c642af3
commit
2390728cc3
4 changed files with 16 additions and 6 deletions
|
|
@ -17,6 +17,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Dict, List, Optional, Set
|
from typing import Any, Dict, List, Optional, Set
|
||||||
|
|
||||||
|
|
@ -228,6 +229,8 @@ class HomeAssistantAdapter(BasePlatformAdapter):
|
||||||
|
|
||||||
async def _read_events(self) -> None:
|
async def _read_events(self) -> None:
|
||||||
"""Read events from WebSocket until disconnected."""
|
"""Read events from WebSocket until disconnected."""
|
||||||
|
if self._ws is None or self._ws.closed:
|
||||||
|
return
|
||||||
async for ws_msg in self._ws:
|
async for ws_msg in self._ws:
|
||||||
if ws_msg.type == aiohttp.WSMsgType.TEXT:
|
if ws_msg.type == aiohttp.WSMsgType.TEXT:
|
||||||
try:
|
try:
|
||||||
|
|
@ -390,7 +393,7 @@ class HomeAssistantAdapter(BasePlatformAdapter):
|
||||||
timeout=aiohttp.ClientTimeout(total=10),
|
timeout=aiohttp.ClientTimeout(total=10),
|
||||||
) as resp:
|
) as resp:
|
||||||
if resp.status < 300:
|
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:
|
else:
|
||||||
body = await resp.text()
|
body = await resp.text()
|
||||||
return SendResult(success=False, error=f"HTTP {resp.status}: {body}")
|
return SendResult(success=False, error=f"HTTP {resp.status}: {body}")
|
||||||
|
|
|
||||||
|
|
@ -490,6 +490,12 @@ class GatewayRunner:
|
||||||
4. Global allow-all (GATEWAY_ALLOW_ALL_USERS=true)
|
4. Global allow-all (GATEWAY_ALLOW_ALL_USERS=true)
|
||||||
5. Default: deny
|
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
|
user_id = source.user_id
|
||||||
if not user_id:
|
if not user_id:
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -130,13 +130,13 @@ class TestBuildServicePayload:
|
||||||
payload = _build_service_payload()
|
payload = _build_service_payload()
|
||||||
assert 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(
|
payload = _build_service_payload(
|
||||||
entity_id="light.a",
|
entity_id="light.a",
|
||||||
data={"entity_id": "light.b"},
|
data={"entity_id": "light.b"},
|
||||||
)
|
)
|
||||||
# data.update overwrites entity_id set earlier
|
# explicit entity_id parameter wins over data["entity_id"]
|
||||||
assert payload["entity_id"] == "light.b"
|
assert payload["entity_id"] == "light.a"
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -106,10 +106,11 @@ def _build_service_payload(
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Build the JSON payload for a HA service call."""
|
"""Build the JSON payload for a HA service call."""
|
||||||
payload: Dict[str, Any] = {}
|
payload: Dict[str, Any] = {}
|
||||||
if entity_id:
|
|
||||||
payload["entity_id"] = entity_id
|
|
||||||
if data:
|
if data:
|
||||||
payload.update(data)
|
payload.update(data)
|
||||||
|
# entity_id parameter takes precedence over data["entity_id"]
|
||||||
|
if entity_id:
|
||||||
|
payload["entity_id"] = entity_id
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue