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.
@@ -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
+ [![Python Version](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
47
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
48
+ [![Version](https://img.shields.io/github/v/release/VoidElle/open-polaris-local-api?label=version)](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)