python-hilo 2026.10.1__tar.gz → 2026.11.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 (76) hide show
  1. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.devcontainer/devcontainer.json +1 -1
  2. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.github/workflows/tests.yaml +14 -10
  3. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.pre-commit-config.yaml +2 -2
  4. python_hilo-2026.11.1/AGENTS.md +56 -0
  5. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/PKG-INFO +6 -5
  6. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/README.md +2 -2
  7. python_hilo-2026.11.1/doc/README.md +92 -0
  8. python_hilo-2026.11.1/doc/graphql/onAnyDeviceUpdated-cee.json +33 -0
  9. python_hilo-2026.11.1/doc/graphql/onAnyDeviceUpdated-meter.json +22 -0
  10. python_hilo-2026.11.1/doc/graphql/onAnyDeviceUpdated-tstat.json +52 -0
  11. python_hilo-2026.11.1/doc/rest/location_weather.json +7 -0
  12. python_hilo-2026.11.1/doc/rest/season_events_ch_2025.json +42 -0
  13. python_hilo-2026.11.1/doc/rest/season_events_ch_2026_empty.json +5 -0
  14. python_hilo-2026.11.1/doc/rest/season_events_hilo_2024.json +36 -0
  15. python_hilo-2026.11.1/doc/rest/seasons_summary.json +27 -0
  16. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/__init__.py +2 -0
  17. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/api.py +45 -15
  18. python_hilo-2026.11.1/pyhilo/auth.py +42 -0
  19. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/const.py +10 -1
  20. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/device/__init__.py +37 -3
  21. python_hilo-2026.11.1/pyhilo/device/climate.py +247 -0
  22. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/device/graphql_value_mapper.py +15 -8
  23. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyproject.toml +6 -5
  24. python_hilo-2026.11.1/tests/test_api_auth.py +135 -0
  25. python_hilo-2026.11.1/tests/test_climate.py +238 -0
  26. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/tests/test_device.py +127 -10
  27. python_hilo-2026.11.1/tests/test_graphql_value_mapper.py +126 -0
  28. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/tests/test_util_state.py +41 -9
  29. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/uv.lock +47 -423
  30. python_hilo-2026.10.1/doc/Readme.md +0 -111
  31. python_hilo-2026.10.1/pyhilo/device/climate.py +0 -100
  32. python_hilo-2026.10.1/tests/conftest.py +0 -18
  33. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.bandit.yaml +0 -0
  34. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.devcontainer/devcontainer-lock.json +0 -0
  35. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.editorconfig +0 -0
  36. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.gitattributes +0 -0
  37. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  38. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.github/ISSUE_TEMPLATE/issue.md +0 -0
  39. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.github/dependabot.yml +0 -0
  40. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.github/labels.yml +0 -0
  41. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.github/release-drafter.yml +0 -0
  42. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.github/workflows/labeler.yml +0 -0
  43. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.github/workflows/release-drafter.yml +0 -0
  44. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/.gitignore +0 -0
  45. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/LICENSE +0 -0
  46. /python_hilo-2026.10.1/doc/Postman/Hilo Prod.postman_environment.json → /python_hilo-2026.11.1/doc/postman/hilo-prod.postman_environment.json +0 -0
  47. /python_hilo-2026.10.1/doc/Postman/Hilo.postman_collection.json → /python_hilo-2026.11.1/doc/postman/hilo.postman_collection.json +0 -0
  48. {python_hilo-2026.10.1/doc/Challenge(Event) JSON → python_hilo-2026.11.1/doc/signalr/challengehub}/ChallengeAdded.json +0 -0
  49. {python_hilo-2026.10.1/doc/Challenge(Event) JSON → python_hilo-2026.11.1/doc/signalr/challengehub}/EventAdded.json +0 -0
  50. /python_hilo-2026.10.1/doc/Challenge(Event) JSON/EventCHConsumptionUpdatedValuesReceived(en cours de d/303/251fi).json" → /python_hilo-2026.11.1/doc/signalr/challengehub/EventCHConsumptionUpdatedValuesReceived-during-event.json +0 -0
  51. {python_hilo-2026.10.1/doc/Challenge(Event) JSON → python_hilo-2026.11.1/doc/signalr/challengehub}/EventCHConsumptionUpdatedValuesReceived.json +0 -0
  52. {python_hilo-2026.10.1/doc/Challenge(Event) JSON → python_hilo-2026.11.1/doc/signalr/challengehub}/EventCHDetailsInitialValuesReceived.json +0 -0
  53. {python_hilo-2026.10.1/doc/Challenge(Event) JSON → python_hilo-2026.11.1/doc/signalr/challengehub}/EventCHDetailsUpdatedValuesReceived.json +0 -0
  54. {python_hilo-2026.10.1/doc/Challenge(Event) JSON → python_hilo-2026.11.1/doc/signalr/challengehub}/EventFlexConsumptionUpdatedValuesReceived.json +0 -0
  55. {python_hilo-2026.10.1/doc/Challenge(Event) JSON → python_hilo-2026.11.1/doc/signalr/challengehub}/EventFlexDetailsInitialValuesReceived.json +0 -0
  56. {python_hilo-2026.10.1/doc/Challenge(Event) JSON → python_hilo-2026.11.1/doc/signalr/challengehub}/EventFlexDetailsUpdatedValuesReceived.json +0 -0
  57. {python_hilo-2026.10.1/doc/Old JSONs (pre 2025-26 season) → python_hilo-2026.11.1/doc/signalr/challengehub/legacy-pre-2025}/ChallengeAdded.json +0 -0
  58. {python_hilo-2026.10.1/doc/Old JSONs (pre 2025-26 season) → python_hilo-2026.11.1/doc/signalr/challengehub/legacy-pre-2025}/ChallengeConsumptionUpdatedValuesReceived.json +0 -0
  59. {python_hilo-2026.10.1/doc/Old JSONs (pre 2025-26 season) → python_hilo-2026.11.1/doc/signalr/challengehub/legacy-pre-2025}/ChallengeDetailsInitialValuesReceived.json +0 -0
  60. {python_hilo-2026.10.1/doc/Old JSONs (pre 2025-26 season) → python_hilo-2026.11.1/doc/signalr/challengehub/legacy-pre-2025}/ChallengeListInitialValuesReceived.json +0 -0
  61. {python_hilo-2026.10.1/doc/Old JSONs (pre 2025-26 season) → python_hilo-2026.11.1/doc/signalr/challengehub/legacy-pre-2025}/ChallengeListUpdatedValuesReceived.json +0 -0
  62. {python_hilo-2026.10.1/doc/Device Replies → python_hilo-2026.11.1/doc/signalr/devicehub}/DeviceListInitialValuesReceived.json +0 -0
  63. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/device/light.py +0 -0
  64. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/device/sensor.py +0 -0
  65. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/device/switch.py +0 -0
  66. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/devices.py +0 -0
  67. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/event.py +0 -0
  68. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/exceptions.py +0 -0
  69. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/graphql.py +0 -0
  70. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/oauth2helper.py +0 -0
  71. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/signalr.py +0 -0
  72. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/util/__init__.py +0 -0
  73. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/pyhilo/util/state.py +0 -0
  74. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/tests/__init__.py +0 -0
  75. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/tests/test_signalr.py +0 -0
  76. {python_hilo-2026.10.1 → python_hilo-2026.11.1}/tests/test_util.py +0 -0
