python-hilo 2025.10.2__tar.gz → 2025.12.1__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 (21) hide show
  1. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/PKG-INFO +1 -1
  2. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/api.py +6 -4
  3. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/const.py +2 -1
  4. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/devices.py +2 -2
  5. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/event.py +7 -1
  6. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/graphql.py +11 -4
  7. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/websocket.py +4 -0
  8. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyproject.toml +1 -1
  9. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/LICENSE +0 -0
  10. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/README.md +0 -0
  11. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/__init__.py +0 -0
  12. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/device/__init__.py +0 -0
  13. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/device/climate.py +0 -0
  14. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/device/graphql_value_mapper.py +0 -0
  15. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/device/light.py +0 -0
  16. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/device/sensor.py +0 -0
  17. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/device/switch.py +0 -0
  18. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/exceptions.py +0 -0
  19. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/oauth2helper.py +0 -0
  20. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/util/__init__.py +0 -0
  21. {python_hilo-2025.10.2 → python_hilo-2025.12.1}/pyhilo/util/state.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-hilo
3
- Version: 2025.10.2
3
+ Version: 2025.12.1
4
4
  Summary: A Python3, async interface to the Hilo API
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -12,6 +12,7 @@ from urllib import parse
12
12
  from aiohttp import ClientSession
13
13
  from aiohttp.client_exceptions import ClientResponseError
14
14
  import backoff
15
+ from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
15
16
 
16
17
  from pyhilo.const import (
17
18
  ANDROID_CLIENT_ENDPOINT,
@@ -66,7 +67,7 @@ class API:
66
67
  self,
67
68
  *,
68
69
  session: ClientSession,
69
- oauth_session,
70
+ oauth_session: OAuth2Session,
70
71
  request_retries: int = REQUEST_RETRY,
71
72
  log_traces: bool = False,
72
73
  ) -> None:
@@ -97,7 +98,7 @@ class API:
97
98
  cls,
98
99
  *,
99
100
  session: ClientSession,
100
- oauth_session,
101
+ oauth_session: OAuth2Session,
101
102
  request_retries: int = REQUEST_RETRY,
102
103
  log_traces: bool = False,
103
104
  ) -> API:
@@ -382,11 +383,12 @@ class API:
382
383
  # Create both websocket clients
383
384
  # ic-dev21 need to work on this as it can't lint as is, may need to
384
385
  # instantiate differently
385
- self.websocket_devices = WebsocketClient(self.websocket_manager.devicehub)
386
+ # TODO: fix type ignore after refactor
387
+ self.websocket_devices = WebsocketClient(self.websocket_manager.devicehub) # type: ignore
386
388
 
387
389
  # For backward compatibility during the transition to challengehub websocket
388
390
  self.websocket = self.websocket_devices
389
- self.websocket_challenges = WebsocketClient(self.websocket_manager.challengehub)
391
+ self.websocket_challenges = WebsocketClient(self.websocket_manager.challengehub) # type: ignore
390
392
 
391
393
  async def refresh_ws_token(self) -> None:
392
394
  """Refresh the websocket token."""
@@ -7,7 +7,7 @@ import aiohttp
7
7
  LOG: Final = logging.getLogger(__package__)
8
8
  DEFAULT_STATE_FILE: Final = "hilo_state.yaml"
9
9
  REQUEST_RETRY: Final = 9
10
- PYHILO_VERSION: Final = "2025.10.02"
10
+ PYHILO_VERSION: Final = "2025.12.01"
11
11
  # TODO: Find a way to keep previous line in sync with pyproject.toml automatically
12
12
 
13
13
  CONTENT_TYPE_FORM: Final = "application/x-www-form-urlencoded"
@@ -32,6 +32,7 @@ API_GD_SERVICE_ENDPOINT: Final = f"/GDService/{API_END}"
32
32
  API_NOTIFICATIONS_ENDPOINT: Final = "/Notifications"
33
33
  API_EVENTS_ENDPOINT: Final = "/Notifications"
34
34
  API_REGISTRATION_ENDPOINT: Final = f"{API_NOTIFICATIONS_ENDPOINT}/Registrations"
35
+ PLATFORM_HOST: Final = "platform.hiloenergie.com"
35
36
 
36
37
  API_REGISTRATION_HEADERS: Final = {
37
38
  "AppId": ANDROID_PKG_NAME,
@@ -55,7 +55,7 @@ class Devices:
55
55
  """Uses the dict from parse_values_received to map the values to devices."""
56
56
  updated_devices = []
57
57
  for reading in readings:
58
- device_identifier = reading.device_id
58
+ device_identifier: Union[int, str] = reading.device_id
59
59
  if device_identifier == 0:
60
60
  device_identifier = reading.hilo_id
61
61
  if device := self.find_device(device_identifier):
@@ -69,7 +69,7 @@ class Devices:
69
69
  )
70
70
  return updated_devices
71
71
 
72
- def find_device(self, device_identifier: int | str) -> HiloDevice:
72
+ def find_device(self, device_identifier: int | str) -> HiloDevice | None:
73
73
  """Makes sure the devices received have an identifier, this means some need to be hardcoded
74
74
  like the unknown power meter.
75
75
  """
