python-hilo 2025.6.1__tar.gz → 2025.9.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 (22) hide show
  1. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/PKG-INFO +5 -3
  2. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/__init__.py +0 -2
  3. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/api.py +17 -16
  4. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/const.py +1 -1
  5. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/device/light.py +1 -1
  6. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/device/sensor.py +1 -1
  7. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/device/switch.py +1 -1
  8. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/devices.py +6 -6
  9. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/event.py +1 -1
  10. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/graphql.py +12 -7
  11. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/websocket.py +21 -13
  12. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyproject.toml +2 -2
  13. python_hilo-2025.6.1/pyhilo/oauth2.py +0 -51
  14. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/LICENSE +0 -0
  15. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/README.md +0 -0
  16. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/device/__init__.py +0 -0
  17. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/device/climate.py +0 -0
  18. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/device/graphql_value_mapper.py +0 -0
  19. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/exceptions.py +0 -0
  20. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/oauth2helper.py +0 -0
  21. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/util/__init__.py +0 -0
  22. {python_hilo-2025.6.1 → python_hilo-2025.9.1}/pyhilo/util/state.py +0 -0
@@ -1,8 +1,9 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: python-hilo
3
- Version: 2025.6.1
3
+ Version: 2025.9.1
4
4
  Summary: A Python3, async interface to the Hilo API
5
5
  License: MIT
6
+ License-File: LICENSE
6
7
  Author: David Vallee Delisle
7
8
  Author-email: me@dvd.dev
8
9
  Maintainer: David Vallee Delisle
@@ -17,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.10
17
18
  Classifier: Programming Language :: Python :: 3.11
18
19
  Classifier: Programming Language :: Python :: 3.12
19
20
  Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
20
22
  Classifier: Programming Language :: Python :: Implementation :: CPython
21
23
  Classifier: Programming Language :: Python :: Implementation :: PyPy
22
24
  Classifier: Topic :: Home Automation
@@ -26,7 +28,7 @@ Requires-Dist: aiosignal (>=1.2.0)
26
28
  Requires-Dist: async-timeout (>=4.0.0)
27
29
  Requires-Dist: attrs (>=21.2.0)
28
30
  Requires-Dist: backoff (>=1.11.1)
29
- Requires-Dist: gql (>=3.5.2,<4.0.0)
31
+ Requires-Dist: gql (>=3.5.2,<5.0.0)
30
32
  Requires-Dist: python-dateutil (>=2.8.2)
31
33
  Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
32
34
  Requires-Dist: voluptuous (>=0.13.1)
@@ -6,7 +6,6 @@ from pyhilo.device.switch import Switch
6
6
  from pyhilo.devices import Devices
7
7
  from pyhilo.event import Event
8
8
  from pyhilo.exceptions import HiloError, InvalidCredentialsError, WebsocketError
9
- from pyhilo.oauth2 import AuthCodeWithPKCEImplementation
10
9
  from pyhilo.util import from_utc_timestamp, time_diff
11
10
  from pyhilo.websocket import WebsocketEvent
12
11
 