@@ -47,7 +47,7 @@
47
47
  "ghcr.io/devcontainers/features/github-cli:1": {},
48
48
  "ghcr.io/jsburckhardt/devcontainer-features/uv:1": {}
49
49
  },
50
- "image": "mcr.microsoft.com/devcontainers/python:3.11-bookworm",
50
+ "image": "mcr.microsoft.com/devcontainers/python:3-3.14-bookworm",
51
51
  "name": "A Python3, async interface to the Hilo API",
52
52
  "postCreateCommand": "uv sync --group dev --group test && uv run pre-commit install"
53
53
  }
@@ -8,7 +8,7 @@ on:
8
8
  pull_request:
9
9
 
10
10
  env:
11
- DEFAULT_PYTHON: 3.13
11
+ DEFAULT_PYTHON: "3.14"
12
12
 
13
13
  jobs:
14
14
  pre-commit:
@@ -19,12 +19,12 @@ jobs:
19
19
  uses: actions/checkout@v7
20
20
 
21
21
  - name: Set up Python ${{ env.DEFAULT_PYTHON }}
22
- uses: actions/setup-python@v7.0.0
22
+ uses: actions/setup-python@v7.1.0
23
23
  with:
24
24
  python-version: ${{ env.DEFAULT_PYTHON }}
