python-hilo 2025.2.2__tar.gz → 2025.4.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.
- python_hilo-2025.4.1/PKG-INFO +125 -0
- python_hilo-2025.4.1/README.md +89 -0
- python_hilo-2025.4.1/pyhilo/__init__.py +27 -0
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyhilo/api.py +7 -9
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyhilo/const.py +9 -8
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyhilo/device/__init__.py +6 -3
- python_hilo-2025.4.1/pyhilo/device/climate.py +99 -0
- python_hilo-2025.4.1/pyhilo/device/graphql_value_mapper.py +481 -0
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyhilo/devices.py +12 -4
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyhilo/event.py +1 -0
- python_hilo-2025.4.1/pyhilo/graphql.py +614 -0
- python_hilo-2025.4.1/pyhilo/oauth2.py +51 -0
- python_hilo-2025.4.1/pyhilo/oauth2helper.py +65 -0
- python_hilo-2025.4.1/pyhilo/util/state.py +166 -0
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyhilo/websocket.py +2 -4
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyproject.toml +4 -3
- python_hilo-2025.2.2/PKG-INFO +0 -54
- python_hilo-2025.2.2/README.md +0 -18
- python_hilo-2025.2.2/pyhilo/__init__.py +0 -3
- python_hilo-2025.2.2/pyhilo/device/climate.py +0 -48
- python_hilo-2025.2.2/pyhilo/oauth2.py +0 -77
- python_hilo-2025.2.2/pyhilo/util/state.py +0 -116
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/LICENSE +0 -0
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyhilo/device/light.py +0 -0
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyhilo/device/sensor.py +0 -0
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyhilo/device/switch.py +0 -0
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyhilo/exceptions.py +0 -0
- {python_hilo-2025.2.2 → python_hilo-2025.4.1}/pyhilo/util/__init__.py +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: python-hilo
|
|
3
|
+
Version: 2025.4.1
|
|
4
|
+
Summary: A Python3, async interface to the Hilo API
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: David Vallee Delisle
|
|
7
|
+
Author-email: me@dvd.dev
|
|
8
|
+
Maintainer: David Vallee Delisle
|
|
9
|
+
Maintainer-email: me@dvd.dev
|
|
10
|
+
Requires-Python: >=3.9.0,<4.0.0
|
|
11
|
+
Classifier: Framework :: aiohttp
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
22
|
+
Classifier: Topic :: Home Automation
|
|
23
|
+
Requires-Dist: aiofiles (>=23.2.1)
|
|
24
|
+
Requires-Dist: aiohttp (>=3.8.0)
|
|
25
|
+
Requires-Dist: aiosignal (>=1.2.0)
|
|
26
|
+
Requires-Dist: async-timeout (>=4.0.0)
|
|
27
|
+
Requires-Dist: attrs (>=21.2.0)
|
|
28
|
+
Requires-Dist: backoff (>=1.11.1)
|
|
29
|
+
Requires-Dist: gql (>=3.5.2,<4.0.0)
|
|
30
|
+
Requires-Dist: python-dateutil (>=2.8.2)
|
|
31
|
+
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
32
|
+
Requires-Dist: voluptuous (>=0.13.1)
|
|
33
|
+
Requires-Dist: websockets (>=8.1,<16.0)
|
|
34
|
+
Project-URL: Repository, https://github.com/dvd-dev/python-hilo
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# python-hilo
|
|
38
|
+
|
|
39
|
+
[![GitHub Release][releases-shield]][releases]
|
|
40
|
+
[![License][license-shield]](LICENSE)
|
|
41
|
+
|
|
42
|
+
[![Build Status][build-shield]][build]
|
|
43
|
+
[![Open in Dev Containers][devcontainer-shield]][devcontainer]
|
|
44
|
+
|
|
45
|
+
`python-hilo` (aka `pyhilo`) is a Python 3.11, `asyncio`-driven interface to the unofficial
|
|
46
|
+
Hilo API from Hydro Quebec. This is meant to be integrated into Home Assistant but can also
|
|
47
|
+
be used as a standalone library.
|
|
48
|
+
|
|
49
|
+
Home assistant integration is available [here](https://github.com/dvd-dev/hilo)
|
|
50
|
+
|
|
51
|
+
## TODO
|
|
52
|
+
- Type everything: almost done, got a few "type: ignore" to fix
|
|
53
|
+
|
|
54
|
+
## Later?
|
|
55
|
+
- Full docstrings and doc generation
|
|
56
|
+
- Unit testing
|
|
57
|
+
- Functional testing
|
|
58
|
+
|
|
59
|
+
If anyone wants to contribute, feel free to submit a PR. If you'd like to sync up first, you can
|
|
60
|
+
fire me an email me@dvd.dev
|
|
61
|
+
|
|
62
|
+
## Setting up development environment
|
|
63
|
+
|
|
64
|
+
The easiest way to start, is by opening a CodeSpace here on GitHub, or by using
|
|
65
|
+
the [Dev Container][devcontainer] feature of Visual Studio Code.
|
|
66
|
+
|
|
67
|
+
[![Open in Dev Containers][devcontainer-shield]][devcontainer]
|
|
68
|
+
|
|
69
|
+
This Python project is fully managed using the [Poetry][poetry] dependency
|
|
70
|
+
manager. But also relies on the use of NodeJS for certain checks during
|
|
71
|
+
development.
|
|
72
|
+
|
|
73
|
+
You need at least:
|
|
74
|
+
|
|
75
|
+
- Python 3.11+
|
|
76
|
+
- [Poetry][poetry-install]
|
|
77
|
+
- NodeJS 20+ (including NPM)
|
|
78
|
+
|
|
79
|
+
To install all packages, including all development requirements:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install
|
|
83
|
+
poetry install
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
As this repository uses the [pre-commit][pre-commit] framework, all changes
|
|
87
|
+
are linted and tested with each commit. You can run all checks and tests
|
|
88
|
+
manually, using the following command:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
poetry run pre-commit run --all-files
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
To run just the Python tests:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
poetry run pytest
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Authors & contributors
|
|
101
|
+
|
|
102
|
+
The original setup of this repository is by [David Vallée Delisle][dvd-dev].
|
|
103
|
+
|
|
104
|
+
Credits to [@frenck][frenck] for the base container configuration.
|
|
105
|
+
The license of python-wled can be found in
|
|
106
|
+
[third_party/python-wled/LICENSE](third_party/python-wled/LICENSE).
|
|
107
|
+
|
|
108
|
+
For a full list of all authors and contributors,
|
|
109
|
+
check [the contributor's page][contributors].
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
[build-shield]: https://github.com/dvd-dev/python-hilo/actions/workflows/tests.yaml/badge.svg
|
|
114
|
+
[build]: https://github.com/dvd-dev/python-hilo/actions/workflows/tests.yaml
|
|
115
|
+
[releases-shield]: https://img.shields.io/github/release/dvd-dev/python-hilo.svg
|
|
116
|
+
[releases]: https://github.com/dvd-dev/python-hilo/releases
|
|
117
|
+
[license-shield]: https://img.shields.io/github/license/dvd-dev/python-hilo.svg
|
|
118
|
+
[devcontainer-shield]: https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode
|
|
119
|
+
[devcontainer]: https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/dvd-dev/python-hilo
|
|
120
|
+
[poetry-install]: https://python-poetry.org/docs/#installation
|
|
121
|
+
[poetry]: https://python-poetry.org
|
|
122
|
+
[pre-commit]: https://pre-commit.com/
|
|
123
|
+
[dvd-dev]: https://github.com/dvd-dev
|
|
124
|
+
[frenck]: https://github.com/frenck
|
|
125
|
+
[contributors]: https://github.com/dvd-dev/python-hilo/graphs/contributors
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# python-hilo
|
|
2
|
+
|
|
3
|
+
[![GitHub Release][releases-shield]][releases]
|
|
4
|
+
[![License][license-shield]](LICENSE)
|
|
5
|
+
|
|
6
|
+
[![Build Status][build-shield]][build]
|
|
7
|
+
[![Open in Dev Containers][devcontainer-shield]][devcontainer]
|
|
8
|
+
|
|
9
|
+
`python-hilo` (aka `pyhilo`) is a Python 3.11, `asyncio`-driven interface to the unofficial
|
|
10
|
+
Hilo API from Hydro Quebec. This is meant to be integrated into Home Assistant but can also
|
|
11
|
+
be used as a standalone library.
|
|
12
|
+
|
|
13
|
+
Home assistant integration is available [here](https://github.com/dvd-dev/hilo)
|
|
14
|
+
|
|
15
|
+
## TODO
|
|
16
|
+
- Type everything: almost done, got a few "type: ignore" to fix
|
|
17
|
+
|
|
18
|
+
## Later?
|
|
19
|
+
- Full docstrings and doc generation
|
|
20
|
+
- Unit testing
|
|
21
|
+
- Functional testing
|
|
22
|
+
|
|
23
|
+
If anyone wants to contribute, feel free to submit a PR. If you'd like to sync up first, you can
|
|
24
|
+
fire me an email me@dvd.dev
|
|
25
|
+
|
|
26
|
+
## Setting up development environment
|
|
27
|
+
|
|
28
|
+
The easiest way to start, is by opening a CodeSpace here on GitHub, or by using
|
|
29
|
+
the [Dev Container][devcontainer] feature of Visual Studio Code.
|
|
30
|
+
|
|
31
|
+
[![Open in Dev Containers][devcontainer-shield]][devcontainer]
|
|
32
|
+
|
|
33
|
+
This Python project is fully managed using the [Poetry][poetry] dependency
|
|
34
|
+
manager. But also relies on the use of NodeJS for certain checks during
|
|
35
|
+
development.
|
|
36
|
+
|
|
37
|
+
You need at least:
|
|
38
|
+
|
|
39
|
+
- Python 3.11+
|
|
40
|
+
- [Poetry][poetry-install]
|
|
41
|
+
- NodeJS 20+ (including NPM)
|
|
42
|
+
|
|
43
|
+
To install all packages, including all development requirements:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install
|
|
47
|
+
poetry install
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
As this repository uses the [pre-commit][pre-commit] framework, all changes
|
|
51
|
+
are linted and tested with each commit. You can run all checks and tests
|
|
52
|
+
manually, using the following command:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
poetry run pre-commit run --all-files
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
To run just the Python tests:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
poetry run pytest
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Authors & contributors
|
|
65
|
+
|
|
66
|
+
The original setup of this repository is by [David Vallée Delisle][dvd-dev].
|
|
67
|
+
|
|
68
|
+
Credits to [@frenck][frenck] for the base container configuration.
|
|
69
|
+
The license of python-wled can be found in
|
|
70
|
+
[third_party/python-wled/LICENSE](third_party/python-wled/LICENSE).
|
|
71
|
+
|
|
72
|
+
For a full list of all authors and contributors,
|
|
73
|
+
check [the contributor's page][contributors].
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
[build-shield]: https://github.com/dvd-dev/python-hilo/actions/workflows/tests.yaml/badge.svg
|
|
78
|
+
[build]: https://github.com/dvd-dev/python-hilo/actions/workflows/tests.yaml
|
|
79
|
+
[releases-shield]: https://img.shields.io/github/release/dvd-dev/python-hilo.svg
|
|
80
|
+
[releases]: https://github.com/dvd-dev/python-hilo/releases
|
|
81
|
+
[license-shield]: https://img.shields.io/github/license/dvd-dev/python-hilo.svg
|
|
82
|
+
[devcontainer-shield]: https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode
|
|
83
|
+
[devcontainer]: https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/dvd-dev/python-hilo
|
|
84
|
+
[poetry-install]: https://python-poetry.org/docs/#installation
|
|
85
|
+
[poetry]: https://python-poetry.org
|
|
86
|
+
[pre-commit]: https://pre-commit.com/
|
|
87
|
+
[dvd-dev]: https://github.com/dvd-dev
|
|
88
|
+
[frenck]: https://github.com/frenck
|
|
89
|
+
[contributors]: https://github.com/dvd-dev/python-hilo/graphs/contributors
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Define the hilo package."""
|
|
2
|
+
from pyhilo.api import API
|
|
3
|
+
from pyhilo.const import UNMONITORED_DEVICES
|
|
4
|
+
from pyhilo.device import HiloDevice
|
|
5
|
+
from pyhilo.device.switch import Switch
|
|
6
|
+
from pyhilo.devices import Devices
|
|
7
|
+
from pyhilo.event import Event
|
|
8
|
+
from pyhilo.exceptions import HiloError, InvalidCredentialsError, WebsocketError
|
|
9
|
+
from pyhilo.oauth2 import AuthCodeWithPKCEImplementation
|
|
10
|
+
from pyhilo.util import from_utc_timestamp, time_diff
|
|
11
|
+
from pyhilo.websocket import WebsocketEvent
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"API",
|
|
15
|
+
"Devices",
|
|
16
|
+
"HiloDevice",
|
|
17
|
+
"Event",
|
|
18
|
+
"HiloError",
|
|
19
|
+
"InvalidCredentialsError",
|
|
20
|
+
"WebsocketError",
|
|
21
|
+
"AuthCodeWithPKCEImplementation",
|
|
22
|
+
"from_utc_timestamp",
|
|
23
|
+
"time_diff",
|
|
24
|
+
"WebsocketEvent",
|
|
25
|
+
"UNMONITORED_DEVICES",
|
|
26
|
+
"Switch",
|
|
27
|
+
]
|
|
@@ -12,7 +12,6 @@ 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 import config_entry_oauth2_flow
|
|
16
15
|
|
|
17
16
|
from pyhilo.const import (
|
|
18
17
|
ANDROID_CLIENT_ENDPOINT,
|
|
@@ -67,7 +66,7 @@ class API:
|
|
|
67
66
|
self,
|
|
68
67
|
*,
|
|
69
68
|
session: ClientSession,
|
|
70
|
-
oauth_session
|
|
69
|
+
oauth_session,
|
|
71
70
|
request_retries: int = REQUEST_RETRY,
|
|
72
71
|
log_traces: bool = False,
|
|
73
72
|
) -> None:
|
|
@@ -98,7 +97,7 @@ class API:
|
|
|
98
97
|
cls,
|
|
99
98
|
*,
|
|
100
99
|
session: ClientSession,
|
|
101
|
-
oauth_session
|
|
100
|
+
oauth_session,
|
|
102
101
|
request_retries: int = REQUEST_RETRY,
|
|
103
102
|
log_traces: bool = False,
|
|
104
103
|
) -> API:
|
|
@@ -141,7 +140,7 @@ class API:
|
|
|
141
140
|
await self._oauth_session.async_ensure_token_valid()
|
|
142
141
|
|
|
143
142
|
access_token = str(self._oauth_session.token["access_token"])
|
|
144
|
-
LOG.debug(f"
|
|
143
|
+
LOG.debug(f"Websocket access token is {access_token}")
|
|
145
144
|
|
|
146
145
|
return str(self._oauth_session.token["access_token"])
|
|
147
146
|
|
|
@@ -239,9 +238,8 @@ class API:
|
|
|
239
238
|
kwargs["headers"]["authorization"] = f"Bearer {access_token}"
|
|
240
239
|
kwargs["headers"]["Host"] = host
|
|
241
240
|
|
|
242
|
-
# ic-dev21 trying Leicas suggestion
|
|
243
241
|
if endpoint.startswith(AUTOMATION_CHALLENGE_ENDPOINT):
|
|
244
|
-
# remove Ocp-Apim-Subscription-Key header to avoid 401 error
|
|
242
|
+
# remove Ocp-Apim-Subscription-Key header to avoid 401 error (Thanks Leicas)
|
|
245
243
|
kwargs["headers"].pop("Ocp-Apim-Subscription-Key", None)
|
|
246
244
|
kwargs["headers"]["authorization"] = f"Bearer {access_token}"
|
|
247
245
|
|
|
@@ -370,7 +368,7 @@ class API:
|
|
|
370
368
|
|
|
371
369
|
async def _async_post_init(self) -> None:
|
|
372
370
|
"""Perform some post-init actions."""
|
|
373
|
-
LOG.debug("Websocket
|
|
371
|
+
LOG.debug("Websocket _async_post_init running")
|
|
374
372
|
await self._get_fid()
|
|
375
373
|
await self._get_device_token()
|
|
376
374
|
|
|
@@ -494,11 +492,11 @@ class API:
|
|
|
494
492
|
},
|
|
495
493
|
)
|
|
496
494
|
|
|
497
|
-
async def
|
|
495
|
+
async def get_location_ids(self) -> tuple[int, str]:
|
|
498
496
|
url = f"{API_AUTOMATION_ENDPOINT}/Locations"
|
|
499
497
|
LOG.debug(f"LocationId URL is {url}")
|
|
500
498
|
req: list[dict[str, Any]] = await self.async_request("get", url)
|
|
501
|
-
return
|
|
499
|
+
return (req[0]["id"], req[0]["locationHiloId"])
|
|
502
500
|
|
|
503
501
|
async def get_devices(self, location_id: int) -> list[dict[str, Any]]:
|
|
504
502
|
"""Get list of all devices"""
|
|
@@ -3,12 +3,11 @@ import platform
|
|
|
3
3
|
from typing import Final
|
|
4
4
|
|
|
5
5
|
import aiohttp
|
|
6
|
-
import homeassistant.core
|
|
7
6
|
|
|
8
7
|
LOG: Final = logging.getLogger(__package__)
|
|
9
8
|
DEFAULT_STATE_FILE: Final = "hilo_state.yaml"
|
|
10
9
|
REQUEST_RETRY: Final = 9
|
|
11
|
-
PYHILO_VERSION: Final = "2025.
|
|
10
|
+
PYHILO_VERSION: Final = "2025.4.01"
|
|
12
11
|
# TODO: Find a way to keep previous line in sync with pyproject.toml automatically
|
|
13
12
|
|
|
14
13
|
CONTENT_TYPE_FORM: Final = "application/x-www-form-urlencoded"
|
|
@@ -46,7 +45,7 @@ AUTOMATION_CHALLENGE_ENDPOINT: Final = "/ChallengeHub"
|
|
|
46
45
|
|
|
47
46
|
|
|
48
47
|
# Request constants
|
|
49
|
-
DEFAULT_USER_AGENT: Final = f"PyHilo/{PYHILO_VERSION}
|
|
48
|
+
DEFAULT_USER_AGENT: Final = f"PyHilo/{PYHILO_VERSION} aiohttp/{aiohttp.__version__} Python/{platform.python_version()}"
|
|
50
49
|
|
|
51
50
|
|
|
52
51
|
# NOTE(dvd): Not sure how to get new ones so I'm using the ones from my emulator
|
|
@@ -196,7 +195,7 @@ HILO_DEVICE_TYPES: Final = {
|
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
HILO_UNIT_CONVERSION: Final = {
|
|
199
|
-
"
|
|
198
|
+
"Celsius": "°C",
|
|
200
199
|
"DB": "dB",
|
|
201
200
|
"Integer": "dB",
|
|
202
201
|
"Mbar": "mbar",
|
|
@@ -209,7 +208,7 @@ HILO_READING_TYPES: Final = {
|
|
|
209
208
|
"BatteryPercent": "Percentage",
|
|
210
209
|
"Co2": "PPM",
|
|
211
210
|
"ColorTemperature": "Integer",
|
|
212
|
-
"CurrentTemperature": "
|
|
211
|
+
"CurrentTemperature": "Celsius",
|
|
213
212
|
"Disconnected": "null",
|
|
214
213
|
"DrmsState": "OnOff",
|
|
215
214
|
"firmwareVersion": "null",
|
|
@@ -217,8 +216,8 @@ HILO_READING_TYPES: Final = {
|
|
|
217
216
|
"Hue": "Integer",
|
|
218
217
|
"Humidity": "Percentage",
|
|
219
218
|
"Intensity": "Percentage",
|
|
220
|
-
"MaxTempSetpoint": "
|
|
221
|
-
"MinTempSetpoint": "
|
|
219
|
+
"MaxTempSetpoint": "Celsius",
|
|
220
|
+
"MinTempSetpoint": "Celsius",
|
|
222
221
|
"Noise": "DB",
|
|
223
222
|
"onlineStatus": "null",
|
|
224
223
|
"OnOff": "OnOff",
|
|
@@ -226,7 +225,7 @@ HILO_READING_TYPES: Final = {
|
|
|
226
225
|
"Pressure": "Mbar",
|
|
227
226
|
"Saturation": "Integer",
|
|
228
227
|
"Status": "OnOff",
|
|
229
|
-
"TargetTemperature": "
|
|
228
|
+
"TargetTemperature": "Celsius",
|
|
230
229
|
"Unpaired": "null",
|
|
231
230
|
"WifiStatus": "Integer",
|
|
232
231
|
"zigBeePairingActivated": "OnOff",
|
|
@@ -269,3 +268,5 @@ UNMONITORED_DEVICES: Final = [
|
|
|
269
268
|
"43094",
|
|
270
269
|
"43100",
|
|
271
270
|
]
|
|
271
|
+
|
|
272
|
+
STATE_UNKNOWN: Final = "unknown"
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"""Define devices"""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
from dataclasses import dataclass, field
|
|
5
6
|
from datetime import datetime
|
|
6
7
|
from typing import TYPE_CHECKING, Any, Dict, Union, cast
|
|
7
8
|
|
|
8
|
-
from homeassistant.const import STATE_UNKNOWN
|
|
9
|
-
|
|
10
9
|
from pyhilo.const import (
|
|
11
10
|
HILO_DEVICE_ATTRIBUTES,
|
|
12
11
|
HILO_LIST_ATTRIBUTES,
|
|
@@ -16,6 +15,7 @@ from pyhilo.const import (
|
|
|
16
15
|
JASCO_MODELS,
|
|
17
16
|
JASCO_OUTLETS,
|
|
18
17
|
LOG,
|
|
18
|
+
STATE_UNKNOWN,
|
|
19
19
|
)
|
|
20
20
|
from pyhilo.util import camel_to_snake, from_utc_timestamp
|
|
21
21
|
|
|
@@ -32,10 +32,11 @@ def get_device_attributes() -> list[DeviceAttribute]:
|
|
|
32
32
|
|
|
33
33
|
class HiloDevice:
|
|
34
34
|
def __init__(
|
|
35
|
-
self, api: API, **kwargs: Dict[str,
|
|
35
|
+
self, api: API, **kwargs: Dict[str, str | int | Dict[Any, Any]]
|
|
36
36
|
) -> None:
|
|
37
37
|
self._api = api
|
|
38
38
|
self.id = 0
|
|
39
|
+
self.hilo_id: str = ""
|
|
39
40
|
self.location_id = 0
|
|
40
41
|
self.type = "Unknown"
|
|
41
42
|
self.name = "Unknown"
|
|
@@ -59,6 +60,7 @@ class HiloDevice:
|
|
|
59
60
|
value = val.get("value")
|
|
60
61
|
reading = {
|
|
61
62
|
"deviceId": self.id,
|
|
63
|
+
"hiloId": self.hilo_id,
|
|
62
64
|
"locationId": self.location_id,
|
|
63
65
|
"timeStampUTC": datetime.utcnow().isoformat(),
|
|
64
66
|
"value": value,
|
|
@@ -234,6 +236,7 @@ class DeviceReading:
|
|
|
234
236
|
self.id = 0
|
|
235
237
|
self.value: Union[int, bool, str] = 0
|
|
236
238
|
self.device_id = 0
|
|
239
|
+
self.hilo_id: str = ""
|
|
237
240
|
self.device_attribute: DeviceAttribute
|
|
238
241
|
self.__dict__.update({camel_to_snake(k): v for k, v in kwargs.items()})
|
|
239
242
|
self.unit_of_measurement = (
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Climate object."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any, cast
|
|
5
|
+
|
|
6
|
+
from pyhilo import API
|
|
7
|
+
from pyhilo.const import LOG
|
|
8
|
+
from pyhilo.device import HiloDevice
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Climate(HiloDevice):
|
|
12
|
+
"""
|
|
13
|
+
Represents a climate device within the Hilo ecosystem.
|
|
14
|
+
|
|
15
|
+
This class provides methods to interact with and control climate-related
|
|
16
|
+
devices such as thermostats.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self, api: API, **kwargs: dict[str, str | int | dict[Any, Any]]
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Initialize the Climate object.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
api: The Hilo API instance.
|
|
26
|
+
**kwargs: Keyword arguments containing device data.
|
|
27
|
+
"""
|
|
28
|
+
super().__init__(api, **kwargs)
|
|
29
|
+
LOG.debug("Setting up Climate device: %s", self.name)
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def current_temperature(self) -> float:
|
|
33
|
+
"""
|
|
34
|
+
Gets the current temperature reported by the device.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
float: The current temperature.
|
|
38
|
+
"""
|
|
39
|
+
return cast(float, self.get_value("current_temperature", 0))
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def target_temperature(self) -> float:
|
|
43
|
+
"""
|
|
44
|
+
Gets the target temperature set for the device.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
float: The target temperature.
|
|
48
|
+
"""
|
|
49
|
+
return cast(float, self.get_value("target_temperature", 0))
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def max_temp(self) -> float:
|
|
53
|
+
"""
|
|
54
|
+
Gets the maximum temperature setpoint allowed for the device.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
float: The maximum temperature. Defaults to 36.0 if not defined.
|
|
58
|
+
"""
|
|
59
|
+
value = self.get_value("max_temp_setpoint", 0)
|
|
60
|
+
|
|
61
|
+
if value is None or value == 0:
|
|
62
|
+
return 36.0
|
|
63
|
+
return float(value)
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def min_temp(self) -> float:
|
|
67
|
+
"""
|
|
68
|
+
Gets the minimum temperature setpoint allowed for the device.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
float: The minimum temperature. Defaults to 5.0 if not defined.
|
|
72
|
+
"""
|
|
73
|
+
value = self.get_value("min_temp_setpoint", 0)
|
|
74
|
+
|
|
75
|
+
if value is None or value == 0:
|
|
76
|
+
return 5.0
|
|
77
|
+
return float(value)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def hvac_action(self) -> str:
|
|
81
|
+
"""
|
|
82
|
+
Gets the current HVAC action of the device.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
str: 'heating' if heating is active, 'idle' otherwise.
|
|
86
|
+
"""
|
|
87
|
+
attr = self.get_value("heating", 0)
|
|
88
|
+
return "heating" if attr > 0 else "idle"
|
|
89
|
+
|
|
90
|
+
async def async_set_temperature(self, temperature: float) -> None:
|
|
91
|
+
"""
|
|
92
|
+
Sets the target temperature of the device.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
temperature: The desired target temperature.
|
|
96
|
+
"""
|
|
97
|
+
if temperature != self.target_temperature:
|
|
98
|
+
LOG.info("%s Setting temperature to %s", self._tag, temperature)
|
|
99
|
+
await self.set_attribute("target_temperature", str(temperature))
|