@@ -18,7 +17,6 @@ __all__ = [
18
17
  "HiloError",
19
18
  "InvalidCredentialsError",
20
19
  "WebsocketError",
21
- "AuthCodeWithPKCEImplementation",
22
20
  "from_utc_timestamp",
23
21
  "time_diff",
24
22
  "WebsocketEvent",
@@ -140,7 +140,7 @@ class API:
140
140
  await self._oauth_session.async_ensure_token_valid()
141
141
 
142
142
  access_token = str(self._oauth_session.token["access_token"])
143
- LOG.debug(f"Websocket access token is {access_token}")
143
+ LOG.debug("Websocket access token is %s", access_token)
144
144
 
145
145
  return str(self._oauth_session.token["access_token"])
146
146
 
@@ -246,8 +246,9 @@ class API:
246
246
  data: dict[str, Any] = {}
247
247
  url = parse.urljoin(f"https://{host}", endpoint)
248
248
  if self.log_traces:
249
- LOG.debug(f"[TRACE] Headers: {kwargs['headers']}")
250
- LOG.debug(f"[TRACE] Async request: {method} {url}")
249
+ LOG.debug("[TRACE] Headers: %s", kwargs["headers"])
250
+ LOG.debug("[TRACE] Async request: %s %s", method, url)
251
+
251
252
  async with self.session.request(method, url, **kwargs) as resp:
252
253
  if "application/json" in resp.headers.get("content-type", ""):
253
254
  try:
@@ -396,7 +397,7 @@ class API:
396
397
  """Retrieves and constructs WebSocket connection parameters from the negotiation endpoint."""
397
398
  uri = parse.urlparse(self.ws_url)
398
399
  LOG.debug("Getting websocket params")
399
- LOG.debug(f"Getting uri {uri}")
400
+ LOG.debug("Getting uri %s", uri)
400
401
  resp: dict[str, Any] = await self.async_request(
401
402
  "post",
402
403
  f"{uri.path}negotiate?{uri.query}",
@@ -407,7 +408,7 @@ class API:
407
408
  )
408
409
  conn_id: str = resp.get("connectionId", "")
409
410
  self.full_ws_url = f"{self.ws_url}&id={conn_id}&access_token={self.ws_token}"
410
- LOG.debug(f"Getting full ws URL {self.full_ws_url}")
411
+ LOG.debug("Getting full ws URL %s", self.full_ws_url)
411
412
  transport_dict: list[WebsocketTransportsDict] = resp.get(
412
413
  "availableTransports", []
413
414
  )
@@ -441,7 +442,7 @@ class API:
441
442
  if err.status in (401, 403):
442
443
  raise InvalidCredentialsError("Invalid credentials") from err
443
444
  raise RequestError(err) from err
444
- LOG.debug(f"FB Install data: {resp}")
445
+ LOG.debug("FB Install data: %s", resp)
445
446
  auth_token = resp.get("authToken", {})
446
447
  LOG.debug("Calling set_state from fb_install")
447
448
  await set_state(
@@ -479,7 +480,7 @@ class API:
479
480
  if err.status in (401, 403):
480
481
  raise InvalidCredentialsError("Invalid credentials") from err
481
482
  raise RequestError(err) from err
482
- LOG.debug(f"Android client register: {resp}")
483
+ LOG.debug("Android client register: %s", resp)
483
484
  msg: str = resp.get("message", "")
484
485
  if msg.startswith("Error="):
485
486
  LOG.error(f"Android registration error: {msg}")
@@ -497,14 +498,14 @@ class API:
497
498
  async def get_location_ids(self) -> tuple[int, str]:
498
499
  """Gets location id from an API call"""
499
500
  url = f"{API_AUTOMATION_ENDPOINT}/Locations"
500
- LOG.debug(f"LocationId URL is {url}")
501
+ LOG.debug("LocationId URL is %s", url)
501
502
  req: list[dict[str, Any]] = await self.async_request("get", url)
502
503
  return (req[0]["id"], req[0]["locationHiloId"])
503
504
 
504
505
  async def get_devices(self, location_id: int) -> list[dict[str, Any]]:
505
506
  """Get list of all devices"""
506
507
  url = self._get_url("Devices", location_id)
507
- LOG.debug(f"Devices URL is {url}")
508
+ LOG.debug("Devices URL is %s", url)
508
509
  devices: list[dict[str, Any]] = await self.async_request("get", url)
509
510
  devices.append(await self.get_gateway(location_id))
510
511
  # Now it's time to add devices coming from external sources like hass
@@ -521,7 +522,7 @@ class API:
521
522
  ) -> None:
522
523
  """Sets device attributes"""
523
524
  url = self._get_url(f"Devices/{device.id}/Attributes", device.location_id)
524
- LOG.debug(f"Device Attribute URL is {url}")
525
+ LOG.debug("Device Attribute URL is %s", url)
525
526
  await self.async_request("put", url, json={key.hilo_attribute: value})
526
527
 
527
528
  async def get_event_notifications(self, location_id: int) -> dict[str, Any]:
@@ -549,7 +550,7 @@ class API:
549
550
  "viewed": false
550
551
  }"""
551
552
  url = self._get_url(None, location_id, events=True)
552
- LOG.debug(f"Event Notifications URL is {url}")
553
+ LOG.debug("Event Notifications URL is %s", url)
553
554
  return cast(dict[str, Any], await self.async_request("get", url))
554
555
 
555
556
  async def get_gd_events(
@@ -622,7 +623,7 @@ class API:
622
623
  else:
623
624
  url += f"/{event_id}"
624
625
 
625
- LOG.debug(f"get_gd_events URL is {url}")
626
+ LOG.debug("get_gd_events URL is %s", url)
626
627
  return cast(dict[str, Any], await self.async_request("get", url))
627
628
 
628
629
  async def get_seasons(self, location_id: int) -> dict[str, Any]:
@@ -645,13 +646,13 @@ class API:
645
646
  ]
646
647
  """