25
25
 
26
26
  - name: Install uv
27
- uses: astral-sh/setup-uv@v10.2.0
27
+ uses: astral-sh/setup-uv@v10.3.3
28
28
 
29
29
  - name: Install dependencies
30
30
  run: uv sync --group dev
@@ -36,21 +36,25 @@ jobs:
36
36
 
37
37
  tests:
38
38
  runs-on: "ubuntu-latest"
39
- name: Tests
39
+ name: Tests (${{ matrix.python-version }})
40
+ strategy:
41
+ matrix:
42
+ # Mirrors the Hilo integration, which still supports Home Assistant releases on 3.13.
43
+ python-version: ["3.13", "3.14"]
40
44
  steps:
41
45
  - name: Check out the repository
42
46
  uses: actions/checkout@v7
43
47
 
44
- - name: Set up Python ${{ env.DEFAULT_PYTHON }}
45
- uses: actions/setup-python@v7.0.0
48
+ - name: Set up Python ${{ matrix.python-version }}
49
+ uses: actions/setup-python@v7.1.0
46
50
  with:
47
- python-version: ${{ env.DEFAULT_PYTHON }}
51
+ python-version: ${{ matrix.python-version }}
48
52
 
49
53
  - name: Install uv
50
- uses: astral-sh/setup-uv@v10.2.0
54
+ uses: astral-sh/setup-uv@v10.3.3
51
55
 
52
56
  - name: Install dependencies
53
- run: uv sync --group test
57
+ run: uv sync --python ${{ matrix.python-version }} --group test
54
58
 
55
59
  - name: Run tests
56
- run: uv run pytest -v
60
+ run: uv run --python ${{ matrix.python-version }} pytest -v
@@ -14,10 +14,10 @@ repos:
14
14
  hooks:
15
15
  - id: ruff-format
16
16
  args: [--quiet]
17
- files: ^pyhilo/.+\.py$
17
+ files: ^(pyhilo|tests)/.+\.py$
18
18
  - id: ruff
19
19
  args: [--fix, --exit-non-zero-on-fix]
20
- files: ^pyhilo/.+\.py$
20
+ files: ^(pyhilo|tests)/.+\.py$
21
21
  - repo: https://github.com/codespell-project/codespell
22
22
  rev: v2.4.1
23
23
  hooks:
