pyhive-integration 1.0.8.dev7__tar.gz → 1.0.8.dev9__tar.gz
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.
- {pyhive_integration-1.0.8.dev7/pyhive_integration.egg-info → pyhive_integration-1.0.8.dev9}/PKG-INFO +1 -1
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9/pyhive_integration.egg-info}/PKG-INFO +1 -1
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/setup.py +1 -1
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/session.py +84 -17
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/LICENSE +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/MANIFEST.in +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/README.md +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/pyhive_integration.egg-info/SOURCES.txt +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/pyhive_integration.egg-info/dependency_links.txt +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/pyhive_integration.egg-info/requires.txt +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/pyhive_integration.egg-info/top_level.txt +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/pyproject.toml +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/requirements.txt +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/requirements_test.txt +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/setup.cfg +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/__init__.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/action.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/alarm.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/api/__init__.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/api/hive_api.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/api/hive_async_api.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/api/hive_auth.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/api/hive_auth_async.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/camera.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/device_attributes.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/heating.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/__init__.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/const.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/debugger.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/hive_exceptions.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/hive_helper.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/hivedataclasses.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/map.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/hive.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/hotwater.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/hub.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/light.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/plug.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/sensor.py +0 -0
- {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/tests/test_hub.py +0 -0
|
@@ -17,7 +17,7 @@ def requirements_from_file(filename="requirements.txt"):
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
setup(
|
|
20
|
-
version="1.0.8.
|
|
20
|
+
version="1.0.8.dev9",
|
|
21
21
|
packages=["apyhiveapi", "apyhiveapi.api", "apyhiveapi.helper"],
|
|
22
22
|
package_dir={"apyhiveapi": "src"},
|
|
23
23
|
package_data={"data": ["*.json"]},
|
|
@@ -108,6 +108,8 @@ class HiveSession:
|
|
|
108
108
|
self.deviceList = {}
|
|
109
109
|
self.hub_id = None
|
|
110
110
|
self._lastPollSlow = False
|
|
111
|
+
self._slowPollThreshold = 3
|
|
112
|
+
self._refreshThreshold = 0.90
|
|
111
113
|
|
|
112
114
|
def openFile(self, file: str):
|
|
113
115
|
"""Open a file.
|
|
@@ -226,6 +228,29 @@ class HiveSession:
|
|
|
226
228
|
if "ExpiresIn" in data:
|
|
227
229
|
self.tokens.tokenExpiry = timedelta(seconds=data["ExpiresIn"])
|
|
228
230
|
|
|
231
|
+
_LOGGER.debug(
|
|
232
|
+
"updateTokens — IdToken: len=%d tail=…%s | "
|
|
233
|
+
"AccessToken: len=%d tail=…%s | "
|
|
234
|
+
"RefreshToken: %s | "
|
|
235
|
+
"ExpiresIn: %s | tokenCreated: %s | tokenExpiry: %s",
|
|
236
|
+
len(self.tokens.tokenData.get("token", "")),
|
|
237
|
+
self.tokens.tokenData.get("token", "")[-4:],
|
|
238
|
+
len(self.tokens.tokenData.get("accessToken", "")),
|
|
239
|
+
self.tokens.tokenData.get("accessToken", "")[-4:],
|
|
240
|
+
(
|
|
241
|
+
"present (len=%d tail=…%s)"
|
|
242
|
+
% (
|
|
243
|
+
len(self.tokens.tokenData.get("refreshToken", "")),
|
|
244
|
+
self.tokens.tokenData.get("refreshToken", "")[-4:],
|
|
245
|
+
)
|
|
246
|
+
if self.tokens.tokenData.get("refreshToken")
|
|
247
|
+
else "not present"
|
|
248
|
+
),
|
|
249
|
+
data.get("ExpiresIn", "N/A"),
|
|
250
|
+
self.tokens.tokenCreated,
|
|
251
|
+
self.tokens.tokenExpiry,
|
|
252
|
+
)
|
|
253
|
+
|
|
229
254
|
return self.tokens
|
|
230
255
|
|
|
231
256
|
async def login(self):
|
|
@@ -255,7 +280,8 @@ class HiveSession:
|
|
|
255
280
|
raise
|
|
256
281
|
|
|
257
282
|
if result and "AuthenticationResult" in result:
|
|
258
|
-
|
|
283
|
+
auth_keys = list(result["AuthenticationResult"].keys())
|
|
284
|
+
_LOGGER.debug("Login successful — AuthenticationResult keys: %s", auth_keys)
|
|
259
285
|
await self.updateTokens(result)
|
|
260
286
|
return result
|
|
261
287
|
|
|
@@ -284,7 +310,10 @@ class HiveSession:
|
|
|
284
310
|
raise
|
|
285
311
|
|
|
286
312
|
if result and "AuthenticationResult" in result:
|
|
287
|
-
|
|
313
|
+
auth_keys = list(result["AuthenticationResult"].keys())
|
|
314
|
+
_LOGGER.debug(
|
|
315
|
+
"2FA login successful — AuthenticationResult keys: %s", auth_keys
|
|
316
|
+
)
|
|
288
317
|
await self.updateTokens(result)
|
|
289
318
|
return result
|
|
290
319
|
|
|
@@ -311,9 +340,11 @@ class HiveSession:
|
|
|
311
340
|
raise
|
|
312
341
|
|
|
313
342
|
if result and "AuthenticationResult" in result:
|
|
314
|
-
|
|
343
|
+
auth_keys = list(result["AuthenticationResult"].keys())
|
|
344
|
+
_LOGGER.debug(
|
|
345
|
+
"Device login successful — AuthenticationResult keys: %s", auth_keys
|
|
346
|
+
)
|
|
315
347
|
await self.updateTokens(result)
|
|
316
|
-
self.tokens.tokenExpiry = timedelta(seconds=0)
|
|
317
348
|
return result
|
|
318
349
|
|
|
319
350
|
async def _retryDeviceLogin(self):
|
|
@@ -345,9 +376,14 @@ class HiveSession:
|
|
|
345
376
|
)
|
|
346
377
|
raise HiveReauthRequired from last_err
|
|
347
378
|
|
|
348
|
-
|
|
379
|
+
await self.hiveRefreshTokens(force_refresh=True)
|
|
380
|
+
|
|
381
|
+
async def hiveRefreshTokens(self, force_refresh: bool = False):
|
|
349
382
|
"""Refresh Hive tokens.
|
|
350
383
|
|
|
384
|
+
Args:
|
|
385
|
+
force_refresh (bool): Whether to force a token refresh regardless of expiry.
|
|
386
|
+
|
|
351
387
|
Returns:
|
|
352
388
|
boolean: True/False if update was successful
|
|
353
389
|
"""
|
|
@@ -356,29 +392,33 @@ class HiveSession:
|
|
|
356
392
|
if self.config.file:
|
|
357
393
|
return None
|
|
358
394
|
else:
|
|
359
|
-
expiry_time = self.tokens.tokenCreated + (
|
|
360
|
-
|
|
395
|
+
expiry_time = self.tokens.tokenCreated + (
|
|
396
|
+
self.tokens.tokenExpiry * self._refreshThreshold
|
|
397
|
+
)
|
|
398
|
+
# Refresh at 90% of token lifetime to prevent expiration during API calls
|
|
361
399
|
_LOGGER.debug(
|
|
362
400
|
"Session token expiry time ( Current: %s | Expiry: %s)",
|
|
363
401
|
datetime.now(),
|
|
364
402
|
expiry_time,
|
|
365
403
|
)
|
|
366
|
-
if datetime.now() >= expiry_time:
|
|
404
|
+
if datetime.now() >= expiry_time or force_refresh:
|
|
367
405
|
async with self._refreshLock:
|
|
368
406
|
# Re-check after acquiring lock — another caller may have already refreshed
|
|
369
407
|
expiry_time = self.tokens.tokenCreated + (
|
|
370
|
-
self.tokens.tokenExpiry *
|
|
408
|
+
self.tokens.tokenExpiry * self._refreshThreshold
|
|
371
409
|
)
|
|
372
|
-
if datetime.now() < expiry_time:
|
|
410
|
+
if datetime.now() < expiry_time and not force_refresh:
|
|
373
411
|
return result
|
|
374
412
|
actual_expiry = self.tokens.tokenCreated + self.tokens.tokenExpiry
|
|
375
413
|
_LOGGER.debug(
|
|
376
414
|
"Session Token created: %s | Actual expiry: %s | "
|
|
377
|
-
"Early refresh (
|
|
415
|
+
"Early refresh (×%s): %s | Now: %s | Force refresh: %s",
|
|
378
416
|
self.tokens.tokenCreated,
|
|
379
417
|
actual_expiry,
|
|
418
|
+
self._refreshThreshold,
|
|
380
419
|
expiry_time,
|
|
381
420
|
datetime.now(),
|
|
421
|
+
force_refresh,
|
|
382
422
|
)
|
|
383
423
|
try:
|
|
384
424
|
result = await self.auth.refresh_token(
|
|
@@ -386,6 +426,11 @@ class HiveSession:
|
|
|
386
426
|
)
|
|
387
427
|
|
|
388
428
|
if result and "AuthenticationResult" in result:
|
|
429
|
+
auth_keys = list(result["AuthenticationResult"].keys())
|
|
430
|
+
_LOGGER.debug(
|
|
431
|
+
"Token refresh — AuthenticationResult keys: %s",
|
|
432
|
+
auth_keys,
|
|
433
|
+
)
|
|
389
434
|
await self.updateTokens(result)
|
|
390
435
|
new_expiry = (
|
|
391
436
|
self.tokens.tokenCreated + self.tokens.tokenExpiry
|
|
@@ -399,7 +444,13 @@ class HiveSession:
|
|
|
399
444
|
"Session Token refresh failed (%s), falling back to device login.",
|
|
400
445
|
type(exc).__name__,
|
|
401
446
|
)
|
|
402
|
-
|
|
447
|
+
if not force_refresh:
|
|
448
|
+
await self._retryDeviceLogin()
|
|
449
|
+
else:
|
|
450
|
+
_LOGGER.error(
|
|
451
|
+
"Token refresh failed during retry attempt, giving up."
|
|
452
|
+
)
|
|
453
|
+
raise HiveReauthRequired from exc
|
|
403
454
|
except HiveApiError:
|
|
404
455
|
_LOGGER.error("API error during token refresh.")
|
|
405
456
|
raise
|
|
@@ -535,12 +586,28 @@ class HiveSession:
|
|
|
535
586
|
"falling back to full device re-login."
|
|
536
587
|
)
|
|
537
588
|
await self._retryDeviceLogin()
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
589
|
+
last_auth_err = None
|
|
590
|
+
for api_retry_delay in (0, 2, 5):
|
|
591
|
+
try:
|
|
592
|
+
if api_retry_delay:
|
|
593
|
+
_LOGGER.debug(
|
|
594
|
+
"Retrying API call in %ss after device re-login.",
|
|
595
|
+
api_retry_delay,
|
|
596
|
+
)
|
|
597
|
+
await asyncio.sleep(api_retry_delay)
|
|
598
|
+
api_resp_d = await self.api.getAll()
|
|
599
|
+
last_auth_err = None
|
|
600
|
+
break
|
|
601
|
+
except HiveAuthError as retry_err:
|
|
602
|
+
_LOGGER.warning(
|
|
603
|
+
"API call still rejected after device re-login (attempt delay=%ss).",
|
|
604
|
+
api_retry_delay,
|
|
605
|
+
)
|
|
606
|
+
last_auth_err = retry_err
|
|
607
|
+
if last_auth_err is not None:
|
|
608
|
+
raise HiveReauthRequired from last_auth_err
|
|
542
609
|
api_call_duration = time.monotonic() - api_call_start
|
|
543
|
-
if api_call_duration >
|
|
610
|
+
if api_call_duration > self._slowPollThreshold:
|
|
544
611
|
_LOGGER.warning(
|
|
545
612
|
"Hive API response took %.1fs — marking poll as slow.",
|
|
546
613
|
api_call_duration,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/hive_exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/hivedataclasses.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|