python-hilo 2026.9.1__tar.gz → 2026.10.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 (62) hide show
  1. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.github/workflows/release-drafter.yml +1 -1
  2. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.github/workflows/tests.yaml +2 -2
  3. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/PKG-INFO +1 -1
  4. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/api.py +5 -1
  5. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/const.py +1 -1
  6. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/util/state.py +14 -8
  7. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyproject.toml +1 -1
  8. python_hilo-2026.10.1/uv.lock +1643 -0
  9. python_hilo-2026.9.1/uv.lock +0 -1434
  10. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.bandit.yaml +0 -0
  11. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.devcontainer/devcontainer-lock.json +0 -0
  12. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.devcontainer/devcontainer.json +0 -0
  13. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.editorconfig +0 -0
  14. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.gitattributes +0 -0
  15. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  16. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.github/ISSUE_TEMPLATE/issue.md +0 -0
  17. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.github/dependabot.yml +0 -0
  18. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.github/labels.yml +0 -0
  19. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.github/release-drafter.yml +0 -0
  20. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.github/workflows/labeler.yml +0 -0
  21. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.gitignore +0 -0
  22. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/.pre-commit-config.yaml +0 -0
  23. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/LICENSE +0 -0
  24. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/README.md +0 -0
  25. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Challenge(Event) JSON/ChallengeAdded.json +0 -0
  26. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Challenge(Event) JSON/EventAdded.json +0 -0
  27. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Challenge(Event) JSON/EventCHConsumptionUpdatedValuesReceived(en cours de d/303/251fi).json" +0 -0
  28. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Challenge(Event) JSON/EventCHConsumptionUpdatedValuesReceived.json +0 -0
  29. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Challenge(Event) JSON/EventCHDetailsInitialValuesReceived.json +0 -0
  30. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Challenge(Event) JSON/EventCHDetailsUpdatedValuesReceived.json +0 -0
  31. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Challenge(Event) JSON/EventFlexConsumptionUpdatedValuesReceived.json +0 -0
  32. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Challenge(Event) JSON/EventFlexDetailsInitialValuesReceived.json +0 -0
  33. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Challenge(Event) JSON/EventFlexDetailsUpdatedValuesReceived.json +0 -0
  34. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Device Replies/DeviceListInitialValuesReceived.json +0 -0
  35. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Old JSONs (pre 2025-26 season)/ChallengeAdded.json +0 -0
  36. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Old JSONs (pre 2025-26 season)/ChallengeConsumptionUpdatedValuesReceived.json +0 -0
  37. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Old JSONs (pre 2025-26 season)/ChallengeDetailsInitialValuesReceived.json +0 -0
  38. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Old JSONs (pre 2025-26 season)/ChallengeListInitialValuesReceived.json +0 -0
  39. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Old JSONs (pre 2025-26 season)/ChallengeListUpdatedValuesReceived.json +0 -0
  40. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Postman/Hilo Prod.postman_environment.json +0 -0
  41. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Postman/Hilo.postman_collection.json +0 -0
  42. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/doc/Readme.md +0 -0
  43. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/__init__.py +0 -0
  44. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/device/__init__.py +0 -0
  45. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/device/climate.py +0 -0
  46. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/device/graphql_value_mapper.py +0 -0
  47. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/device/light.py +0 -0
  48. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/device/sensor.py +0 -0
  49. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/device/switch.py +0 -0
  50. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/devices.py +0 -0
  51. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/event.py +0 -0
  52. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/exceptions.py +0 -0
  53. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/graphql.py +0 -0
  54. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/oauth2helper.py +0 -0
  55. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/signalr.py +0 -0
  56. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/pyhilo/util/__init__.py +0 -0
  57. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/tests/__init__.py +0 -0
  58. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/tests/conftest.py +0 -0
  59. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/tests/test_device.py +0 -0
  60. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/tests/test_signalr.py +0 -0
  61. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/tests/test_util.py +0 -0
  62. {python_hilo-2026.9.1 → python_hilo-2026.10.1}/tests/test_util_state.py +0 -0
@@ -9,6 +9,6 @@ jobs:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
11
  - name: Run release-drafter
12
- uses: release-drafter/release-drafter@v7.7.0
12
+ uses: release-drafter/release-drafter@v7.10.0
13
13
  env:
14
14
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -24,7 +24,7 @@ jobs:
24
24
  python-version: ${{ env.DEFAULT_PYTHON }}
25
25
 
26
26
  - name: Install uv
27
- uses: astral-sh/setup-uv@v10.0.1
27
+ uses: astral-sh/setup-uv@v10.2.0
28
28
 
29
29
  - name: Install dependencies
30
30
  run: uv sync --group dev
@@ -47,7 +47,7 @@ jobs:
47
47
  python-version: ${{ env.DEFAULT_PYTHON }}
48
48
 
49
49
  - name: Install uv
50
- uses: astral-sh/setup-uv@v10.0.1
50
+ uses: astral-sh/setup-uv@v10.2.0
51
51
 
52
52
  - name: Install dependencies
53
53
  run: uv sync --group test
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-hilo
3
- Version: 2026.9.1
3
+ Version: 2026.10.1
4
4
  Summary: A Python3, async interface to the Hilo API
5
5
  Project-URL: Repository, https://github.com/dvd-dev/python-hilo