647
648
  url = self._get_url("Seasons", location_id, challenge=True)
648
- LOG.debug(f"Seasons URL is {url}")
649
+ LOG.debug("Seasons URL is %s", url)
649
650
  return cast(dict[str, Any], await self.async_request("get", url))
650
651
 
651
652
  async def get_gateway(self, location_id: int) -> dict[str, Any]:
652
653
  """Gets info about the Hilo hub (gateway)"""
653
654
  url = self._get_url("Gateways/Info", location_id)
654
- LOG.debug(f"Gateway URL is {url}")
655
+ LOG.debug("Gateway URL is %s", url)
655
656
  req = await self.async_request("get", url)
656
657
  saved_attrs = [
657
658
  "zigBeePairingActivated",
@@ -694,7 +695,7 @@ class API:
694
695
  ]
695
696
  """
696
697
  url = self._get_url("Weather", location_id)
697
- LOG.debug(f"Weather URL is {url}")
698
+ LOG.debug("Weather URL is %s", url)
698
699
  response = await self.async_request("get", url)
699
- LOG.debug(f"Weather API response: {response}")
700
+ LOG.debug("Weather API response: %s", response)
700
701
  return cast(dict[str, Any], await self.async_request("get", url))
@@ -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.6.01"
10
+ PYHILO_VERSION: Final = "2025.9.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"
@@ -10,7 +10,7 @@ from pyhilo.device import HiloDevice
10
10
  class Light(HiloDevice):
11
11
  def __init__(self, api: API, **kwargs: dict[str, Union[str, int]]):
12
12
  super().__init__(api, **kwargs) # type: ignore
13
- LOG.debug(f"Setting up Light device: {self.name}")
13
+ LOG.debug("Setting up Light device: %s", self.name)
14
14
 
15
15
  @property
16
16
  def brightness(self) -> float:
@@ -10,7 +10,7 @@ from pyhilo.device import HiloDevice
10
10
  class Sensor(HiloDevice):
11
11
  def __init__(self, api: API, **kwargs: dict[str, Union[str, int]]):
12
12
  super().__init__(api, **kwargs) # type: ignore
13
- LOG.debug(f"Setting up Sensor device: {self.name}")
13
+ LOG.debug("Setting up Sensor device: %s", self.name)
14
14
 
15
15
  @property
16
16
  def state(self) -> str:
@@ -10,7 +10,7 @@ from pyhilo.device import HiloDevice
10
10
  class Switch(HiloDevice):
11
11
  def __init__(self, api: API, **kwargs: dict[str, Union[str, int]]):
12
12
  super().__init__(api, **kwargs) # type: ignore
13
- LOG.debug(f"Setting up Switch device: {self.name}")
13
+ LOG.debug("Setting up Switch device: %s", self.name)
14
14
 
15
15
  @property
16
16
  def state(self) -> str:
@@ -38,7 +38,7 @@ class Devices:
38
38
  ]
39
39
 
40
40
  def parse_values_received(self, values: list[dict[str, Any]]) -> list[HiloDevice]:
41
- """Places value received in a dict while removing null attributes,
41
+ """Places value received in a dict while removing null attributes,
42
42
  this returns values to be mapped to devices.
43
43
  """
44
44
  readings = []
@@ -60,7 +60,7 @@ class Devices:
60
60
  device_identifier = reading.hilo_id
