open-polaris-local-api 1.2.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.
- open_polaris_local_api-1.2.1/LICENSE +21 -0
- open_polaris_local_api-1.2.1/PKG-INFO +549 -0
- open_polaris_local_api-1.2.1/README.md +508 -0
- open_polaris_local_api-1.2.1/open_polaris_local_api/__init__.py +5 -0
- open_polaris_local_api-1.2.1/open_polaris_local_api/client.py +378 -0
- open_polaris_local_api-1.2.1/open_polaris_local_api/models.py +265 -0
- open_polaris_local_api-1.2.1/open_polaris_local_api/polaris_auto_discovery.py +174 -0
- open_polaris_local_api-1.2.1/open_polaris_local_api.egg-info/PKG-INFO +549 -0
- open_polaris_local_api-1.2.1/open_polaris_local_api.egg-info/SOURCES.txt +14 -0
- open_polaris_local_api-1.2.1/open_polaris_local_api.egg-info/dependency_links.txt +1 -0
- open_polaris_local_api-1.2.1/open_polaris_local_api.egg-info/top_level.txt +1 -0
- open_polaris_local_api-1.2.1/pyproject.toml +28 -0
- open_polaris_local_api-1.2.1/setup.cfg +4 -0
- open_polaris_local_api-1.2.1/tests/test_models.py +412 -0
- open_polaris_local_api-1.2.1/tests/test_polaris_auto_discovery.py +208 -0
- open_polaris_local_api-1.2.1/tests/test_polaris_client.py +483 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Luca
|
|
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,549 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: open-polaris-local-api
|
|
3
|
+
Version: 1.2.1
|
|
4
|
+
Summary: Async Python library for Tecnosystemi Polaris 5X HVAC Control Unit IoT devices
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 Luca
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Project-URL: Homepage, https://github.com/VoidElle/open-polaris-local-api
|
|
28
|
+
Project-URL: Source, https://github.com/VoidElle/open-polaris-local-api
|
|
29
|
+
Project-URL: Bug Tracker, https://github.com/VoidElle/open-polaris-local-api/issues
|
|
30
|
+
Keywords: polaris,hvac,iot,asyncio,home-assistant
|
|
31
|
+
Classifier: Programming Language :: Python :: 3
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Framework :: AsyncIO
|
|
37
|
+
Requires-Python: >=3.11
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
License-File: LICENSE
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# ๐ก๏ธ Open Polaris Local API
|
|
43
|
+
|
|
44
|
+
> *Asynchronous Python library for Tecnosystemi Polaris 5X HVAC Control Unit devices*
|
|
45
|
+
|
|
46
|
+
[](https://www.python.org/downloads/)
|
|
47
|
+
[](LICENSE)
|
|
48
|
+
[](https://github.com/VoidElle/open-polaris-local-api/releases)
|
|
49
|
+
|
|
50
|
+
**[Features](#-features) โข [Installation](#-installation) โข [Quick Start](#-quick-start) โข [Documentation](#-documentation) โข [Examples](#-examples) โข [Scripts](#๏ธ-scripts) โข [Testing](#-testing)**
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## โจ Features
|
|
55
|
+
|
|
56
|
+
### ๐ **Performance**
|
|
57
|
+
- Built with **asyncio** for non-blocking operations
|
|
58
|
+
- Efficient TCP communication โ short-lived connections per command (mirrors official app)
|
|
59
|
+
- **Automatic retry** on timeout or transient failure
|
|
60
|
+
- Compact `stato_r` polling with transparent fallback to full `stato`
|
|
61
|
+
|
|
62
|
+
### ๐ **Reliability**
|
|
63
|
+
- **Auto-retry** with configurable attempts and delay
|
|
64
|
+
- Graceful fallback: `stato_r` โ `stato` on older firmware
|
|
65
|
+
- Robust error decoding via bitmask for both CU and zone errors
|
|
66
|
+
|
|
67
|
+
### ๐ฏ **Developer Friendly**
|
|
68
|
+
- Type-safe dataclasses โ `PolarisDevice` and `PolarisZone`
|
|
69
|
+
- Async context manager support
|
|
70
|
+
- Comprehensive logging with optional verbose mode
|
|
71
|
+
- Supports both local TCP (snake_case) and cloud API (PascalCase) response formats
|
|
72
|
+
|
|
73
|
+
### ๐ **Auto-Discovery**
|
|
74
|
+
- **Subnet scan** โ finds all Polaris devices on your LAN without knowing their IPs
|
|
75
|
+
- Concurrent TCP probing with configurable timeout and concurrency
|
|
76
|
+
- Identifies devices by validating the Polaris protocol response
|
|
77
|
+
|
|
78
|
+
### ๐๏ธ **Full Control**
|
|
79
|
+
- CU-level: power, cooling mode, operating mode
|
|
80
|
+
- Zone-level: temperature setpoint, on/off, chrono mode, fan speed, shutter
|
|
81
|
+
- Human-readable error decoding (bitmask โ error string list)
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## ๐ฆ Installation
|
|
86
|
+
|
|
87
|
+
### pip (recommended)
|
|
88
|
+
|
|
89
|
+
Install directly from a GitHub tag:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install "open-polaris-local-api @ git+https://github.com/VoidElle/open-polaris-local-api.git@v1.2.1"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Home Assistant integration
|
|
96
|
+
|
|
97
|
+
Add to your integration's `manifest.json`:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
"requirements": [
|
|
101
|
+
"open-polaris-local-api @ git+https://github.com/VoidElle/open-polaris-local-api.git@v1.2.1"
|
|
102
|
+
]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Manual
|
|
106
|
+
|
|
107
|
+
1. Clone this repository into your project
|
|
108
|
+
2. Ensure the `open_polaris_local_api/` folder is on your Python path
|
|
109
|
+
3. Import with `from open_polaris_local_api import PolarisLocalClient`
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## ๐ Quick Start
|
|
114
|
+
|
|
115
|
+
### Auto-Discovery
|
|
116
|
+
|
|
117
|
+
Find all Polaris devices on your network without knowing their IPs:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
import asyncio
|
|
121
|
+
from open_polaris_local_api import PolarisAutoDiscovery
|
|
122
|
+
|
|
123
|
+
async def main():
|
|
124
|
+
ips = await PolarisAutoDiscovery.discover(pin="1234", subnet="192.168.1.0/24")
|
|
125
|
+
print(f"Found {len(ips)} device(s): {ips}")
|
|
126
|
+
# ["192.168.1.42"]
|
|
127
|
+
|
|
128
|
+
asyncio.run(main())
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Single Device
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import asyncio
|
|
135
|
+
from open_polaris_local_api import PolarisLocalClient
|
|
136
|
+
|
|
137
|
+
async def main():
|
|
138
|
+
client = PolarisLocalClient(
|
|
139
|
+
ip="192.168.1.100",
|
|
140
|
+
pin="1234",
|
|
141
|
+
device_id="living_room",
|
|
142
|
+
verbose=True,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
async with client:
|
|
146
|
+
device, zones = await client.async_update()
|
|
147
|
+
print(f"โ {device.name} โ {device.cooling_mode_name}, on={device.is_on}")
|
|
148
|
+
|
|
149
|
+
for zone in zones:
|
|
150
|
+
print(f" Zone {zone.zone_id} '{zone.name}': {zone.current_temp}ยฐC โ {zone.set_temp}ยฐC")
|
|
151
|
+
|
|
152
|
+
# Turn on and set heating
|
|
153
|
+
await client.set_heating_mode()
|
|
154
|
+
await client.set_zone_temp(zones[0], 21.0)
|
|
155
|
+
|
|
156
|
+
if __name__ == "__main__":
|
|
157
|
+
asyncio.run(main())
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Zone Control
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
async def control_zones():
|
|
164
|
+
async with PolarisLocalClient(ip="192.168.1.100", pin="1234") as client:
|
|
165
|
+
_, zones = await client.async_update()
|
|
166
|
+
|
|
167
|
+
# Turn off a zone
|
|
168
|
+
await client.turn_zone_off(zones[0])
|
|
169
|
+
|
|
170
|
+
# Set temperature on all zones
|
|
171
|
+
for zone in zones:
|
|
172
|
+
await client.set_zone_temp(zone, 20.0)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### CU-Level Control
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
async def control_cu():
|
|
179
|
+
async with PolarisLocalClient(ip="192.168.1.100", pin="1234") as client:
|
|
180
|
+
# Switch to cooling mode (raffrescamento)
|
|
181
|
+
await client.set_cooling_mode(1)
|
|
182
|
+
|
|
183
|
+
# Switch to dehumidification
|
|
184
|
+
await client.set_cooling_mode(2)
|
|
185
|
+
|
|
186
|
+
# Switch to ventilation
|
|
187
|
+
await client.set_cooling_mode(3)
|
|
188
|
+
|
|
189
|
+
# Back to heating
|
|
190
|
+
await client.set_heating_mode()
|
|
191
|
+
|
|
192
|
+
# Full power off
|
|
193
|
+
await client.turn_off()
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## ๐ Documentation
|
|
199
|
+
|
|
200
|
+
- [Auto-Discovery](#-auto-discovery)
|
|
201
|
+
- [Configuration](#๏ธ-configuration)
|
|
202
|
+
- [Connection Management](#-connection-management)
|
|
203
|
+
- [Device Control (CU)](#๏ธ-device-control-cu)
|
|
204
|
+
- [Zone Control](#๏ธ-zone-control)
|
|
205
|
+
- [Data Models](#๏ธ-data-models)
|
|
206
|
+
- [Error Handling](#-error-handling)
|
|
207
|
+
- [Library Structure](#-library-structure)
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## ๐ Auto-Discovery
|
|
212
|
+
|
|
213
|
+
Scans every host in the given subnet over TCP port 1235 and returns the IPs
|
|
214
|
+
of all responding Polaris devices. No prior knowledge of device IPs required.
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
from open_polaris_local_api import PolarisAutoDiscovery
|
|
218
|
+
|
|
219
|
+
ips = await PolarisAutoDiscovery.discover(pin="1234", subnet="192.168.1.0/24")
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Parameters
|
|
223
|
+
|
|
224
|
+
| Parameter | Type | Default | Description |
|
|
225
|
+
|-----------|:----:|:-------:|-------------|
|
|
226
|
+
| **`pin`** โญ | `str` | *required* | ๐ Device PIN used for the probe |
|
|
227
|
+
| **`subnet`** โญ | `str` | *required* | ๐ CIDR subnet to scan (e.g. `"192.168.1.0/24"`) |
|
|
228
|
+
| `port` | `int` | `1235` | ๐ก TCP port to probe |
|
|
229
|
+
| `probe_timeout` | `float` | `1.5` | โฑ๏ธ Per-host timeout in seconds |
|
|
230
|
+
| `max_concurrent` | `int` | `50` | โก Maximum simultaneous TCP probes |
|
|
231
|
+
| `verbose` | `bool` | `False` | ๐ข Enable debug logging |
|
|
232
|
+
|
|
233
|
+
### Notes
|
|
234
|
+
|
|
235
|
+
- A 254-host `/24` scan with defaults completes in roughly **8 seconds**
|
|
236
|
+
- Devices running older firmware (no `stato_r` support) are still discovered โ a `res=4` reply is enough to confirm a Polaris device
|
|
237
|
+
- Tune `probe_timeout` upward if devices are on a slow or congested network
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## โ๏ธ Configuration
|
|
242
|
+
|
|
243
|
+
### **Constructor Parameters**
|
|
244
|
+
|
|
245
|
+
| Parameter | Type | Default | Description |
|
|
246
|
+
|-----------|:----:|:-------:|-------------|
|
|
247
|
+
| **`ip`** โญ | `str` | *required* | ๐ IP address of the Polaris CU device |
|
|
248
|
+
| **`pin`** โญ | `str` | *required* | ๐ PIN code for authentication |
|
|
249
|
+
| `device_id` | `str` | `None` | ๐ท๏ธ Friendly identifier (auto-set to `polaris_{ip}:{port}` if omitted) |
|
|
250
|
+
| `port` | `int` | `1235` | ๐ก TCP port on the device |
|
|
251
|
+
| `timeout` | `float` | `5.0` | โฑ๏ธ Per-command socket timeout (seconds) |
|
|
252
|
+
| `retry_attempts` | `int` | `2` | ๐ Number of retry attempts on failure |
|
|
253
|
+
| `retry_delay` | `float` | `1.0` | โณ Delay between retries (seconds) |
|
|
254
|
+
| `verbose` | `bool` | `False` | ๐ข Enable verbose debug logging |
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## ๐ Connection Management
|
|
259
|
+
|
|
260
|
+
### Connect
|
|
261
|
+
|
|
262
|
+
Verifies connectivity by performing an initial `async_update`. Sets `_connected=True` and populates cached device/zones state.
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
await client.connect()
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Raises:** `TimeoutError` or `ConnectionError` on failure.
|
|
269
|
+
|
|
270
|
+
### Disconnect
|
|
271
|
+
|
|
272
|
+
Marks the client as disconnected. TCP is stateless per-command, so no socket needs closing.
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
await client.disconnect()
|
|
276
|
+
# or
|
|
277
|
+
await client.close()
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Context Manager (recommended)
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
async with PolarisLocalClient(ip="192.168.1.100", pin="1234") as client:
|
|
284
|
+
# automatically connects on enter, disconnects on exit
|
|
285
|
+
device, zones = await client.async_update()
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## ๐ฅ๏ธ Device Control (CU)
|
|
291
|
+
|
|
292
|
+
### Full Status Refresh
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
device, zones = await client.async_update()
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Returns a tuple of `(PolarisDevice, list[PolarisZone])` and updates the internal cache.
|
|
299
|
+
|
|
300
|
+
### Raw Status
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
raw: dict = await client.get_status()
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Tries `stato_r` first (compact), falls back to `stato` if unsupported (`res=4`).
|
|
307
|
+
|
|
308
|
+
### Power
|
|
309
|
+
|
|
310
|
+
```python
|
|
311
|
+
await client.turn_on() # is_off=False
|
|
312
|
+
await client.turn_off() # is_off=True
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Operating Mode
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
await client.set_heating_mode() # cooling=False, mode=0
|
|
319
|
+
await client.set_cooling_mode(1) # raffrescamento
|
|
320
|
+
await client.set_cooling_mode(2) # dehumidification
|
|
321
|
+
await client.set_cooling_mode(3) # ventilation
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Full CU Update
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
await client.update_cu(
|
|
328
|
+
is_off=False,
|
|
329
|
+
is_cooling=True,
|
|
330
|
+
operating_mode=1, # 0=heating, 1=cooling, 2=dehum, 3=vent
|
|
331
|
+
)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## ๐ก๏ธ Zone Control
|
|
337
|
+
|
|
338
|
+
### Convenience Methods
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
await client.turn_zone_on(zone)
|
|
342
|
+
await client.turn_zone_off(zone)
|
|
343
|
+
await client.set_zone_temp(zone, 21.5)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Full Zone Update
|
|
347
|
+
|
|
348
|
+
```python
|
|
349
|
+
await client.update_zone(
|
|
350
|
+
zone,
|
|
351
|
+
is_off=False,
|
|
352
|
+
set_temp=21.0,
|
|
353
|
+
is_crono=False,
|
|
354
|
+
fancoil_set=2,
|
|
355
|
+
serranda_set=3,
|
|
356
|
+
)
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
All parameters are optional; omitted values fall back to the zone's current state.
|
|
360
|
+
|
|
361
|
+
> **Note:** `set_temp` must be known (either from last `async_update` or provided explicitly). If neither is available, `ValueError` is raised.
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## ๐๏ธ Data Models
|
|
366
|
+
|
|
367
|
+
### `PolarisDevice`
|
|
368
|
+
|
|
369
|
+
Represents the Polaris Control Unit (CU).
|
|
370
|
+
|
|
371
|
+
| Property / Field | Type | Description |
|
|
372
|
+
|------------------|------|-------------|
|
|
373
|
+
| `serial` | `str` | Device serial number |
|
|
374
|
+
| `name` | `str` | Device name |
|
|
375
|
+
| `fw_ver` | `str` | Firmware version |
|
|
376
|
+
| `ip` | `str` | IP address |
|
|
377
|
+
| `is_off` | `bool` | Whether CU is off |
|
|
378
|
+
| `is_on` | `bool` (property) | Inverse of `is_off` |
|
|
379
|
+
| `is_cooling` | `bool` | Cooling active |
|
|
380
|
+
| `operating_mode` | `int` | 0=heating, 1=cooling, 2=dehum, 3=vent |
|
|
381
|
+
| `cooling_mode_name` | `str` (property) | Human-readable mode name |
|
|
382
|
+
| `t_can` | `int` | Canal temperature setpoint (ยฐC) |
|
|
383
|
+
| `f_inv` | `int` | Winter fan speed |
|
|
384
|
+
| `f_est` | `int` | Summer fan speed |
|
|
385
|
+
| `ir_present` | `int` | IR module present flag |
|
|
386
|
+
| `num_errors` | `int` | CU error bitmask |
|
|
387
|
+
| `has_error` | `bool` (property) | `num_errors != 0` |
|
|
388
|
+
| `active_errors` | `list[str]` (property) | Decoded error strings |
|
|
389
|
+
| `zones` | `list[PolarisZone]` | Zones (populated by `async_update`) |
|
|
390
|
+
|
|
391
|
+
### `PolarisZone`
|
|
392
|
+
|
|
393
|
+
Represents a single HVAC zone.
|
|
394
|
+
|
|
395
|
+
| Property / Field | Type | Description |
|
|
396
|
+
|------------------|------|-------------|
|
|
397
|
+
| `zone_id` | `int` | Zone ID |
|
|
398
|
+
| `name` | `str` | Zone name |
|
|
399
|
+
| `current_temp` | `float \| None` | Current temperature (ยฐC) |
|
|
400
|
+
| `set_temp` | `float \| None` | Temperature setpoint (ยฐC) |
|
|
401
|
+
| `is_off` | `bool` | Whether zone is off |
|
|
402
|
+
| `is_on` | `bool` (property) | Inverse of `is_off` |
|
|
403
|
+
| `is_cooling` | `bool` | Cooling active |
|
|
404
|
+
| `fancoil` | `int` | Fan coil current speed (-1 = not installed) |
|
|
405
|
+
| `fancoil_set` | `int` | Fan coil setpoint (-1 = not installed) |
|
|
406
|
+
| `serranda` | `int` | Shutter position (-1 = not installed) |
|
|
407
|
+
| `serranda_set` | `int` | Shutter setpoint (-1 = not installed) |
|
|
408
|
+
| `is_crono_mode` | `bool` | Chrono (scheduled) mode active |
|
|
409
|
+
| `is_master` | `bool` | Zone is master |
|
|
410
|
+
| `humidity` | `float \| None` | Current humidity (%) |
|
|
411
|
+
| `set_humidity` | `float \| None` | Humidity setpoint (%) |
|
|
412
|
+
| `num_error` | `int` | Zone error bitmask |
|
|
413
|
+
| `has_error` | `bool` (property) | `num_error != 0` |
|
|
414
|
+
| `active_errors` | `list[str]` (property) | Decoded error strings |
|
|
415
|
+
|
|
416
|
+
Both models accept both **local TCP** (snake_case, ridotto, full) and **cloud API** (PascalCase) response formats via the `from_local()` factory method.
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
## โ ๏ธ Error Handling
|
|
421
|
+
|
|
422
|
+
### `PolarisApiError`
|
|
423
|
+
|
|
424
|
+
Raised on communication failures:
|
|
425
|
+
|
|
426
|
+
```python
|
|
427
|
+
from open_polaris_local_api import PolarisApiError
|
|
428
|
+
|
|
429
|
+
try:
|
|
430
|
+
async with PolarisLocalClient(ip="192.168.1.100", pin="1234") as client:
|
|
431
|
+
device, zones = await client.async_update()
|
|
432
|
+
except TimeoutError:
|
|
433
|
+
print("Device unreachable")
|
|
434
|
+
except PolarisApiError as e:
|
|
435
|
+
print(f"Communication error: {e}")
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Device / Zone Errors
|
|
439
|
+
|
|
440
|
+
```python
|
|
441
|
+
device, zones = await client.async_update()
|
|
442
|
+
|
|
443
|
+
if device.has_error:
|
|
444
|
+
print(f"CU errors: {device.active_errors}")
|
|
445
|
+
|
|
446
|
+
for zone in zones:
|
|
447
|
+
if zone.has_error:
|
|
448
|
+
print(f"Zone '{zone.name}' errors: {zone.active_errors}")
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## ๐ฆ Library Structure
|
|
454
|
+
|
|
455
|
+
```
|
|
456
|
+
open-polaris-local-api/
|
|
457
|
+
โโโ scripts/
|
|
458
|
+
โ โโโ bump_version.sh # Bump version across all files
|
|
459
|
+
โ โโโ run_tests.sh # Run the full test suite
|
|
460
|
+
โโโ examples/
|
|
461
|
+
โ โโโ README.md # Examples documentation
|
|
462
|
+
โ โโโ basic_control.py # Connect, read status, control a single device
|
|
463
|
+
โ โโโ auto_discovery.py # Discover devices then read their status
|
|
464
|
+
โ โโโ multi_device.py # Concurrent control of multiple devices
|
|
465
|
+
โ โโโ monitoring.py # Continuous polling with error alerts
|
|
466
|
+
โโโ open_polaris_local_api/
|
|
467
|
+
โ โโโ __init__.py # exports: PolarisLocalClient, PolarisApiError, PolarisDevice, PolarisZone, PolarisAutoDiscovery
|
|
468
|
+
โ โโโ client.py # PolarisLocalClient, PolarisApiError
|
|
469
|
+
โ โโโ models.py # PolarisDevice, PolarisZone dataclasses + parsing helpers
|
|
470
|
+
โ โโโ polaris_auto_discovery.py # PolarisAutoDiscovery โ subnet scanner
|
|
471
|
+
โโโ tests/
|
|
472
|
+
โโโ test_models.py
|
|
473
|
+
โโโ test_polaris_client.py
|
|
474
|
+
โโโ test_polaris_auto_discovery.py
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## ๐ก Examples
|
|
480
|
+
|
|
481
|
+
Ready-to-run example scripts are available in the [`examples/`](examples/) directory.
|
|
482
|
+
See [`examples/README.md`](examples/README.md) for full documentation and usage instructions.
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## ๐ ๏ธ Scripts
|
|
487
|
+
|
|
488
|
+
Utility scripts live in the [`scripts/`](scripts/) directory and must be run from the repo root.
|
|
489
|
+
|
|
490
|
+
### `scripts/bump_version.sh`
|
|
491
|
+
|
|
492
|
+
Keeps all version references in sync across `pyproject.toml` and `README.md` in one command.
|
|
493
|
+
|
|
494
|
+
```bash
|
|
495
|
+
./scripts/bump_version.sh patch # 1.0.1 โ 1.0.2
|
|
496
|
+
./scripts/bump_version.sh minor # 1.0.1 โ 1.1.0
|
|
497
|
+
./scripts/bump_version.sh major # 1.0.1 โ 2.0.0
|
|
498
|
+
./scripts/bump_version.sh 1.2.3 # set an explicit version
|
|
499
|
+
./scripts/bump_version.sh # interactive menu
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### `scripts/run_tests.sh`
|
|
503
|
+
|
|
504
|
+
Runs the full unit test suite with verbose output.
|
|
505
|
+
|
|
506
|
+
```bash
|
|
507
|
+
./scripts/run_tests.sh
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## ๐งช Testing
|
|
513
|
+
|
|
514
|
+
```bash
|
|
515
|
+
./scripts/run_tests.sh
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
## ๐ Requirements
|
|
521
|
+
|
|
522
|
+
- **Python 3.11+**
|
|
523
|
+
- **asyncio** support
|
|
524
|
+
- **Local network access** to the Polaris CU device (TCP port 1235)
|
|
525
|
+
- No third-party dependencies โ stdlib only
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## ๐ค Contributing
|
|
530
|
+
|
|
531
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
532
|
+
|
|
533
|
+
1. Fork the repository
|
|
534
|
+
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
|
|
535
|
+
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
|
536
|
+
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
|
537
|
+
5. Open a Pull Request
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## ๐ License
|
|
542
|
+
|
|
543
|
+
This project is licensed under the MIT License โ see the [LICENSE](LICENSE) file for details.
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## ๐ Support
|
|
548
|
+
|
|
549
|
+
- ๐ **Issues**: [Report a bug](https://github.com/VoidElle/open-polaris-local-api/issues)
|