pyhive-integration 1.0.8.dev6__tar.gz → 1.0.8.dev8__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.dev6/pyhive_integration.egg-info → pyhive_integration-1.0.8.dev8}/PKG-INFO +1 -1
  2. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8/pyhive_integration.egg-info}/PKG-INFO +1 -1
  3. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/setup.py +1 -1
  4. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/alarm.py +1 -0
  5. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/api/hive_api.py +1 -1
  6. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/api/hive_async_api.py +1 -1
  7. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/camera.py +1 -0
  8. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/heating.py +29 -9
  9. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/hotwater.py +1 -0
  10. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/light.py +1 -0
  11. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/plug.py +1 -0
  12. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/sensor.py +1 -0
  13. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/session.py +63 -10
  14. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/LICENSE +0 -0
  15. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/MANIFEST.in +0 -0
  16. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/README.md +0 -0
  17. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/pyhive_integration.egg-info/SOURCES.txt +0 -0
  18. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/pyhive_integration.egg-info/dependency_links.txt +0 -0
  19. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/pyhive_integration.egg-info/requires.txt +0 -0
  20. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/pyhive_integration.egg-info/top_level.txt +0 -0
  21. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/pyproject.toml +0 -0
  22. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/requirements.txt +0 -0
  23. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/requirements_test.txt +0 -0
  24. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/setup.cfg +0 -0
  25. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/__init__.py +0 -0
  26. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/action.py +0 -0
  27. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/api/__init__.py +0 -0
  28. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/api/hive_auth.py +0 -0
  29. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/api/hive_auth_async.py +0 -0
  30. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/device_attributes.py +0 -0
  31. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/helper/__init__.py +0 -0
  32. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/helper/const.py +0 -0
  33. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/helper/debugger.py +0 -0
  34. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/helper/hive_exceptions.py +0 -0
  35. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/helper/hive_helper.py +0 -0
  36. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/helper/hivedataclasses.py +0 -0
  37. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/helper/map.py +0 -0
  38. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/hive.py +0 -0
  39. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/src/hub.py +0 -0
  40. {pyhive_integration-1.0.8.dev6 → pyhive_integration-1.0.8.dev8}/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.dev6
3
+ Version: 1.0.8.dev8
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.dev6
3
+ Version: 1.0.8.dev8
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.dev6",
20
+ version="1.0.8.dev8",
21
21
  packages=["apyhiveapi", "apyhiveapi.api", "apyhiveapi.helper"],
22
22
  package_dir={"apyhiveapi": "src"},
23
23
  package_data={"data": ["*.json"]},
@@ -131,4 +131,5 @@ class Alarm(HiveHomeShield):
131
131
  await self.session.helper.errorCheck(
132
132
  device["device_id"], "ERROR", device["deviceData"]["online"]
133
133
  )
134
+ device.setdefault("status", {"state": None, "mode": None})
134
135
  return device
@@ -33,7 +33,7 @@ class HiveApi:
33
33
  "actions": "/actions",
34
34
  "nodes": "/nodes/{0}/{1}",
35
35
  }
36
- self.timeout = 10
36
+ self.timeout = 5
37
37
  self.json_return = {
38
38
  "original": "No response to Hive API request",
39
39
  "parsed": "No response to Hive API request",
@@ -84,7 +84,7 @@ class HiveApiAsync:
84
84
  auth_token[-4:] if len(auth_token) >= 4 else auth_token,
85
85
  )
86
86
 
87
- timeout = ClientTimeout(total=10)
87
+ timeout = ClientTimeout(total=self.timeout)
88
88
  req_start = time.monotonic()