@@ -0,0 +1,56 @@
1
+ # Coding Assistant Instructions: python-hilo Library
2
+
3
+ This repository contains `pyhilo`, a standalone asynchronous Python client library for Hilo energy services (SignalR WebSocket, GraphQL, and REST APIs).
4
+
5
+ ## Git Commit Guidelines
6
+
7
+ - **First line**: Short, specific imperative sentence (e.g. "Add abstract auth interface for token provider"). No emojis in titles.
8
+ - **Commit description**: Focus on the **WHY** (problem, context, and architectural rationale). Keep it concise, factual, and proportional.
9
+ - **Do NOT amend, squash, or rebase commits that have already been pushed to the PR branch after the PR is opened**: Reviewers need to follow the commit history and see what changed since their last review.
10
+
11
+ ## Development Commands
12
+
13
+ - Install dependencies: `uv sync`
14
+ - Run test suite: `uv run pytest`
15
+ - Run a specific test: `uv run pytest tests/test_device.py`
16
+ - Check linting and formatting: `uv run ruff check .` and `uv run ruff format --check .`
17
+
18
+ ## Architecture & Boundaries
19
+
20
+ - **Strict Decoupling from Home Assistant**:
21
+ - This library MUST NEVER import `homeassistant` or depend on Home Assistant Core modules, constants, or types.
22
+ - The library must be 100% installable, testable, and runnable independently in any standard Python 3.13+ environment.
23
+ - **Authentication Contract**:
24
+ - Expose an abstract base class `AbstractAuth` (`pyhilo.auth`):
25
+ ```python
26
+ class AbstractAuth(ABC):
27
+ @abstractmethod
28
+ async def async_get_access_token(self) -> str:
29
+ """Return a valid access token, refreshing if necessary."""
30
+ ```
31
+ - The library calls this method whenever it needs a valid token, leaving token storage and refresh mechanisms to the consumer (such as the Hilo Home Assistant integration or CLI tools).
32
+ - **Backwards Compatibility**: When modifying public API signatures, provide temporary shims with deprecation warnings for at least 1–2 minor versions to prevent breaking downstream consumers.
33
+ - **Credential Protection**: Never log raw access tokens, refresh tokens, passwords, or decoded JWT claims in `DEBUG` or `INFO` logs.
34
+
35
+ ## Testing Standards
36
+
37
+ - Verify library protocol behavior, event decoding, device mapping, and error handling.
38
+ - When writing tests or implementation code, ensure all parameters have precise type annotations. **The use of `Any` is strictly forbidden in new code.**
39
+ - **Single purpose tests**: Each test function must verify only one specific behavior or outcome. Avoid testing multiple unrelated scenarios in a single test; split them into focused tests or use parametrization.
40
+ - Prefer standard library `unittest.mock` (`AsyncMock`, `MagicMock`) over large bespoke stub classes.
41
+ - Prefer `@pytest.mark.usefixtures` over unused fixture arguments.
42
+ - Avoid branching (`if`/`else`) in tests; use `@pytest.mark.parametrize` with `pytest.param(..., id="...")` for table-driven testing.
43
+ - Tests must execute cleanly with `uv run pytest` without requiring network access.
44
+
45
+ ## Good practices
46
+
47
+ - Built with `asyncio`, `aiohttp`, `pysignalr`, and `gql`.
48
+ - Non-blocking I/O: avoid any synchronous disk reads/writes on active event loops. Accept explicit file paths when managing state.
49
+ - Keep `try` clauses as small as possible.
50
+ - When validation guarantees a dict key exists, prefer direct key access (`data["key"]`) instead of `.get("key")`.
51
+ - Keep comments concise and explain only the WHY. Do not restate code or use divider comments (`# --- XYZ ---`).
52
+ - CalVer format: Versions follow `vYYYY.MM.MICRO` (e.g. `v2026.10.1`).
53
+
54
+ ## AI policy
55
+
56
+ Autonomous contributions are not accepted. Every change must be reviewed and understood by a human contributor before submission. Never open pull requests, create release tags, or post comments autonomously without explicit contributor instruction.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-hilo
3
- Version: 2026.10.1
3
+ Version: 2026.11.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>
@@ -11,11 +11,12 @@ Classifier: Framework :: aiohttp
11
11
  Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Programming Language :: Python
13
13
  Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
15
16
  Classifier: Programming Language :: Python :: Implementation :: CPython
16
17
  Classifier: Programming Language :: Python :: Implementation :: PyPy
17
18
  Classifier: Topic :: Home Automation
18
- Requires-Python: >=3.11
19
+ Requires-Python: >=3.13
19
20
  Requires-Dist: aiofiles>=23.2.1
20
21
  Requires-Dist: aiohttp>=3.8.0
21
22
  Requires-Dist: backoff>=1.11.1
@@ -34,7 +35,7 @@ Description-Content-Type: text/markdown
34
35
  [![Build Status][build-shield]][build]
35
36
  [![Open in Dev Containers][devcontainer-shield]][devcontainer]
36
37
 
37
- `python-hilo` (aka `pyhilo`) is a Python 3.11, `asyncio`-driven interface to the unofficial
38
+ `python-hilo` (aka `pyhilo`) is a Python 3.13+, `asyncio`-driven interface to the unofficial
38
39
  Hilo API from Hydro Quebec. This is meant to be integrated into Home Assistant but can also
39
40
  be used as a standalone library.
40
41
 
@@ -63,7 +64,7 @@ for formatting and linting.
63
64
 