61
61
  if device := self.find_device(device_identifier):
62
62
  device.update_readings(reading)
63
- LOG.debug(f"{device} Received {reading}")
63
+ LOG.debug("%s Received %s", device, reading)
64
64
  if device not in updated_devices:
65
65
  updated_devices.append(device)
66
66
  else:
@@ -96,24 +96,24 @@ class Devices:
96
96
  fresh_devices = await self._api.get_devices(self.location_id)
97
97
  generated_devices = []
98
98
  for raw_device in fresh_devices:
99
- LOG.debug(f"Generating device {raw_device}")
99
+ LOG.debug("Generating device %s", raw_device)
100
100
  dev = self.generate_device(raw_device)
101
101
  generated_devices.append(dev)
102
102
  if dev not in self.devices:
103
103
  self.devices.append(dev)
104
104
  for device in self.devices:
105
105
  if device not in generated_devices:
106
- LOG.debug(f"Device unpaired {device}")
106
+ LOG.debug("Device unpaired %s", device)
107
107
  # Don't do anything with unpaired device for now.
108
108
  # self.devices.remove(device)
109
109
 
110
110
  async def update_devicelist_from_signalr(
111
111
  self, values: list[dict[str, Any]]
112
112
  ) -> list[HiloDevice]:
113
- #ic-dev21 not sure if this is dead code?
113
+ # ic-dev21 not sure if this is dead code?
114
114
  new_devices = []
115
115
  for raw_device in values:
116
- LOG.debug(f"Generating device {raw_device}")
116
+ LOG.debug("Generating device %s", raw_device)
117
117
  dev = self.generate_device(raw_device)
118
118
  if dev not in self.devices:
119
119
  self.devices.append(dev)
@@ -67,7 +67,7 @@ class Event:
67
67
 
68
68
  def update_wh(self, used_wH: float) -> None:
69
69
  """This function is used to update the used_kWh attribute during a Hilo Challenge Event"""
70
- LOG.debug(f"Updating Wh: {used_wH}")
70
+ LOG.debug("Updating Wh: %s", used_wH)
71
71
  self.used_kWh = round(used_wH / 1000, 2)
72
72
  self.last_update = datetime.now(timezone.utc).astimezone()
73
73
 
@@ -562,23 +562,28 @@ class GraphQlHelper:
562
562
  query, variable_values={"locationHiloId": location_hilo_id}
563
563
  ):
564
564
  LOG.debug(
565
- f"subscribe_to_device_updated: Received subscription result {result}"
565
+ "subscribe_to_device_updated: Received subscription result %s",
566
+ result,
566
567
  )
567
568
  device_hilo_id = self._handle_device_subscription_result(result)
568
569
  if callback:
569
570
  callback(device_hilo_id)
570
571
  except Exception as e:
571
572
  LOG.debug(
572
- f"subscribe_to_device_updated: Connection lost: {e}. Reconnecting in 5 seconds..."
573
+ "subscribe_to_device_updated: Connection lost: %s. Reconnecting in 5 seconds...",
574
+ e,
573
575
  )
574
576
  await asyncio.sleep(5)
575
577
  try:
576
578
  await self.call_get_location_query(location_hilo_id)
577
- LOG.debug("subscribe_to_device_updated, call_get_location_query success")
579
+ LOG.debug(
580
+ "subscribe_to_device_updated, call_get_location_query success"
581
+ )
578
582
 
579
583
  except Exception as e2:
580
584
  LOG.error(
581
- f"subscribe_to_device_updated, exception while reconnecting, retrying: {e2}"
585
+ "subscribe_to_device_updated, exception while reconnecting, retrying: %s",
586
+ e2,
582
587
  )
583
588
 
