pyfenixcontrol 0.1.0__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.
- pyfenixcontrol-0.1.0/.gitignore +12 -0
- pyfenixcontrol-0.1.0/CHANGELOG.md +35 -0
- pyfenixcontrol-0.1.0/LICENSE +21 -0
- pyfenixcontrol-0.1.0/PKG-INFO +145 -0
- pyfenixcontrol-0.1.0/README.md +119 -0
- pyfenixcontrol-0.1.0/pyproject.toml +87 -0
- pyfenixcontrol-0.1.0/src/pyfenixcontrol/__init__.py +46 -0
- pyfenixcontrol-0.1.0/src/pyfenixcontrol/auth.py +372 -0
- pyfenixcontrol-0.1.0/src/pyfenixcontrol/client.py +356 -0
- pyfenixcontrol-0.1.0/src/pyfenixcontrol/const.py +75 -0
- pyfenixcontrol-0.1.0/src/pyfenixcontrol/exceptions.py +27 -0
- pyfenixcontrol-0.1.0/src/pyfenixcontrol/models.py +230 -0
- pyfenixcontrol-0.1.0/src/pyfenixcontrol/py.typed +0 -0
- pyfenixcontrol-0.1.0/tests/__init__.py +1 -0
- pyfenixcontrol-0.1.0/tests/conftest.py +156 -0
- pyfenixcontrol-0.1.0/tests/test_auth.py +328 -0
- pyfenixcontrol-0.1.0/tests/test_client.py +416 -0
- pyfenixcontrol-0.1.0/tests/test_models.py +167 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] - 2026-10-15
|
|
8
|
+
|
|
9
|
+
First release, extracted from the `api.py` module of the
|
|
10
|
+
[`fenix_tft`](https://github.com/baracudaz/fenix_tft) Home Assistant integration
|
|
11
|
+
(v1.3.8).
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- `FenixControlClient` with typed models (`Installation`, `Room`, `Device`,
|
|
15
|
+
`EnergyMetric`) and enums (`PresetMode`, `HvacState`, `HolidayMode`,
|
|
16
|
+
`EnergyPeriod`).
|
|
17
|
+
- Pluggable authentication via `AbstractAuth`; `PasswordAuth` implements the
|
|
18
|
+
existing e-mail/password OAuth2 PKCE login.
|
|
19
|
+
- Exception hierarchy separating auth, connection and response errors.
|
|
20
|
+
|
|
21
|
+
### Changed compared to the integration's `api.py`
|
|
22
|
+
- No dependency on Home Assistant or BeautifulSoup (login form parsed with the
|
|
23
|
+
standard library).
|
|
24
|
+
- Network errors and timeouts during login raise `FenixControlConnectionError`
|
|
25
|
+
instead of an auth error, so a network outage no longer looks like invalid
|
|
26
|
+
credentials.
|
|
27
|
+
- Invalid credentials are reported explicitly instead of a generic login failure.
|
|
28
|
+
- Concurrent requests share one login/refresh (asyncio lock) instead of a
|
|
29
|
+
re-entrancy flag that let parallel callers proceed without a token.
|
|
30
|
+
- Identity-server cookies are cleared before a full login, so a cached identity
|
|
31
|
+
session cannot authenticate a different account sharing the HTTP session.
|
|
32
|
+
- PUT requests now use the request timeout (previously unbounded).
|
|
33
|
+
- API requests no longer follow HTTP redirects.
|
|
34
|
+
- Energy queries take explicit `start`/`end` instead of "today in HA's time zone";
|
|
35
|
+
the daily total and the "user connected" update trigger are left to the caller.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Stanislav Likavčan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pyfenixcontrol
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Asynchronous Python client for the FENIX Control cloud API (FENIX TFT WiFi thermostats).
|
|
5
|
+
Project-URL: Homepage, https://github.com/baracudaz/pyfenixcontrol
|
|
6
|
+
Project-URL: Source, https://github.com/baracudaz/pyfenixcontrol
|
|
7
|
+
Project-URL: Issues, https://github.com/baracudaz/pyfenixcontrol/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/baracudaz/pyfenixcontrol/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Stanislav Likavčan <likavcan@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: asyncio,fenix,fenix control,home assistant,tft,thermostat
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Framework :: AsyncIO
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Home Automation
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# pyfenixcontrol
|
|
28
|
+
|
|
29
|
+
Asynchronous Python client for the **FENIX Control** cloud, used by FENIX TFT WiFi
|
|
30
|
+
thermostats. It powers the FENIX integration for [Home Assistant](https://www.home-assistant.io/),
|
|
31
|
+
but has no Home Assistant dependency and can be used from any asyncio code.
|
|
32
|
+
|
|
33
|
+
- Fully typed (`py.typed`), `asyncio` + `aiohttp`, Python 3.12+
|
|
34
|
+
- Read thermostats, rooms and installations; set target temperature and operating mode
|
|
35
|
+
- Installation-wide holiday schedules
|
|
36
|
+
- Room energy consumption at hourly … yearly aggregation
|
|
37
|
+
- Pluggable authentication: bundled e-mail/password login, or bring your own OAuth2 tokens
|
|
38
|
+
- Automatic token refresh, single retry on HTTP 401, backoff on HTTP 5xx
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install pyfenixcontrol
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick start
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
import asyncio
|
|
50
|
+
|
|
51
|
+
import aiohttp
|
|
52
|
+
|
|
53
|
+
from pyfenixcontrol import FenixControlClient, PasswordAuth, PresetMode
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def main() -> None:
|
|
57
|
+
async with aiohttp.ClientSession() as session:
|
|
58
|
+
auth = PasswordAuth(session, "you@example.com", "password")
|
|
59
|
+
client = FenixControlClient(auth)
|
|
60
|
+
|
|
61
|
+
for device in await client.get_devices():
|
|
62
|
+
print(device.name, device.room_temperature, device.target_temperature)
|
|
63
|
+
|
|
64
|
+
await client.set_target_temperature(device.id, 21.5)
|
|
65
|
+
await client.set_preset_mode(device.id, PresetMode.PROGRAM)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
asyncio.run(main())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Authentication
|
|
72
|
+
|
|
73
|
+
The client never sees credentials. It asks an `AbstractAuth` for a bearer token:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from pyfenixcontrol import AbstractAuth
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class MyAuth(AbstractAuth):
|
|
80
|
+
async def async_get_access_token(self) -> str:
|
|
81
|
+
return await my_token_store.get_valid_token()
|
|
82
|
+
|
|
83
|
+
async def async_invalidate_access_token(self) -> None:
|
|
84
|
+
# Called after an HTTP 401; optional.
|
|
85
|
+
await my_token_store.mark_expired()
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`PasswordAuth` performs the OAuth2 authorization-code + PKCE login used by the
|
|
89
|
+
FENIX Control app and keeps tokens in memory. `await auth.async_login()` validates
|
|
90
|
+
credentials up front (e.g. in a setup wizard).
|
|
91
|
+
|
|
92
|
+
`PasswordAuth` needs an `aiohttp` session with a working cookie jar, because the
|
|
93
|
+
identity server tracks its login form with cookies. Cookies of the identity host
|
|
94
|
+
are cleared before each full login.
|
|
95
|
+
|
|
96
|
+
## API overview
|
|
97
|
+
|
|
98
|
+
| Method | Description |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `get_user_id()` | Account subject id (cached on the client) |
|
|
101
|
+
| `get_installations()` | Installations (homes) with their rooms and device ids |
|
|
102
|
+
| `get_devices(installations=None)` | `Device` state of every thermostat |
|
|
103
|
+
| `get_device_properties(device_id)` | Raw device twin properties |
|
|
104
|
+
| `trigger_installation_update(installation_id)` | Ask thermostats to report fresh values |
|
|
105
|
+
| `set_target_temperature(device_id, celsius)` | Set target °C (switches to manual) |
|
|
106
|
+
| `set_preset_mode(device_id, mode)` | `PresetMode.OFF / PROGRAM / MANUAL / DEFROST / BOOST / HOLIDAYS` |
|
|
107
|
+
| `set_holiday_schedule(installation_id, start, end, mode)` | Holiday for the whole installation |
|
|
108
|
+
| `cancel_holiday_schedule(installation_id)` | Remove the holiday schedule |
|
|
109
|
+
| `get_room_energy(installation_id, room_id, start, end, period)` | `EnergyMetric` buckets in Wh |
|
|
110
|
+
|
|
111
|
+
### Errors
|
|
112
|
+
|
|
113
|
+
| Exception | Meaning |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `FenixControlAuthError` | Credentials or tokens rejected – user action needed |
|
|
116
|
+
| `FenixControlConnectionError` | Network error or timeout – retry later |
|
|
117
|
+
| `FenixControlResponseError` | Unexpected status or body (`.status` holds the HTTP code) |
|
|
118
|
+
|
|
119
|
+
All inherit from `FenixControlError`.
|
|
120
|
+
|
|
121
|
+
### Units and conventions
|
|
122
|
+
|
|
123
|
+
- Temperatures are °C. The API transports °F × `divFactor`;
|
|
124
|
+
`decode_temperature()` / `encode_temperature()` convert.
|
|
125
|
+
- Holiday `start`/`end` are wall-clock times of the installation (no time zone
|
|
126
|
+
conversion). Energy queries convert to UTC; naive datetimes are taken as UTC.
|
|
127
|
+
- Every model keeps the original payload in `.raw` for fields not yet modelled.
|
|
128
|
+
|
|
129
|
+
## Development
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
uv sync
|
|
133
|
+
uv run pytest --cov
|
|
134
|
+
uv run ruff check . && uv run ruff format --check . && uv run mypy
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Tests run against a local fake of the cloud and identity server; no account is needed.
|
|
138
|
+
|
|
139
|
+
Releases: bump `__version__`, update `CHANGELOG.md`, publish a GitHub release
|
|
140
|
+
tagged `vX.Y.Z`. The release workflow publishes to PyPI via trusted publishing.
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT – see [LICENSE](LICENSE). Not an official product of FENIX Trading s.r.o.
|
|
145
|
+
unless stated otherwise in the project metadata.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# pyfenixcontrol
|
|
2
|
+
|
|
3
|
+
Asynchronous Python client for the **FENIX Control** cloud, used by FENIX TFT WiFi
|
|
4
|
+
thermostats. It powers the FENIX integration for [Home Assistant](https://www.home-assistant.io/),
|
|
5
|
+
but has no Home Assistant dependency and can be used from any asyncio code.
|
|
6
|
+
|
|
7
|
+
- Fully typed (`py.typed`), `asyncio` + `aiohttp`, Python 3.12+
|
|
8
|
+
- Read thermostats, rooms and installations; set target temperature and operating mode
|
|
9
|
+
- Installation-wide holiday schedules
|
|
10
|
+
- Room energy consumption at hourly … yearly aggregation
|
|
11
|
+
- Pluggable authentication: bundled e-mail/password login, or bring your own OAuth2 tokens
|
|
12
|
+
- Automatic token refresh, single retry on HTTP 401, backoff on HTTP 5xx
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install pyfenixcontrol
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
import asyncio
|
|
24
|
+
|
|
25
|
+
import aiohttp
|
|
26
|
+
|
|
27
|
+
from pyfenixcontrol import FenixControlClient, PasswordAuth, PresetMode
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def main() -> None:
|
|
31
|
+
async with aiohttp.ClientSession() as session:
|
|
32
|
+
auth = PasswordAuth(session, "you@example.com", "password")
|
|
33
|
+
client = FenixControlClient(auth)
|
|
34
|
+
|
|
35
|
+
for device in await client.get_devices():
|
|
36
|
+
print(device.name, device.room_temperature, device.target_temperature)
|
|
37
|
+
|
|
38
|
+
await client.set_target_temperature(device.id, 21.5)
|
|
39
|
+
await client.set_preset_mode(device.id, PresetMode.PROGRAM)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
asyncio.run(main())
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Authentication
|
|
46
|
+
|
|
47
|
+
The client never sees credentials. It asks an `AbstractAuth` for a bearer token:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from pyfenixcontrol import AbstractAuth
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class MyAuth(AbstractAuth):
|
|
54
|
+
async def async_get_access_token(self) -> str:
|
|
55
|
+
return await my_token_store.get_valid_token()
|
|
56
|
+
|
|
57
|
+
async def async_invalidate_access_token(self) -> None:
|
|
58
|
+
# Called after an HTTP 401; optional.
|
|
59
|
+
await my_token_store.mark_expired()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`PasswordAuth` performs the OAuth2 authorization-code + PKCE login used by the
|
|
63
|
+
FENIX Control app and keeps tokens in memory. `await auth.async_login()` validates
|
|
64
|
+
credentials up front (e.g. in a setup wizard).
|
|
65
|
+
|
|
66
|
+
`PasswordAuth` needs an `aiohttp` session with a working cookie jar, because the
|
|
67
|
+
identity server tracks its login form with cookies. Cookies of the identity host
|
|
68
|
+
are cleared before each full login.
|
|
69
|
+
|
|
70
|
+
## API overview
|
|
71
|
+
|
|
72
|
+
| Method | Description |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `get_user_id()` | Account subject id (cached on the client) |
|
|
75
|
+
| `get_installations()` | Installations (homes) with their rooms and device ids |
|
|
76
|
+
| `get_devices(installations=None)` | `Device` state of every thermostat |
|
|
77
|
+
| `get_device_properties(device_id)` | Raw device twin properties |
|
|
78
|
+
| `trigger_installation_update(installation_id)` | Ask thermostats to report fresh values |
|
|
79
|
+
| `set_target_temperature(device_id, celsius)` | Set target °C (switches to manual) |
|
|
80
|
+
| `set_preset_mode(device_id, mode)` | `PresetMode.OFF / PROGRAM / MANUAL / DEFROST / BOOST / HOLIDAYS` |
|
|
81
|
+
| `set_holiday_schedule(installation_id, start, end, mode)` | Holiday for the whole installation |
|
|
82
|
+
| `cancel_holiday_schedule(installation_id)` | Remove the holiday schedule |
|
|
83
|
+
| `get_room_energy(installation_id, room_id, start, end, period)` | `EnergyMetric` buckets in Wh |
|
|
84
|
+
|
|
85
|
+
### Errors
|
|
86
|
+
|
|
87
|
+
| Exception | Meaning |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `FenixControlAuthError` | Credentials or tokens rejected – user action needed |
|
|
90
|
+
| `FenixControlConnectionError` | Network error or timeout – retry later |
|
|
91
|
+
| `FenixControlResponseError` | Unexpected status or body (`.status` holds the HTTP code) |
|
|
92
|
+
|
|
93
|
+
All inherit from `FenixControlError`.
|
|
94
|
+
|
|
95
|
+
### Units and conventions
|
|
96
|
+
|
|
97
|
+
- Temperatures are °C. The API transports °F × `divFactor`;
|
|
98
|
+
`decode_temperature()` / `encode_temperature()` convert.
|
|
99
|
+
- Holiday `start`/`end` are wall-clock times of the installation (no time zone
|
|
100
|
+
conversion). Energy queries convert to UTC; naive datetimes are taken as UTC.
|
|
101
|
+
- Every model keeps the original payload in `.raw` for fields not yet modelled.
|
|
102
|
+
|
|
103
|
+
## Development
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
uv sync
|
|
107
|
+
uv run pytest --cov
|
|
108
|
+
uv run ruff check . && uv run ruff format --check . && uv run mypy
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Tests run against a local fake of the cloud and identity server; no account is needed.
|
|
112
|
+
|
|
113
|
+
Releases: bump `__version__`, update `CHANGELOG.md`, publish a GitHub release
|
|
114
|
+
tagged `vX.Y.Z`. The release workflow publishes to PyPI via trusted publishing.
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT – see [LICENSE](LICENSE). Not an official product of FENIX Trading s.r.o.
|
|
119
|
+
unless stated otherwise in the project metadata.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyfenixcontrol"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Asynchronous Python client for the FENIX Control cloud API (FENIX TFT WiFi thermostats)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.12"
|
|
13
|
+
authors = [{ name = "Stanislav Likavčan", email = "likavcan@gmail.com" }]
|
|
14
|
+
keywords = ["fenix", "fenix control", "tft", "thermostat", "home assistant", "asyncio"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Framework :: AsyncIO",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
|
+
"Topic :: Home Automation",
|
|
25
|
+
"Typing :: Typed",
|
|
26
|
+
]
|
|
27
|
+
dependencies = ["aiohttp>=3.9.0"]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/baracudaz/pyfenixcontrol"
|
|
31
|
+
Source = "https://github.com/baracudaz/pyfenixcontrol"
|
|
32
|
+
Issues = "https://github.com/baracudaz/pyfenixcontrol/issues"
|
|
33
|
+
Changelog = "https://github.com/baracudaz/pyfenixcontrol/blob/main/CHANGELOG.md"
|
|
34
|
+
|
|
35
|
+
[dependency-groups]
|
|
36
|
+
dev = [
|
|
37
|
+
"mypy>=1.11",
|
|
38
|
+
"pytest>=8.0",
|
|
39
|
+
"pytest-asyncio>=0.24",
|
|
40
|
+
"pytest-cov>=5.0",
|
|
41
|
+
"ruff>=0.6",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.version]
|
|
45
|
+
path = "src/pyfenixcontrol/__init__.py"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.sdist]
|
|
48
|
+
include = ["src/pyfenixcontrol", "tests", "README.md", "CHANGELOG.md", "LICENSE"]
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
asyncio_mode = "auto"
|
|
52
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
53
|
+
testpaths = ["tests"]
|
|
54
|
+
addopts = "--strict-markers"
|
|
55
|
+
|
|
56
|
+
[tool.coverage.run]
|
|
57
|
+
source = ["pyfenixcontrol"]
|
|
58
|
+
branch = true
|
|
59
|
+
|
|
60
|
+
[tool.coverage.report]
|
|
61
|
+
show_missing = true
|
|
62
|
+
exclude_also = ["if TYPE_CHECKING:", "@abstractmethod", "raise NotImplementedError"]
|
|
63
|
+
|
|
64
|
+
[tool.mypy]
|
|
65
|
+
python_version = "3.12"
|
|
66
|
+
strict = true
|
|
67
|
+
warn_unreachable = true
|
|
68
|
+
files = ["src", "tests"]
|
|
69
|
+
|
|
70
|
+
[tool.ruff]
|
|
71
|
+
target-version = "py312"
|
|
72
|
+
line-length = 88
|
|
73
|
+
|
|
74
|
+
[tool.ruff.lint]
|
|
75
|
+
select = ["ALL"]
|
|
76
|
+
ignore = [
|
|
77
|
+
"COM812", # conflicts with formatter
|
|
78
|
+
"D203", # incompatible with D211
|
|
79
|
+
"D213", # incompatible with D212
|
|
80
|
+
"PLR0913", # API methods legitimately take many arguments
|
|
81
|
+
"CPY001", # license is declared once in LICENSE
|
|
82
|
+
"ANN401", # JSON payloads are genuinely untyped at the API boundary
|
|
83
|
+
"B027", # AbstractAuth.async_invalidate_access_token is an optional hook
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
[tool.ruff.lint.per-file-ignores]
|
|
87
|
+
"tests/**" = ["S101", "S105", "PLR2004", "SLF001", "D", "FBT001", "DTZ001", "TC001", "TC003"]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Asynchronous Python client for the FENIX Control cloud API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .auth import AbstractAuth, PasswordAuth
|
|
6
|
+
from .client import FenixControlClient
|
|
7
|
+
from .const import EnergyPeriod, HolidayMode, HvacState, PresetMode
|
|
8
|
+
from .exceptions import (
|
|
9
|
+
FenixControlAuthError,
|
|
10
|
+
FenixControlConnectionError,
|
|
11
|
+
FenixControlError,
|
|
12
|
+
FenixControlResponseError,
|
|
13
|
+
)
|
|
14
|
+
from .models import (
|
|
15
|
+
Device,
|
|
16
|
+
EnergyMetric,
|
|
17
|
+
Installation,
|
|
18
|
+
Room,
|
|
19
|
+
decode_temperature,
|
|
20
|
+
encode_temperature,
|
|
21
|
+
parse_holiday_date,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__version__ = "0.1.0"
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"AbstractAuth",
|
|
28
|
+
"Device",
|
|
29
|
+
"EnergyMetric",
|
|
30
|
+
"EnergyPeriod",
|
|
31
|
+
"FenixControlAuthError",
|
|
32
|
+
"FenixControlClient",
|
|
33
|
+
"FenixControlConnectionError",
|
|
34
|
+
"FenixControlError",
|
|
35
|
+
"FenixControlResponseError",
|
|
36
|
+
"HolidayMode",
|
|
37
|
+
"HvacState",
|
|
38
|
+
"Installation",
|
|
39
|
+
"PasswordAuth",
|
|
40
|
+
"PresetMode",
|
|
41
|
+
"Room",
|
|
42
|
+
"__version__",
|
|
43
|
+
"decode_temperature",
|
|
44
|
+
"encode_temperature",
|
|
45
|
+
"parse_holiday_date",
|
|
46
|
+
]
|