64
65
  You need at least:
65
66
 
66
- - Python 3.11+
67
+ - Python 3.13+
67
68
  - [uv][uv]
68
69
 
69
70
  To install all packages, including all development requirements:
@@ -6,7 +6,7 @@
6
6
  [![Build Status][build-shield]][build]
7
7
  [![Open in Dev Containers][devcontainer-shield]][devcontainer]
8
8
 
9
- `python-hilo` (aka `pyhilo`) is a Python 3.11, `asyncio`-driven interface to the unofficial
9
+ `python-hilo` (aka `pyhilo`) is a Python 3.13+, `asyncio`-driven interface to the unofficial
10
10
  Hilo API from Hydro Quebec. This is meant to be integrated into Home Assistant but can also
11
11
  be used as a standalone library.
12
12
 
@@ -35,7 +35,7 @@ for formatting and linting.
35
35
 
36
36
  You need at least:
37
37
 
38
- - Python 3.11+
38
+ - Python 3.13+
39
39
  - [uv][uv]
40
40
 
41
41
  To install all packages, including all development requirements:
@@ -0,0 +1,92 @@
1
+ # Hilo API reference payloads
2
+
3
+ Captured responses from Hilo's services, grouped by transport to mirror the library modules.
4
+ IDs, URNs and serial numbers in the payload files are anonymized.
5
+
6
+ | Folder | Transport | Library module | Contents |
7
+ |---|---|---|---|
8
+ | [`rest/`](rest/) | HTTPS REST | `pyhilo/api.py` | Automation (`/Locations`, `/Weather`) and challenge season endpoints |
9
+ | [`graphql/`](graphql/) | GraphQL subscription | `pyhilo/graphql.py` | `onAnyDeviceUpdated` payloads per device type |
10
+ | [`signalr/devicehub/`](signalr/devicehub/) | SignalR WebSocket | `pyhilo/signalr.py` | DeviceHub messages |
11
+ | [`signalr/challengehub/`](signalr/challengehub/) | SignalR WebSocket | `pyhilo/signalr.py` | ChallengeHub messages, 2025-26 season onward (CH = Winter Credits, Flex = Flex D) |
12
+ | [`signalr/challengehub/legacy-pre-2025/`](signalr/challengehub/legacy-pre-2025/) | SignalR WebSocket | — | Challenge messages before the CH/Flex split; kept for reference |
13
+ | [`postman/`](postman/) | — | — | Postman collection and environment for manual testing |
14
+
15
+ ## Adding a payload
16
+
17
+ 1. Strip tokens, location URNs, serial numbers and personal data before committing.
18
+ 2. Name REST files after the endpoint (`season_events_ch_2025.json`) and SignalR/GraphQL files after the message target (`EventCHDetailsInitialValuesReceived.json`).
19
+ 3. Add a short suffix for variants (`-during-event`, `-empty`, `-meter`).
20
+
21
+ # Using Postman to query Hilo
22
+
23
+ Import [`postman/hilo.postman_collection.json`](postman/hilo.postman_collection.json) and [`postman/hilo-prod.postman_environment.json`](postman/hilo-prod.postman_environment.json), then select the **Hilo Prod** environment.
24
+
25
+ ## Authentication
26
+
27
+ The collection uses OAuth 2.0 at the collection level. Open the collection's **Authorization** tab, click **Get New Access Token**, log in with your Hilo credentials in the browser window, then click **Use Token**. Every request in the collection inherits that token.
28
+
29
+ Tokens are short-lived and contain personally identifiable information. Do not share them or commit an exported environment that contains them.
30
+
31
+ ## REST requests
32
+
33
+ With the access token set, the REST requests (`1-Get Locations`, `Get SeasonsSummary`, `Get CHEvents`, `Get FlexEvents`, ...) can be sent directly.
34
+
35
+ ## Connecting to a hub
36
+
37
+ Each hub folder (`DeviceHub`, `ChallengeHub`) contains two negotiate requests. Send them in order:
38
+
39
+ 1. `1-API Hilo ... Hub Negotiate` stores the hub URL and hub access token in the environment (`_deviceHubUrl` / `_deviceHubAccessToken`, or `_challengeHubUrl` / `_challengeHubAccessToken`).
40
+ 2. `2-... Hub Negotiate` stores the connection token (`_deviceHubConnectionToken` or `_challengeHubConnectionToken`).
41
+
42
+ Then open a new WebSocket request in Postman:
43
+
44
+ 1. Use the hub URL from the environment (`{{_deviceHubUrl}}` or `{{_challengeHubUrl}}`).
45
+ 2. In **Headers**, add `Authorization` with the value `Bearer {{_deviceHubAccessToken}}` (or `Bearer {{_challengeHubAccessToken}}`).
46
+ 3. Connect, then send the handshake below. The trailing record separator character `chr(30)` is required after every message.
47
+
48
+ ```
49
+ {
50
+ "protocol": "json",
51
+ "version": 1
52
+ }
53
+ ```
54
+
55
+ ### DeviceHub
56
+
57
+ Invoke `SubscribeToLocation` with your location ID to get your device information:
58
+
59
+ ```
60
+ {
61
+ "arguments": [
62
+ 69420
63
+ ],
64
+ "invocationId": "0",
65
+ "target": "SubscribeToLocation",
66
+ "type": 1
67
+ }
68
+ ```
69
+
70
+ You should receive a [`DeviceListInitialValuesReceived`](signalr/devicehub/DeviceListInitialValuesReceived.json) message back.
71
+
72
+ ### ChallengeHub
73
+
74
+ Since the 2025-2026 season and the addition of Flex D, messages are split between Winter Credits (Crédit Hivernal) and Flex D. Winter Credits targets contain `CH` and Flex D targets contain `Flex`. Each rate has its own event ID; for example, event 337 was for Winter Credits and event 338 for Flex D, both at the same time. You only have access to the one matching your rate.
75
+
76
+ #### SubscribeToEventCH or SubscribeToEventFlex
77
+
78
+ ```
79
+ {
80
+ "arguments": [
81
+ {
82
+ "locationHiloId": "urn:YOUR-URN",
83
+ "eventId": ID(numeric)
84
+ }
85
+ ],
86
+ "invocationId": "1",
87
+ "target": "SubscribeToEventCH",
88
+ "type": 1
89
+ }
90
+ ```
91
+
92
+ Returns [`EventCHDetailsInitialValuesReceived`](signalr/challengehub/EventCHDetailsInitialValuesReceived.json) or [`EventFlexDetailsInitialValuesReceived`](signalr/challengehub/EventFlexDetailsInitialValuesReceived.json).
@@ -0,0 +1,33 @@
1
+ {
2
+ "data": {
3
+ "onAnyDeviceUpdated": {
4
+ "deviceType": "cee",
5
+ "locationHiloId": "urn:hilo:crm:00000-abcdef:0",
6
+ "transmissionTime": "2026-10-24T19:46:35.207Z",
7
+ "operationId": "00000000-0000-0000-0000-000000000002",
8
+ "status": "REPORT",
9
+ "device": {
10
+ "deviceType": "cee",
11
+ "hiloId": "urn:hilo:philo:4a5b6c7d8e9f0001:0",
12
+ "physicalAddress": "4A5B6C7D8E9F0001",
13
+ "connectionStatus": "CONNECTED",
14
+ "gDState": "EXCLUDED",
15
+ "version": "45",
16
+ "probeTemp": {
17
+ "value": 54,
18
+ "kind": "DEGREE_CELSIUS"
19
+ },
20
+ "zigbeeVersion": "45",
21
+ "state": "ON",
22
+ "ccrType": "WATER_HEATER",
23
+ "alerts": [
24
+ "0"
25
+ ],
26
+ "power": {
27
+ "value": 0,
28
+ "kind": "WATT"
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "data": {
3
+ "onAnyDeviceUpdated": {
4
+ "deviceType": "meter",
5
+ "locationHiloId": "urn:hilo:crm:00000-abcdef:0",
6
+ "transmissionTime": "2026-10-24T19:48:54.966Z",
7
+ "operationId": "00000000-0000-0000-0000-000000000001",
8
+ "status": "REPORT",
9
+ "device": {
10
+ "deviceType": "meter",
11
+ "hiloId": "urn:hilo:philo:0x006baa0000000001-smart_meter:0",
12
+ "physicalAddress": "0x006BAA0000000001-smart_meter",
13
+ "connectionStatus": "CONNECTED",
14
+ "zigBeeChannel": 24,
15
+ "power": {
16
+ "value": 0.572,
17
+ "kind": "KILOWATT"
18
+ }
19
+ }
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "data": {
3
+ "onAnyDeviceUpdated": {
4
+ "deviceType": "tstat",
5
+ "locationHiloId": "urn:hilo:crm:00000-abcdef:0",
6
+ "transmissionTime": "2026-10-29T22:44:57.250Z",
7
+ "operationId": "00000000-0000-0000-0000-000000000003",
8
+ "status": "REPORT",
9
+ "device": {
10
+ "deviceType": "tstat",
11
+ "hiloId": "urn:hilo:philo:1a2b3c4d5e6f0001:0",
12
+ "physicalAddress": "1A2B3C4D5E6F0001",
13
+ "connectionStatus": "CONNECTED",
14
+ "ambientHumidity": 0,
15
+ "gDState": "EXCLUDED",
16
+ "version": "21",
17
+ "zigbeeVersion": "22",
18
+ "ambientTemperature": {
19
+ "value": 18,
20
+ "kind": "DEGREE_CELSIUS"
21
+ },
22
+ "ambientTempSetpoint": {
23
+ "value": 15,
24
+ "kind": "DEGREE_CELSIUS"
25
+ },
26
+ "maxAmbientTempSetpoint": {
27
+ "value": 30,
28
+ "kind": "DEGREE_CELSIUS"
29
+ },
30
+ "minAmbientTempSetpoint": {
31
+ "value": 5,
32
+ "kind": "DEGREE_CELSIUS"
33
+ },
34
+ "maxAmbientTempSetpointLimit": {
35
+ "value": 30,
36
+ "kind": "DEGREE_CELSIUS"
37
+ },
38
+ "minAmbientTempSetpointLimit": {
39
+ "value": 5,
40
+ "kind": "DEGREE_CELSIUS"
41
+ },
42
+ "heatDemand": 0,
43
+ "power": {
44
+ "value": null,
45
+ "kind": "WATT"
46
+ },
47
+ "mode": "UNKNOWN",
48
+ "allowedModes": []
49
+ }
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "temperature": 10.0,
3
+ "time": "0001-01-01T00:00:00Z",
4
+ "condition": "Clear",
5
+ "icon": 0,
6
+ "humidity": 72.0
7
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "season": 2025,
3
+ "events": [
4
+ {
5
+ "id": 378,
6
+ "startDateUtc": "2026-03-18T08:00:00+00:00",
7
+ "period": "AM",
8
+ "reward": 11.68,
9
+ "energySavingsWh": 20570,
10
+ "status": "Success",
11
+ "isPreseasonEvent": false,
12
+ "isCredited": true,
13
+ "isFlatRate": false,
14
+ "isInsufficientReduction": false
15
+ },
16
+ {
17
+ "id": 377,
18
+ "startDateUtc": "2026-03-03T09:00:00+00:00",
19
+ "period": "AM",
20
+ "reward": 17.95,
21
+ "energySavingsWh": 31620,
22
+ "status": "Success",
23
+ "isPreseasonEvent": false,
24
+ "isCredited": true,
25
+ "isFlatRate": false,
26
+ "isInsufficientReduction": false
27
+ },
28
+ {
29
+ "id": 373,
30
+ "startDateUtc": "2026-02-08T09:00:00+00:00",
31
+ "period": "AM",
32
+ "reward": 20.56,
33
+ "energySavingsWh": 36220,
34
+ "status": "Success",
35
+ "isPreseasonEvent": false,
36
+ "isCredited": true,
37
+ "isFlatRate": false,
38
+ "isInsufficientReduction": false
39
+ }
40
+ ],
41
+ "periodId": "Q0h8dXJuOmhpbG86Y3JtOjAwMDAwLWFiY2RlZjowfDIwMjUtMTEtMDFUMTM6MDE6MTcuODA4Nzc4NVo="
42
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "season": 2026,
3
+ "events": [],
4
+ "periodId": "Q0h8dXJuOmhpbG86Y3JtOjAwMDAwLWFiY2RlZjowfDIwMjUtMTEtMDFUMTM6MDE6MTcuODA4Nzc4NVo="
5
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "season": 2024,
3
+ "events": [
4
+ {
5
+ "id": 303,
6
+ "startDateUtc": "2025-03-03T09:00:00+00:00",
7
+ "period": "AM",
8
+ "reportId": 1000000,
9
+ "reward": 20.39,
10
+ "energySavingsWh": 37081.6845703125,
11
+ "status": "Success",
12
+ "isPreseasonEvent": false
13
+ },
14
+ {
15
+ "id": 302,
16
+ "startDateUtc": "2025-02-19T09:00:00+00:00",
17
+ "period": "AM",
18
+ "reportId": 1000001,
19
+ "reward": 21.85,
20
+ "energySavingsWh": 39730.3349609375,
21
+ "status": "Success",
22
+ "isPreseasonEvent": false
23
+ },
24
+ {
25
+ "id": 301,
26
+ "startDateUtc": "2025-02-18T09:00:00+00:00",
27
+ "period": "AM",
28
+ "reportId": 1000002,
29
+ "reward": 23.91,
30
+ "energySavingsWh": 43477.09521484375,
31
+ "status": "Success",
32
+ "isPreseasonEvent": false
33
+ }
34
+ ],
35
+ "periodId": "SGlsb3x1cm46aGlsbzpjcm06MDAwMDAtYWJjZGVmOjB8MjAyMi0wNi0yMlQxOTo1MDozOC4wMDAwMDAwWg=="
36
+ }
@@ -0,0 +1,27 @@
1
+ [
2
+ {
3
+ "season": 2026,
4
+ "ratePlan": "CH",
5
+ "periodId": "Q0h8dXJuOmhpbG86Y3JtOjAwMDAwLWFiY2RlZjowfDIwMjUtMTEtMDFUMTM6MDE6MTcuODA4Nzc4NVo="
6
+ },
7
+ {
8
+ "season": 2025,
9
+ "ratePlan": "CH",
10
+ "periodId": "Q0h8dXJuOmhpbG86Y3JtOjAwMDAwLWFiY2RlZjowfDIwMjUtMTEtMDFUMTM6MDE6MTcuODA4Nzc4NVo="
11
+ },
12
+ {
13
+ "season": 2024,
14
+ "ratePlan": "Hilo",
15
+ "periodId": "SGlsb3x1cm46aGlsbzpjcm06MDAwMDAtYWJjZGVmOjB8MjAyMi0wNi0yMlQxOTo1MDozOC4wMDAwMDAwWg=="
16
+ },
17
+ {
18
+ "season": 2023,
19
+ "ratePlan": "Hilo",
20
+ "periodId": "SGlsb3x1cm46aGlsbzpjcm06MDAwMDAtYWJjZGVmOjB8MjAyMi0wNi0yMlQxOTo1MDozOC4wMDAwMDAwWg=="
21
+ },
22
+ {
23
+ "season": 2022,
24
+ "ratePlan": "Hilo",
25
+ "periodId": "SGlsb3x1cm46aGlsbzpjcm06MDAwMDAtYWJjZGVmOjB8MjAyMi0wNi0yMlQxOTo1MDozOC4wMDAwMDAwWg=="
26
+ }
27
+ ]
@@ -1,6 +1,7 @@
1
1
  """Define the hilo package."""
2
2
 
3
3
  from pyhilo.api import API
4
+ from pyhilo.auth import AbstractAuth
4
5
  from pyhilo.const import UNMONITORED_DEVICES
5
6
  from pyhilo.device import HiloDevice
6
7
  from pyhilo.device.switch import Switch
@@ -12,6 +13,7 @@ from pyhilo.util import from_utc_timestamp, time_diff
12
13
 
13
14
  __all__ = [
14
15
  "API",
16
+ "AbstractAuth",
15
17
  "Devices",
16
18
  "HiloDevice",
17
19
  "Event",