584
589
  async def subscribe_to_location_updated(
@@ -595,7 +600,7 @@ class GraphQlHelper:
595
600
  async for result in session.subscribe(
596
601
  query, variable_values={"locationHiloId": location_hilo_id}
597
602
  ):
598
- LOG.debug(f"Received subscription result {result}")
603
+ LOG.debug("Received subscription result %s", result)
599
604
  device_hilo_id = self._handle_location_subscription_result(result)
600
605
  callback(device_hilo_id)
601
606
  except asyncio.CancelledError:
@@ -618,7 +623,7 @@ class GraphQlHelper:
618
623
  attributes = self.mapper.map_device_subscription_values(devices_values)
619
624
  updated_device = self._devices.parse_values_received(attributes)
620
625
  # callback to update the device in the UI
621
- LOG.debug(f"Device updated: {updated_device}")
626
+ LOG.debug("Device updated: %s", updated_device)
622
627
  return devices_values.get("hiloId")
623
628
 
624
629
  def _handle_location_subscription_result(self, result: Dict[str, Any]) -> str:
@@ -626,5 +631,5 @@ class GraphQlHelper:
626
631
  attributes = self.mapper.map_location_subscription_values(devices_values)
627
632
  updated_device = self._devices.parse_values_received(attributes)
628
633
  # callback to update the device in the UI
629
- LOG.debug(f"Device updated: {updated_device}")
634
+ LOG.debug("Device updated: %s", updated_device)
630
635
  return devices_values.get("hiloId")
@@ -214,18 +214,22 @@ class WebsocketClient:
214
214
 
215
215
  if self._api.log_traces:
216
216
  LOG.debug(
217
- f"[TRACE] Sending data to websocket {self._api.endpoint} : {json.dumps(payload)}"
217
+ "[TRACE] Sending data to websocket %s : %s",
218
+ self._api.endpoint,
219
+ json.dumps(payload),
218
220
  )
219
221
  # Hilo added a control character (chr(30)) at the end of each payload they send.
220
222
  # They also expect this char to be there at the end of every payload we send them.
221
- LOG.debug(f"WebsocketClient _async_send_json payload: {payload}")
223
+ LOG.debug("WebsocketClient _async_send_json payload: %s", payload)
222
224
  await self._client.send_str(json.dumps(payload) + chr(30))
223
225
 
224
226
  def _parse_message(self, msg: dict[str, Any]) -> None:
225
227
  """Parse an incoming message."""
226
228
  if self._api.log_traces:
227
229
  LOG.debug(
228
- f"[TRACE] Received message on websocket(_parse_message) {self._api.endpoint}: {msg}"
230
+ "[TRACE] Received message on websocket(_parse_message) %s: %s",
231
+ self._api.endpoint,
232
+ msg,
229
233
  )
230
234
  if msg.get("type") == SignalRMsgType.PING:
231
235
  schedule_callback(self._async_pong)
@@ -272,7 +276,7 @@ class WebsocketClient:
272
276
 
273
277
  LOG.info("Websocket: Connecting to server %s", self._api.endpoint)
274
278
  if self._api.log_traces:
275
- LOG.debug(f"[TRACE] Websocket URL: {self._api.full_ws_url}")
279
+ LOG.debug("[TRACE] Websocket URL: %s", self._api.full_ws_url)
276
280
  headers = {
277
281
  "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits",
278
282
  "Pragma": "no-cache",
@@ -396,9 +400,13 @@ class WebsocketClient:
396
400
  except asyncio.TimeoutError:
397
401
  return
398
402
  self._ready_event.clear()
399
- LOG.debug(
400
- f"async_invoke invoke argument: {arg}, invocationId: {inv_id}, target: {target}, type: {type}"
401
- )
403
+ LOG.debug(
404
+ "async_invoke invoke argument: %s, invocationId: %s, target: %s, type: %s",
405
+ arg,
406
+ inv_id,
407
+ target,
408
+ type,
409
+ )
402
410
  await self._async_send_json(
403
411
  {
404
412
  "arguments": arg,
@@ -481,9 +489,9 @@ class WebsocketManager:
481
489
  Returns:
482
490
  Tuple containing the websocket URL and access token
483
491
  """
484
- LOG.debug(f"Getting websocket url for {config.endpoint}")
492
+ LOG.debug("Getting websocket url for %s", config.endpoint)
485
493
  url = f"{config.endpoint}/negotiate"
486
- LOG.debug(f"Negotiate URL is {url}")
494
+ LOG.debug("Negotiate URL is %s", url)
487
495
 
488
496
  resp = await self.async_request("post", url)
489
497
  ws_url = resp.get("url")
@@ -513,8 +521,8 @@ class WebsocketManager:
513
521
  config: The websocket configuration to get parameters for
514
522
  """
515
523
  uri = parse.urlparse(config.url)
516
- LOG.debug(f"Getting websocket params for {config.endpoint}")
517
- LOG.debug(f"Getting uri {uri}")
524
+ LOG.debug("Getting websocket params for %s", config.endpoint)
525
+ LOG.debug("Getting uri %s", uri)
518
526
 
519
527
  resp = await self.async_request(
520
528
  "post",
@@ -529,7 +537,7 @@ class WebsocketManager:
529
537
  config.full_ws_url = (
530
538
  f"{config.url}&id={config.connection_id}&access_token={config.token}"
531
539
  )
532
- LOG.debug(f"Getting full ws URL {config.full_ws_url}")
540
+ LOG.debug("Getting full ws URL %s", config.full_ws_url)
533
541
 
534
542
  transport_dict = resp.get("availableTransports", [])
535
543
  websocket_dict = {
@@ -544,5 +552,5 @@ class WebsocketManager:
544
552
  if config.endpoint == AUTOMATION_DEVICEHUB_ENDPOINT
545
553
  else "websocketChallenges"
546
554
  )
547
- LOG.debug(f"Calling set_state {state_key}_params")
555
+ LOG.debug("Calling set_state %s_params", state_key)
548
556
  await self._set_state(self._state_yaml, state_key, websocket_dict)
@@ -40,7 +40,7 @@ exclude = ".venv/.*"
40
40
 
41
41
  [tool.poetry]
42
42
  name = "python-hilo"
43
- version = "2025.6.1"
43
+ version = "2025.9.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>"]
@@ -74,7 +74,7 @@ python-dateutil = ">=2.8.2"
74
74
  python = "^3.9.0"
75
75
  voluptuous = ">=0.13.1"
76
76
  websockets = ">=8.1,<16.0"
77
- gql = "^3.5.2"
77
+ gql = ">=3.5.2,<5.0.0"
78
78
  pyyaml = "^6.0.2"
79
79
 
80
80
  [poetry.group.dev.dependencies]
@@ -1,51 +0,0 @@
1
- """Custom OAuth2 implementation."""
2
-
3
- from typing import Any, cast
4
-
5
- from homeassistant.core import HomeAssistant
6
- from homeassistant.helpers.config_entry_oauth2_flow import LocalOAuth2Implementation
7
-
8
- from pyhilo.const import AUTH_AUTHORIZE, AUTH_CLIENT_ID, AUTH_TOKEN, DOMAIN
9
- from pyhilo.oauth2helper import OAuth2Helper
10
-
11
-
12
- class AuthCodeWithPKCEImplementation(LocalOAuth2Implementation): # type: ignore[misc]
13
- """Custom OAuth2 implementation."""
14
-
15
- def __init__(
16
- self,
17
- hass: HomeAssistant,
18
- ) -> None:
19
- """Initialize AuthCodeWithPKCEImplementation."""
20
- super().__init__(
21
- hass,
22
- DOMAIN,
23
- AUTH_CLIENT_ID,
24
- "",
25
- AUTH_AUTHORIZE,
26
- AUTH_TOKEN,
27
- )
28
-
29
- self.oauth_helper = OAuth2Helper()
30
-
31
- # ... Override AbstractOAuth2Implementation details
32
- @property
33
- def name(self) -> str:
34
- """Name of the implementation."""
35
- return "Hilo"
36
-
37
- @property
38
- def extra_authorize_data(self) -> dict:
39
- """Extra data that needs to be appended to the authorize url."""
40
- return self.oauth_helper.get_authorize_parameters()
41
-
42
- async def async_resolve_external_data(self, external_data: Any) -> dict:
43
- """Resolve the authorization code to tokens."""
44
- return cast(
45
- dict,
46
- await self._token_request(
47
- self.oauth_helper.get_token_request_parameters(
48
- external_data["code"], external_data["state"]["redirect_uri"]
49
- )
50
- ),
51
- )
File without changes
File without changes