pyhive-integration 1.0.8.dev0__tar.gz → 1.0.8.dev2__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.dev0/pyhive_integration.egg-info → pyhive_integration-1.0.8.dev2}/PKG-INFO +1 -1
  2. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2/pyhive_integration.egg-info}/PKG-INFO +1 -1
  3. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/setup.py +1 -1
  4. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/api/hive_async_api.py +27 -20
  5. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/helper/hive_exceptions.py +8 -0
  6. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/session.py +29 -6
  7. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/LICENSE +0 -0
  8. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/MANIFEST.in +0 -0
  9. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/README.md +0 -0
  10. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/pyhive_integration.egg-info/SOURCES.txt +0 -0
  11. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/pyhive_integration.egg-info/dependency_links.txt +0 -0
  12. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/pyhive_integration.egg-info/requires.txt +0 -0
  13. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/pyhive_integration.egg-info/top_level.txt +0 -0
  14. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/pyproject.toml +0 -0
  15. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/requirements.txt +0 -0
  16. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/requirements_test.txt +0 -0
  17. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/setup.cfg +0 -0
  18. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/__init__.py +0 -0
  19. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/action.py +0 -0
  20. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/alarm.py +0 -0
  21. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/api/__init__.py +0 -0
  22. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/api/hive_api.py +0 -0
  23. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/api/hive_auth.py +0 -0
  24. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/api/hive_auth_async.py +0 -0
  25. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/camera.py +0 -0
  26. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/device_attributes.py +0 -0
  27. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/heating.py +0 -0
  28. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/helper/__init__.py +0 -0
  29. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/helper/const.py +0 -0
  30. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/helper/debugger.py +0 -0
  31. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/helper/hive_helper.py +0 -0
  32. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/helper/hivedataclasses.py +0 -0
  33. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/helper/map.py +0 -0
  34. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/hive.py +0 -0
  35. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/hotwater.py +0 -0
  36. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/hub.py +0 -0
  37. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/light.py +0 -0
  38. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/plug.py +0 -0
  39. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/src/sensor.py +0 -0
  40. {pyhive_integration-1.0.8.dev0 → pyhive_integration-1.0.8.dev2}/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.dev0
3
+ Version: 1.0.8.dev2
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.dev0
3
+ Version: 1.0.8.dev2
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_dev",
20
+ version="1.0.8.dev2",
21
21
  packages=["apyhiveapi", "apyhiveapi.api", "apyhiveapi.helper"],
22
22
  package_dir={"apyhiveapi": "src"},
23
23
  package_data={"data": ["*.json"]},
@@ -10,8 +10,8 @@ import urllib3
10
10
  from aiohttp import ClientResponse, ClientSession, web_exceptions
11
11
  from pyquery import PyQuery
12
12
 
13
- from ..helper.const import HTTP_UNAUTHORIZED
14
- from ..helper.hive_exceptions import FileInUse, HiveApiError, NoApiToken
13
+ from ..helper.const import HTTP_FORBIDDEN, HTTP_UNAUTHORIZED
14
+ from ..helper.hive_exceptions import FileInUse, HiveApiError, HiveAuthError, NoApiToken
15
15
 
16
16
  _LOGGER = logging.getLogger(__name__)
17
17
 
@@ -56,45 +56,52 @@ class HiveApiAsync:
56
56
  _LOGGER.debug("API %s request to %s", method.upper(), url)
57
57
  data = kwargs.get("data", None)
58
58
 
59
+ headers = {
60
+ "content-type": "application/json",
61
+ "Accept": "*/*",
62
+ "User-Agent": "Hive/12.04.0 iOS/18.3.1 Apple",
63
+ }
59
64
  try:
60
65
  if camera:
61
- headers = {
62
- "content-type": "application/json",
63
- "Accept": "*/*",
64
- "Authorization": f"Bearer {self.session.tokens.tokenData['token']}",
65
- "x-jwt-token": self.session.tokens.tokenData["token"],
66
- "User-Agent": "Hive/12.04.0 iOS/18.3.1 Apple",
67
- }
66
+ headers["Authorization"] = (
67
+ f"Bearer {self.session.tokens.tokenData['token']}"
68
+ )
69
+ headers["x-jwt-token"] = self.session.tokens.tokenData["token"]
68
70
  else:
69
- headers = {
70
- "content-type": "application/json",
71
- "Accept": "*/*",
72
- "Authorization": self.session.tokens.tokenData["token"],
73
- "User-Agent": "Hive/12.04.0 iOS/18.3.1 Apple",
74
- }
71
+ headers["Authorization"] = self.session.tokens.tokenData["token"]
75
72
  except KeyError:
76
73
  if "sso" in url:
77
74
  pass
78
75
  else:
79
76
  raise NoApiToken
80
77
 
78
+ auth_token = headers.get("Authorization", "")
79
+ _LOGGER.debug(
80
+ "Using token (len=%d, tail=…%s)",
81
+ len(auth_token),
82
+ auth_token[-4:] if len(auth_token) >= 4 else auth_token,
83
+ )
84
+
81
85
  async with self.websession.request(
82
86
  method, url, headers=headers, data=data
83
87
  ) as resp:
84
- await resp.text()
88
+ resp_body = await resp.text()
85
89
  if str(resp.status).startswith("20"):
86
90
  _LOGGER.debug("API response %s from %s", resp.status, url)
87
91
  return resp
88
92
 
89
- if resp.status == HTTP_UNAUTHORIZED:
93
+ if resp.status in (HTTP_UNAUTHORIZED, HTTP_FORBIDDEN):
90
94
  _LOGGER.error(
91
- f"Hive token has expired when calling {url} - "
92
- f"HTTP status is - {resp.status}"
95
+ f"Hive token rejected calling {url} - "
96
+ f"HTTP {resp.status} — response: {resp_body[:200]}"
97
+ )
98
+ raise HiveAuthError(
99
+ f"Token expired or forbidden calling {url} — HTTP {resp.status}"
93
100
  )
94
101
  elif url is not None and resp.status is not None:
95
102
  _LOGGER.error(
96
103
  f"Something has gone wrong calling {url} - "
97
- f"HTTP status is - {resp.status}"
104
+ f"HTTP status is - {resp.status} — response: {resp_body[:200]}"
98
105
  )
99
106
 
100
107
  raise HiveApiError
@@ -27,6 +27,14 @@ class HiveApiError(Exception):
27
27
  """
28
28
 
29
29
 
30
+ class HiveAuthError(HiveApiError):
31
+ """Auth error (401/403) — token may be expired or invalid.
32
+
33
+ Args:
34
+ HiveApiError (object): Parent API error class
35
+ """
36
+
37
+
30
38
  class HiveRefreshTokenExpired(Exception):
31
39
  """Refresh token expired.
32
40
 
@@ -17,6 +17,7 @@ from .device_attributes import HiveAttributes
17
17
  from .helper.const import ACTIONS, DEVICES, HIVE_TYPES, PRODUCTS
18
18
  from .helper.hive_exceptions import (
19
19
  HiveApiError,
20
+ HiveAuthError,
20
21
  HiveFailedToRefreshTokens,
21
22
  HiveInvalid2FACode,
22
23
  HiveInvalidDeviceAuthentication,
@@ -73,7 +74,7 @@ class HiveSession:
73
74
  self.tokens = Map(
74
75
  {
75
76
  "tokenData": {},
76
- "tokenCreated": datetime.now() - timedelta(seconds=4000),
77
+ "tokenCreated": datetime.min,
77
78
  "tokenExpiry": timedelta(seconds=3600),
78
79
  }
79
80
  )
@@ -407,12 +408,16 @@ class HiveSession:
407
408
  if datetime.now() < ep:
408
409
  return updated
409
410
  _LOGGER.debug("Polling Hive API for device updates.")
410
- await self.getDevices(device["hiveID"])
411
- if len(self.deviceList["camera"]) > 0:
411
+ updated = await self.getDevices(device["hiveID"])
412
+ if updated and len(self.deviceList["camera"]) > 0:
412
413
  for camera in self.data.camera:
413
414
  await self.getCamera(self.devices[camera])
414
- updated = True
415
- _LOGGER.debug("Device update completed successfully.")
415
+ if updated:
416
+ _LOGGER.debug("Device update completed successfully.")
417
+ else:
418
+ _LOGGER.debug(
419
+ "Device update failed, will retry after scan interval."
420
+ )
416
421
 
417
422
  return updated
418
423
 
@@ -505,7 +510,18 @@ class HiveSession:
505
510
  elif self.tokens is not None:
506
511
  await self.hiveRefreshTokens()
507
512
  _LOGGER.debug("Fetching all devices from Hive API.")
508
- api_resp_d = await self.api.getAll()
513
+ try:
514
+ api_resp_d = await self.api.getAll()
515
+ except HiveAuthError:
516
+ _LOGGER.warning(
517
+ "Auth error (401/403) after token refresh, "
518
+ "falling back to full device re-login."
519
+ )
520
+ await self._retryDeviceLogin()
521
+ try:
522
+ api_resp_d = await self.api.getAll()
523
+ except HiveAuthError as retry_err:
524
+ raise HiveReauthRequired from retry_err
509
525
  if operator.contains(str(api_resp_d["original"]), "20") is False:
510
526
  raise HTTPException
511
527
  elif api_resp_d["parsed"] is None:
@@ -551,6 +567,10 @@ class HiveSession:
551
567
  await self.getAlarm()
552
568
  self.config.lastUpdate = datetime.now()
553
569
  get_nodes_successful = True
570
+ except HiveReauthRequired:
571
+ _LOGGER.error("Reauthentication required, propagating to caller.")
572
+ self.config.lastUpdate = datetime.now()
573
+ raise
554
574
  except (
555
575
  OSError,
556
576
  RuntimeError,
@@ -559,6 +579,9 @@ class HiveSession:
559
579
  HTTPException,
560
580
  ) as err:
561
581
  _LOGGER.error("Failed to fetch devices: %s", err)
582
+ self.config.lastUpdate = (
583
+ datetime.now() - self.config.scanInterval + timedelta(seconds=30)
584
+ )
562
585
  get_nodes_successful = False
563
586
 
564
587
  return get_nodes_successful