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.
Files changed (40) hide show
  1. {pyhive_integration-1.0.8.dev7/pyhive_integration.egg-info → pyhive_integration-1.0.8.dev9}/PKG-INFO +1 -1
  2. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9/pyhive_integration.egg-info}/PKG-INFO +1 -1
  3. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/setup.py +1 -1
  4. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/session.py +84 -17
  5. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/LICENSE +0 -0
  6. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/MANIFEST.in +0 -0
  7. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/README.md +0 -0
  8. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/pyhive_integration.egg-info/SOURCES.txt +0 -0
  9. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/pyhive_integration.egg-info/dependency_links.txt +0 -0
  10. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/pyhive_integration.egg-info/requires.txt +0 -0
  11. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/pyhive_integration.egg-info/top_level.txt +0 -0
  12. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/pyproject.toml +0 -0
  13. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/requirements.txt +0 -0
  14. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/requirements_test.txt +0 -0
  15. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/setup.cfg +0 -0
  16. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/__init__.py +0 -0
  17. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/action.py +0 -0
  18. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/alarm.py +0 -0
  19. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/api/__init__.py +0 -0
  20. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/api/hive_api.py +0 -0
  21. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/api/hive_async_api.py +0 -0
  22. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/api/hive_auth.py +0 -0
  23. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/api/hive_auth_async.py +0 -0
  24. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/camera.py +0 -0
  25. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/device_attributes.py +0 -0
  26. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/heating.py +0 -0
  27. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/__init__.py +0 -0
  28. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/const.py +0 -0
  29. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/debugger.py +0 -0
  30. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/hive_exceptions.py +0 -0
  31. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/hive_helper.py +0 -0
  32. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/hivedataclasses.py +0 -0
  33. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/helper/map.py +0 -0
  34. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/hive.py +0 -0
  35. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/hotwater.py +0 -0
  36. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/hub.py +0 -0
  37. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/light.py +0 -0
  38. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/plug.py +0 -0
  39. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/src/sensor.py +0 -0
  40. {pyhive_integration-1.0.8.dev7 → pyhive_integration-1.0.8.dev9}/tests/test_hub.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyhive-integration
3
- Version: 1.0.8.dev7
3
+ Version: 1.0.8.dev9
4
4
  Summary: A Python library to interface with the Hive API
5
5
  Home-page: https://github.com/Pyhive/pyhiveapi
6
6
  Author: KJonline24
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyhive-integration
3
- Version: 1.0.8.dev7
3
+ Version: 1.0.8.dev9
4
4
  Summary: A Python library to interface with the Hive API
5
5
  Home-page: https://github.com/Pyhive/pyhiveapi
6
6
  Author: KJonline24
@@ -17,7 +17,7 @@ def requirements_from_file(filename="requirements.txt"):
17
17
 
18
18
 
19
19
  setup(
20
- version="1.0.8.dev7",
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
- _LOGGER.debug("Login successful, tokens received.")
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
- _LOGGER.debug("2FA login successful, tokens received.")
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
- _LOGGER.debug("Device login successful, tokens received.")
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
- async def hiveRefreshTokens(self):
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 + (self.tokens.tokenExpiry * 0.95)
360
- # Refresh at 95% of token lifetime to prevent expiration during API calls
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 * 0.95
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 (×0.95): %s | Now: %s",
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
- await self._retryDeviceLogin()
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
- try:
539
- api_resp_d = await self.api.getAll()
540
- except HiveAuthError as retry_err:
541
- raise HiveReauthRequired from retry_err
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 > 3:
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,