89
89
  async with self.websession.request(
90
90
  method, url, headers=headers, data=data, timeout=timeout
@@ -189,4 +189,5 @@ class Camera(HiveCamera):
189
189
  await self.session.helper.errorCheck(
190
190
  device["device_id"], "ERROR", device["deviceData"]["online"]
191
191
  )
192
+ device.setdefault("status", {"state": None})
192
193
  return device
@@ -54,7 +54,6 @@ class HiveHeating:
54
54
  """
55
55
  from datetime import datetime
56
56
 
57
- f_state = None
58
57
  state = None
59
58
  final = None
60
59
 
@@ -62,6 +61,16 @@ class HiveHeating:
62
61
  data = self.session.data.products[device["hiveID"]]
63
62
  state = data["props"]["temperature"]
64
63
 
64
+ try:
65
+ state = float(state)
66
+ except (ValueError, TypeError):
67
+ _LOGGER.debug(
68
+ "Non-numeric temperature value '%s' for %s.",
69
+ state,
70
+ device.get("haName", device.get("hiveID")),
71
+ )
72
+ return None
73
+
65
74
  if device["hiveID"] in self.session.data.minMax:
66
75
  if self.session.data.minMax[device["hiveID"]]["TodayDate"] == str(
67
76
  datetime.date(datetime.now())
@@ -94,8 +103,7 @@ class HiveHeating:
94
103
  }
95
104
  self.session.data.minMax[device["hiveID"]] = data
96
105
 
97
- f_state = round(float(state), 1)
98
- final = f_state
106
+ final = round(state, 1)
99
107
  except KeyError as e:
100
108
  _LOGGER.error(e)
101
109
 
@@ -159,12 +167,13 @@ class HiveHeating:
159
167
  try:
160
168
  current_temp = await self.getCurrentTemperature(device)
161
169
  target_temp = await self.getTargetTemperature(device)
162
- if current_temp < target_temp:
163
- state = "ON"
164
- else:
165
- state = "OFF"
166
- final = HIVETOHA[self.heatingType].get(state, state)
167
- except KeyError as e:
170
+ if current_temp is not None and target_temp is not None:
171
+ if current_temp < target_temp:
172
+ state = "ON"
173
+ else:
174
+ state = "OFF"
175
+ final = HIVETOHA[self.heatingType].get(state, state)
176
+ except (KeyError, TypeError) as e:
168
177
  _LOGGER.error(e)
169
178
 
170
179
  return final
@@ -503,6 +512,17 @@ class Climate(HiveHeating):
503
512
  device["haName"],
504
513
  )
505
514
  return cached
515
+ device.setdefault(
516
+ "status",
517
+ {
518
+ "current_temperature": None,
519
+ "target_temperature": None,
520
+ "action": None,
521
+ "mode": None,
522
+ "boost": None,
523
+ "state": None,
524
+ },
525
+ )
506
526
  return device
507
527
 
508
528
  async def getScheduleNowNextLater(self, device: dict):
@@ -271,6 +271,7 @@ class WaterHeater(HiveHotwater):
271
271
  device["haName"],
272
272
  )
273
273
  return cached
274
+ device.setdefault("status", {"current_operation": None})
274
275
  return device
275
276
 
276
277
  async def getScheduleNowNextLater(self, device: dict):
@@ -428,6 +428,7 @@ class Light(HiveLight):
428
428
  device["haName"],
429
429
  )
430
430
  return cached
431
+ device.setdefault("status", {"state": None})
431
432
  return device
432
433
 
433
434
  async def turnOn(self, device: dict, brightness: int, color_temp: int, color: list):
@@ -188,6 +188,7 @@ class Switch(HiveSmartPlug):
188
188
  device["haName"],
189
189
  )
190
190
  return cached
191
+ device.setdefault("status", {"state": None})
191
192
  return device
192
193
 
193
194
  async def getSwitchState(self, device: dict):
@@ -160,4 +160,5 @@ class Sensor(HiveSensor):
160
160
  device["haName"],
161
161
  )
162
162
  return cached
163
+ device.setdefault("status", {"state": None})
163
164
  return device
@@ -108,6 +108,7 @@ class HiveSession:
108
108
  self.deviceList = {}
109
109
  self.hub_id = None
110
110
  self._lastPollSlow = False
111
+ self._slowPollThreshold = timedelta(seconds=3)
111
112
 
112
113
  def openFile(self, file: str):
113
114
  """Open a file.
@@ -226,6 +227,29 @@ class HiveSession:
226
227
  if "ExpiresIn" in data:
227
228
  self.tokens.tokenExpiry = timedelta(seconds=data["ExpiresIn"])
228
229
 
230
+ _LOGGER.debug(
231
+ "updateTokens — IdToken: len=%d tail=…%s | "
232
+ "AccessToken: len=%d tail=…%s | "
233
+ "RefreshToken: %s | "
234
+ "ExpiresIn: %s | tokenCreated: %s | tokenExpiry: %s",
235
+ len(self.tokens.tokenData.get("token", "")),
236
+ self.tokens.tokenData.get("token", "")[-4:],
237
+ len(self.tokens.tokenData.get("accessToken", "")),
238
+ self.tokens.tokenData.get("accessToken", "")[-4:],
239
+ (
240
+ "present (len=%d tail=…%s)"
241
+ % (
242
+ len(self.tokens.tokenData.get("refreshToken", "")),
243
+ self.tokens.tokenData.get("refreshToken", "")[-4:],
244
+ )
245
+ if self.tokens.tokenData.get("refreshToken")
246
+ else "not present"
247
+ ),
248
+ data.get("ExpiresIn", "N/A"),
249
+ self.tokens.tokenCreated,
250
+ self.tokens.tokenExpiry,
251
+ )
252
+
229
253
  return self.tokens
230
254
 
231
255
  async def login(self):
@@ -255,7 +279,8 @@ class HiveSession:
255
279
  raise
256
280
 
257
281
  if result and "AuthenticationResult" in result:
258
- _LOGGER.debug("Login successful, tokens received.")
282
+ auth_keys = list(result["AuthenticationResult"].keys())
283
+ _LOGGER.debug("Login successful — AuthenticationResult keys: %s", auth_keys)
259
284
  await self.updateTokens(result)
260
285
  return result
261
286
 
@@ -284,7 +309,10 @@ class HiveSession:
284
309
  raise
285
310
 
286
311
  if result and "AuthenticationResult" in result:
287
- _LOGGER.debug("2FA login successful, tokens received.")
312
+ auth_keys = list(result["AuthenticationResult"].keys())
313
+ _LOGGER.debug(
314
+ "2FA login successful — AuthenticationResult keys: %s", auth_keys
315
+ )
288
316
  await self.updateTokens(result)
289
317
  return result
290
318
 
@@ -311,9 +339,11 @@ class HiveSession:
311
339
  raise
312
340
 
313
341
  if result and "AuthenticationResult" in result:
314
- _LOGGER.debug("Device login successful, tokens received.")
342
+ auth_keys = list(result["AuthenticationResult"].keys())
343
+ _LOGGER.debug(
344
+ "Device login successful — AuthenticationResult keys: %s", auth_keys
345
+ )
315
346
  await self.updateTokens(result)
316
- self.tokens.tokenExpiry = timedelta(seconds=0)
317
347
  return result
318
348
 
319
349
  async def _retryDeviceLogin(self):
@@ -386,6 +416,11 @@ class HiveSession:
386
416
  )