@@ -27,7 +27,7 @@ class Event:
27
27
  def __init__(self, **event: dict[str, Any]):
28
28
  """Initialize."""
29
29
  self._convert_phases(cast(dict[str, Any], event.get("phases")))
30
- params: dict[str, Any] = event.get("parameters", {})
30
+ params: dict[str, Any] = event.get("parameters") or {}
31
31
  devices: list[dict[str, Any]] = params.get("devices", [])
32
32
  consumption: dict[str, Any] = event.get("consumption", {})
33
33
  allowed_wH: int = consumption.get("baselineWh", 0) or 0
@@ -71,6 +71,12 @@ class Event:
71
71
  self.used_kWh = round(used_wH / 1000, 2)
72
72
  self.last_update = datetime.now(timezone.utc).astimezone()
73
73
 
74
+ def update_allowed_wh(self, allowed_wH: float) -> None:
75
+ """This function is used to update the allowed_kWh attribute during a Hilo Challenge Event"""
76
+ LOG.debug("Updating allowed Wh: %s", allowed_wH)
77
+ self.allowed_kWh = round(allowed_wH / 1000, 2)
78
+ self.last_update = datetime.now(timezone.utc).astimezone()
79
+
74
80
  def should_check_for_allowed_wh(self) -> bool:
75
81
  """This function is used to authorize subscribing to a specific event in Hilo to receive the allowed_kWh
76
82
  that is made available in the pre_heat phase"""
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
  import logging
3
+ import ssl
3
4
  from typing import Any, Dict, List, Optional
4
5
 
5
6
  from gql import Client, gql
@@ -7,7 +8,7 @@ from gql.transport.aiohttp import AIOHTTPTransport
7
8
  from gql.transport.websockets import WebsocketsTransport
8
9
 
9
10
  from pyhilo import API
10
- from pyhilo.const import LOG
11
+ from pyhilo.const import LOG, PLATFORM_HOST
11
12
  from pyhilo.device.graphql_value_mapper import GraphqlValueMapper
12
13
  from pyhilo.devices import Devices
13
14
 
@@ -533,7 +534,7 @@ class GraphQlHelper:
533
534
  """This functions calls the digital-twin and requests location id"""
534
535
  access_token = await self._get_access_token()
535
536
  transport = AIOHTTPTransport(
536
- url="https://platform.hiloenergie.com/api/digital-twin/v3/graphql",
537
+ url=f"https://{PLATFORM_HOST}/api/digital-twin/v3/graphql",
537
538
  headers={"Authorization": f"Bearer {access_token}"},
538
539
  )
539
540
  client = Client(transport=transport, fetch_schema_from_transport=True)
@@ -552,11 +553,17 @@ class GraphQlHelper:
552
553
 
553
554
  # Setting log level to suppress keepalive messages on gql transport
554
555
  logging.getLogger("gql.transport.websockets").setLevel(logging.WARNING)
556
+
557
+ #
558
+ loop = asyncio.get_event_loop()
559
+ ssl_context = await loop.run_in_executor(None, ssl.create_default_context)
560
+
555
561
  while True: # Loop to reconnect if the connection is lost
556
562
  LOG.debug("subscribe_to_device_updated while true")
557
563
  access_token = await self._get_access_token()
558
564
  transport = WebsocketsTransport(
559
- url=f"wss://platform.hiloenergie.com/api/digital-twin/v3/graphql?access_token={access_token}"
565
+ url=f"wss://{PLATFORM_HOST}/api/digital-twin/v3/graphql?access_token={access_token}",
566
+ ssl=ssl_context,
560
567
  )
561
568
  client = Client(transport=transport, fetch_schema_from_transport=True)
562
569
  query = gql(self.SUBSCRIPTION_DEVICE_UPDATED)
@@ -595,7 +602,7 @@ class GraphQlHelper:
595
602
  ) -> None:
596
603
  access_token = await self._get_access_token()
597
604
  transport = WebsocketsTransport(
598
- url=f"wss://platform.hiloenergie.com/api/digital-twin/v3/graphql?access_token={access_token}"
605
+ url=f"wss://{PLATFORM_HOST}/api/digital-twin/v3/graphql?access_token={access_token}"
599
606
  )
600
607
  client = Client(transport=transport, fetch_schema_from_transport=True)
601
608
  query = gql(self.SUBSCRIPTION_LOCATION_UPDATED)
@@ -310,6 +310,10 @@ class WebsocketClient:
310
310
  raise CannotConnectError(err) from err
311
311
 
312
312
  LOG.info(f"Connected to websocket server {self._api.endpoint}")
313
+
314
+ # Quick pause to prevent race condition
315
+ await asyncio.sleep(0.05)
316
+
313
317
  self._watchdog.trigger()
314
318
  for callback in self._connect_callbacks:
315
319
  schedule_callback(callback)
@@ -40,7 +40,7 @@ exclude = ".venv/.*"
40
40
 
41
41
  [tool.poetry]
42
42
  name = "python-hilo"
43
- version = "2025.10.2"
43
+ version = "2025.12.1"
44
44
  description = "A Python3, async interface to the Hilo API"
45
45
  readme = "README.md"
46
46
  authors = ["David Vallee Delisle <me@dvd.dev>"]
File without changes