python-homely 0.1.7__py3-none-any.whl → 0.1.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- homely/client.py +14 -2
- homely/websocket.py +16 -7
- {python_homely-0.1.7.dist-info → python_homely-0.1.9.dist-info}/METADATA +1 -1
- python_homely-0.1.9.dist-info/RECORD +11 -0
- python_homely-0.1.7.dist-info/RECORD +0 -11
- {python_homely-0.1.7.dist-info → python_homely-0.1.9.dist-info}/WHEEL +0 -0
- {python_homely-0.1.7.dist-info → python_homely-0.1.9.dist-info}/licenses/LICENSE +0 -0
- {python_homely-0.1.7.dist-info → python_homely-0.1.9.dist-info}/top_level.txt +0 -0
homely/client.py
CHANGED
|
@@ -189,6 +189,7 @@ class HomelyClient:
|
|
|
189
189
|
payload=payload,
|
|
190
190
|
invalid_reason="invalid_refresh_token",
|
|
191
191
|
action="Token refresh",
|
|
192
|
+
extra_headers={"X-Forwarded-For": "0.0.0.0"},
|
|
192
193
|
)
|
|
193
194
|
|
|
194
195
|
async def fetch_refresh_token(self, refresh_token: str) -> dict[str, Any] | None:
|
|
@@ -217,12 +218,15 @@ class HomelyClient:
|
|
|
217
218
|
payload: dict[str, Any],
|
|
218
219
|
invalid_reason: TokenFailureReason,
|
|
219
220
|
action: str,
|
|
221
|
+
extra_headers: dict[str, str] | None = None,
|
|
220
222
|
) -> TokenEndpointResult:
|
|
221
223
|
"""Post to a token endpoint and return a detailed typed result."""
|
|
222
224
|
url = f"{self._base_url}{endpoint}"
|
|
223
225
|
|
|
224
226
|
try:
|
|
225
|
-
async with self._session.post(
|
|
227
|
+
async with self._session.post(
|
|
228
|
+
url, json=payload, headers=extra_headers, timeout=self._timeout
|
|
229
|
+
) as response:
|
|
226
230
|
body_preview = await self._response_text_preview(response)
|
|
227
231
|
if response.status in (200, 201):
|
|
228
232
|
try:
|
|
@@ -442,10 +446,18 @@ class HomelyClient:
|
|
|
442
446
|
)
|
|
443
447
|
return None, response.status
|
|
444
448
|
return parsed, response.status
|
|
449
|
+
retry_after = response.headers.get("Retry-After")
|
|
450
|
+
try:
|
|
451
|
+
body = await response.text()
|
|
452
|
+
except Exception:
|
|
453
|
+
body = "<unreadable>"
|
|
445
454
|
_LOGGER.debug(
|
|
446
|
-
"Location data fetch failed with status=%s location_id=%s"
|
|
455
|
+
"Location data fetch failed with status=%s location_id=%s "
|
|
456
|
+
"retry_after=%s body=%s",
|
|
447
457
|
response.status,
|
|
448
458
|
_log_identifier(location_id),
|
|
459
|
+
retry_after,
|
|
460
|
+
body[:500] if body else None,
|
|
449
461
|
)
|
|
450
462
|
return None, response.status
|
|
451
463
|
except (aiohttp.ClientError, TimeoutError) as err:
|
homely/websocket.py
CHANGED
|
@@ -89,12 +89,14 @@ class HomelyWebSocket:
|
|
|
89
89
|
status_update_callback: Callable[[str, str | None], None] | None = None,
|
|
90
90
|
context_id: str | None = None,
|
|
91
91
|
entry_id: str | None = None,
|
|
92
|
+
partner_code: int | str | None = None,
|
|
92
93
|
) -> None:
|
|
93
94
|
"""Initialize WebSocket client."""
|
|
94
95
|
self.context_id = context_id or entry_id
|
|
95
96
|
self.entry_id = self.context_id
|
|
96
97
|
self.location_id = location_id
|
|
97
98
|
self.token = token
|
|
99
|
+
self.partner_code = partner_code
|
|
98
100
|
self.on_data_update = on_data_update
|
|
99
101
|
self.socket: Any | None = None
|
|
100
102
|
self._is_closing = False
|
|
@@ -368,10 +370,13 @@ class HomelyWebSocket:
|
|
|
368
370
|
|
|
369
371
|
self._set_status("Connecting")
|
|
370
372
|
try:
|
|
373
|
+
# SECURITY: never enable the engineio logger. It logs the full
|
|
374
|
+
# connection URL, which carries the bearer token in the query
|
|
375
|
+
# string (?token=Bearer <access_token>), leaking it to HA logs.
|
|
371
376
|
self.socket = socketio.AsyncClient(
|
|
372
377
|
reconnection=False,
|
|
373
378
|
logger=False,
|
|
374
|
-
engineio_logger=
|
|
379
|
+
engineio_logger=False,
|
|
375
380
|
)
|
|
376
381
|
|
|
377
382
|
async def connect() -> None:
|
|
@@ -405,12 +410,16 @@ class HomelyWebSocket:
|
|
|
405
410
|
self.socket.on("connect_error", connect_error)
|
|
406
411
|
|
|
407
412
|
bearer_token = self._bearer_value(self.token)
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
)
|
|
413
|
+
query_params: dict[str, str] = {
|
|
414
|
+
"locationId": str(self.location_id),
|
|
415
|
+
"token": bearer_token,
|
|
416
|
+
}
|
|
417
|
+
# NOTE: do NOT send a "partner" query parameter. Homely's websocket
|
|
418
|
+
# server rejects it ("Partner <code> is not supported") and closes
|
|
419
|
+
# the connection immediately, causing a connect/disconnect loop.
|
|
420
|
+
# partner_code is accepted for API compatibility but intentionally
|
|
421
|
+
# unused here.
|
|
422
|
+
query = urlencode(query_params)
|
|
414
423
|
url = f"{self.websocket_url}?{query}"
|
|
415
424
|
_LOGGER.debug("WebSocket connecting %s to %s", self._ctx(), self.websocket_url)
|
|
416
425
|
await asyncio.wait_for(
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
homely/__init__.py,sha256=TK4sclUiHqDewtsOenxS9KNLkprZxqbiYkh__JeNmMA,1023
|
|
2
|
+
homely/client.py,sha256=4kDsZkj3P7z6iNlWZJX9De5GiRATD3GdY_beEXC_ZAg,18781
|
|
3
|
+
homely/exceptions.py,sha256=w6y5MC8yjPe5QtI-YEKUsbAPF1D0ARWx6ZkRsLTjrgQ,1040
|
|
4
|
+
homely/models.py,sha256=8chNpjo1SF4TGww7XPWFUK3kXILH3bSmUnjNteyfsys,1842
|
|
5
|
+
homely/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
6
|
+
homely/websocket.py,sha256=Zf-NcRO_pEqb9A2LEPVyeqYbMaAeUVFL_qBrLuJI9OI,20262
|
|
7
|
+
python_homely-0.1.9.dist-info/licenses/LICENSE,sha256=nNVHKvQjryAonWsz5TbhzLd6M2kZaoNUvhxz92MgeAA,1079
|
|
8
|
+
python_homely-0.1.9.dist-info/METADATA,sha256=nAPt3MWkZThc8lpYyqJXWzNBSMNkr5ZzNJiKfuT0mX8,5239
|
|
9
|
+
python_homely-0.1.9.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
10
|
+
python_homely-0.1.9.dist-info/top_level.txt,sha256=auE-j6ghVMdT_jAw04jiXthgbuLpi-jYBU0fCkKvREQ,7
|
|
11
|
+
python_homely-0.1.9.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
homely/__init__.py,sha256=TK4sclUiHqDewtsOenxS9KNLkprZxqbiYkh__JeNmMA,1023
|
|
2
|
-
homely/client.py,sha256=iqM1ChFEVez5PJcvWk8BlkkVx4bekaQacBa-rAvSxpo,18276
|
|
3
|
-
homely/exceptions.py,sha256=w6y5MC8yjPe5QtI-YEKUsbAPF1D0ARWx6ZkRsLTjrgQ,1040
|
|
4
|
-
homely/models.py,sha256=8chNpjo1SF4TGww7XPWFUK3kXILH3bSmUnjNteyfsys,1842
|
|
5
|
-
homely/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
6
|
-
homely/websocket.py,sha256=X84KhYVsmqwSmab18D-MgMKi2W0J8aMgRcPx417CPCY,19624
|
|
7
|
-
python_homely-0.1.7.dist-info/licenses/LICENSE,sha256=nNVHKvQjryAonWsz5TbhzLd6M2kZaoNUvhxz92MgeAA,1079
|
|
8
|
-
python_homely-0.1.7.dist-info/METADATA,sha256=Rp3rNA6o9CjOP1oD2LmQjTBS6OPyVUt1YqZLySN_OwU,5239
|
|
9
|
-
python_homely-0.1.7.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
10
|
-
python_homely-0.1.7.dist-info/top_level.txt,sha256=auE-j6ghVMdT_jAw04jiXthgbuLpi-jYBU0fCkKvREQ,7
|
|
11
|
-
python_homely-0.1.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|