6
6
  Author-email: David Vallee Delisle <me@dvd.dev>
@@ -65,11 +65,12 @@ class API:
65
65
  oauth_session: OAuth2Session,
66
66
  request_retries: int = REQUEST_RETRY,
67
67
  log_traces: bool = False,
68
+ state_yaml: str = DEFAULT_STATE_FILE,
68
69
  ) -> None:
69
70
  """Initialize"""
70
71
  self._backoff_refresh_lock_api = asyncio.Lock()
71
72
  self._request_retries = request_retries
72
- self._state_yaml: str = DEFAULT_STATE_FILE
73
+ self._state_yaml: str = state_yaml
73
74
  self.state: StateDict = {}
74
75
  self.async_request = self._wrap_request_method(self._request_retries)
75
76
  self.device_attributes = get_device_attributes()
@@ -92,6 +93,7 @@ class API:
92
93
  oauth_session: OAuth2Session,
93
94
  request_retries: int = REQUEST_RETRY,
94
95
  log_traces: bool = False,
96
+ state_yaml: str = DEFAULT_STATE_FILE,
95
97
  ) -> API:
96
98
  """Get an authenticated API object.
97
99
  :param session: The ``aiohttp`` ``ClientSession`` session used for all HTTP requests
@@ -100,6 +102,7 @@ class API:
100
102
  :type oauth_session: ``config_entry_oauth2_flow.OAuth2Session``
101
103
  :param request_retries: The default number of request retries to use
102
104
  :type request_retries: ``int``
105
+ :param state_yaml: Path of the file where the API state (tokens, etc.) is saved.
103
106
  :rtype: :meth:`pyhilo.api.API`
104
107
  """
105
108
  api = cls(
@@ -107,6 +110,7 @@ class API:
107
110
  oauth_session=oauth_session,
108
111
  request_retries=request_retries,
109
112
  log_traces=log_traces,
113
+ state_yaml=state_yaml,
110
114
  )
111
115
  # Test token before post init
112
116
  await api.async_get_access_token()
@@ -11,7 +11,7 @@ INSTANCE_ID: Final = str(uuid.uuid4())[24:]
11
11
  LOG: Final = logging.getLogger(__package__)
12
12
  DEFAULT_STATE_FILE: Final = "hilo_state.yaml"
13
13
  REQUEST_RETRY: Final = 9
14
- PYHILO_VERSION: Final = "2026.9.01"
14
+ PYHILO_VERSION: Final = "2026.10.01"
15
15
  # TODO: Find a way to keep previous line in sync with pyproject.toml automatically
16
16
 
17
17
  CONTENT_TYPE_FORM: Final = "application/x-www-form-urlencoded"
@@ -115,6 +115,15 @@ def _write_state(state_yaml: str, state: dict[str, Any] | StateDict) -> None:
115
115
  os.replace(tmp_path, state_yaml)
116
116
 
117
117
 
118
+ async def _async_write_state(
119
+ state_yaml: str, state: dict[str, Any] | StateDict
120
+ ) -> None:
121
+ """Run _write_state in a worker thread so it doesn't block the event loop."""
122
+ await asyncio.get_running_loop().run_in_executor(
123
+ None, _write_state, state_yaml, state
124
+ )
125
+
126
+
118
127
  async def get_state(state_yaml: str, _already_locked: bool = False) -> StateDict:
119
128
  """Read in state yaml.
120
129
 
@@ -143,10 +152,10 @@ async def get_state(state_yaml: str, _already_locked: bool = False) -> StateDict
143
152
  )
144
153
  defaults = _get_defaults(StateDict)
145
154
  if _already_locked:
146
- _write_state(state_yaml, defaults)
155
+ await _async_write_state(state_yaml, defaults)
147
156
  else:
148
157
  async with lock:
149
- _write_state(state_yaml, defaults)
158
+ await _async_write_state(state_yaml, defaults)
150
159
  return defaults
151
160
 
152
161
  return state_yaml_payload
@@ -159,10 +168,10 @@ async def get_state(state_yaml: str, _already_locked: bool = False) -> StateDict
159
168
  )
160
169
  defaults = _get_defaults(StateDict)
161
170
  if _already_locked:
162
- _write_state(state_yaml, defaults)
171
+ await _async_write_state(state_yaml, defaults)
163
172
  else:
164
173
  async with lock:
165
- _write_state(state_yaml, defaults)
174
+ await _async_write_state(state_yaml, defaults)
166
175
  return defaults
167
176
 
168
177
 
@@ -186,7 +195,4 @@ async def set_state(
186
195
  merged_state: dict[str, Any] = {key: {**current_state.get(key, {}), **state}} # type: ignore[dict-item]
187
196
  new_state: dict[str, Any] = {**current_state, **merged_state}
188
197
  LOG.debug("Saving state to yaml file")
189
- # TODO: Use asyncio.get_running_loop() and run_in_executor to write
190
- # to the file in a non blocking manner. Currently, yaml.dump is
191
- # synchronous on the main event loop.
192
- _write_state(state_yaml, new_state)
198
+ await _async_write_state(state_yaml, new_state)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-hilo"
3
- version = "2026.9.1"
3
+ version = "2026.10.1"
4
4
  description = "A Python3, async interface to the Hilo API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"