387
417
 
388
418
  if result and "AuthenticationResult" in result:
419
+ auth_keys = list(result["AuthenticationResult"].keys())
420
+ _LOGGER.debug(
421
+ "Token refresh — AuthenticationResult keys: %s",
422
+ auth_keys,
423
+ )
389
424
  await self.updateTokens(result)
390
425
  new_expiry = (
391
426
  self.tokens.tokenCreated + self.tokens.tokenExpiry
@@ -535,12 +570,28 @@ class HiveSession:
535
570
  "falling back to full device re-login."
536
571
  )
537
572
  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
573
+ last_auth_err = None
574
+ for api_retry_delay in (0, 2, 5):
575
+ try:
576
+ if api_retry_delay:
577
+ _LOGGER.debug(
578
+ "Retrying API call in %ss after device re-login.",
579
+ api_retry_delay,
580
+ )
581
+ await asyncio.sleep(api_retry_delay)
582
+ api_resp_d = await self.api.getAll()
583
+ last_auth_err = None
584
+ break
585
+ except HiveAuthError as retry_err:
586
+ _LOGGER.warning(
587
+ "API call still rejected after device re-login (attempt delay=%ss).",
588
+ api_retry_delay,
589
+ )
590
+ last_auth_err = retry_err
591
+ if last_auth_err is not None:
592
+ raise HiveReauthRequired from last_auth_err
542
593
  api_call_duration = time.monotonic() - api_call_start
543
- if api_call_duration > 3:
594
+ if api_call_duration > self._slowPollThreshold:
544
595
  _LOGGER.warning(
545
596
  "Hive API response took %.1fs — marking poll as slow.",
546
597
  api_call_duration,
@@ -595,7 +646,9 @@ class HiveSession:
595
646
  get_nodes_successful = True
596
647
  except HiveReauthRequired:
597
648
  _LOGGER.error("Reauthentication required, propagating to caller.")
598
- self.config.lastUpdate = datetime.now()
649
+ self.config.lastUpdate = (
650
+ datetime.now() - self.config.scanInterval + timedelta(seconds=30)
651
+ )
599
652
  raise
600
653
  except asyncio.TimeoutError:
601
654
  _LOGGER.warning("Hive API request timed out — keeping cached device data.")