python-izone 1.3.4__tar.gz → 1.3.6__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_izone-1.3.4 → python_izone-1.3.6}/.github/workflows/main.yml +4 -2
- python_izone-1.3.6/.pre-commit-config.yaml +7 -0
- python_izone-1.3.6/.vscode/extensions.json +7 -0
- python_izone-1.3.6/.vscode/settings.json +34 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/PKG-INFO +3 -2
- {python_izone-1.3.4 → python_izone-1.3.6}/README.md +2 -1
- python_izone-1.3.6/docs/release-notes/v1.3.5.md +85 -0
- python_izone-1.3.6/docs/release-notes/v1.3.6.md +98 -0
- python_izone-1.3.6/pizone/__init__.py +77 -0
- python_izone-1.3.6/pizone/const.py +4 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/pizone/controller.py +445 -171
- python_izone-1.3.6/pizone/discovery.py +1019 -0
- python_izone-1.3.6/pizone/exceptions.py +37 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/pizone/power.py +29 -19
- python_izone-1.3.6/pizone/types.py +11 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/pizone/version.py +2 -2
- {python_izone-1.3.4 → python_izone-1.3.6}/pizone/zone.py +25 -13
- python_izone-1.3.6/pyproject.toml +261 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/scripts/check +2 -1
- {python_izone-1.3.4 → python_izone-1.3.6}/tests/conftest.py +65 -29
- {python_izone-1.3.4 → python_izone-1.3.6}/tests/http_fakes.py +13 -2
- {python_izone-1.3.4 → python_izone-1.3.6}/tests/power_data.py +21 -3
- {python_izone-1.3.4 → python_izone-1.3.6}/tests/resources.py +65 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_controller.py +332 -59
- {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_discovery.py +52 -18
- python_izone-1.3.6/tests/test_discovery_api.py +862 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_fullstack.py +25 -26
- {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_http.py +16 -10
- {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_power.py +6 -1
- {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_zone.py +25 -11
- {python_izone-1.3.4 → python_izone-1.3.6}/uv.lock +24 -71
- python_izone-1.3.4/.pre-commit-config.yaml +0 -6
- python_izone-1.3.4/.pylintrc +0 -609
- python_izone-1.3.4/.vscode/settings.json +0 -26
- python_izone-1.3.4/pizone/__init__.py +0 -38
- python_izone-1.3.4/pizone/discovery.py +0 -521
- python_izone-1.3.4/pizone/exceptions.py +0 -18
- python_izone-1.3.4/pyproject.toml +0 -103
- {python_izone-1.3.4 → python_izone-1.3.6}/.gitattributes +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/.gitignore +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/.python-version +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/.vscode/.ropeproject/config.py +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/.vscode/launch.json +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/api_docs/AC-DOC-1401-11_iZoneEthernetInterface.pdf +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/api_docs/iPower_JSON_datastrings_Out.h +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/api_docs/iZone_JSON_datastrings.h +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/docs/release-notes/v1.3.4.md +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/licence.txt +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/pizone/py.typed +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/scripts/coverage +0 -0
- {python_izone-1.3.4 → python_izone-1.3.6}/tests/__init__.py +0 -0
|
@@ -23,8 +23,10 @@ jobs:
|
|
|
23
23
|
cache-suffix: build
|
|
24
24
|
- name: Install dependencies
|
|
25
25
|
run: uv sync --frozen --all-extras --group dev
|
|
26
|
-
- name: Lint with
|
|
27
|
-
run:
|
|
26
|
+
- name: Lint with Ruff
|
|
27
|
+
run: |
|
|
28
|
+
uv run ruff check pizone tests
|
|
29
|
+
uv run ruff format --check pizone tests
|
|
28
30
|
- name: Type check with mypy
|
|
29
31
|
run: uv run mypy pizone
|
|
30
32
|
- name: Test with pytest
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
|
|
3
|
+
"python.testing.pytestEnabled": true,
|
|
4
|
+
"python.globalModuleInstallation": false,
|
|
5
|
+
"[python]": {
|
|
6
|
+
"editor.defaultFormatter": "charliermarsh.ruff",
|
|
7
|
+
"editor.formatOnSave": true,
|
|
8
|
+
"editor.codeActionsOnSave": {
|
|
9
|
+
"source.fixAll.ruff": "explicit",
|
|
10
|
+
"source.organizeImports.ruff": "explicit"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"ruff.enable": true,
|
|
14
|
+
"ruff.nativeServer": "on",
|
|
15
|
+
"ruff.configurationPreference": "filesystemFirst",
|
|
16
|
+
"ruff.interpreter": ["${workspaceFolder}/.venv/bin/python"],
|
|
17
|
+
"python.analysis.typeCheckingMode": "off",
|
|
18
|
+
"cursorpyright.analysis.typeCheckingMode": "off",
|
|
19
|
+
"pylint.enabled": false,
|
|
20
|
+
"mypy-type-checker.interpreter": ["${workspaceFolder}/.venv/bin/python"],
|
|
21
|
+
"mypy-type-checker.args": ["--config-file=${workspaceFolder}/pyproject.toml"],
|
|
22
|
+
"cSpell.words": [
|
|
23
|
+
"inetaddrs",
|
|
24
|
+
"inetaddr",
|
|
25
|
+
"ifaddresses",
|
|
26
|
+
"rescan",
|
|
27
|
+
"pizone",
|
|
28
|
+
"ipower",
|
|
29
|
+
"COOLDOWN",
|
|
30
|
+
"coro",
|
|
31
|
+
"datagram",
|
|
32
|
+
"fanout"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-izone
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.6
|
|
4
4
|
Summary: A python interface to the iZone airconditioner controller
|
|
5
5
|
Project-URL: Homepage, https://github.com/Swamp-Ig/pizone
|
|
6
6
|
Author-email: Penny Wood <pypl@ninjateaparty.com>
|
|
@@ -68,7 +68,8 @@ The iZone Ethernet interface is documented in
|
|
|
68
68
|
Individual commands:
|
|
69
69
|
|
|
70
70
|
- `uv run pytest tests/`
|
|
71
|
-
- `uv run
|
|
71
|
+
- `uv run ruff check pizone tests`
|
|
72
|
+
- `uv run ruff format pizone tests`
|
|
72
73
|
- `uv run mypy pizone`
|
|
73
74
|
- `uv build`
|
|
74
75
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Release notes — v1.3.5
|
|
2
|
+
|
|
3
|
+
Released 2026-07-17.
|
|
4
|
+
|
|
5
|
+
**Compare range:** https://github.com/Swamp-Ig/pizone/compare/v1.3.4...v1.3.5
|
|
6
|
+
|
|
7
|
+
This is a **pre-release**. It includes work toward a better Home Assistant
|
|
8
|
+
integration (stored bridge IP, improved discovery, and cleaner refresh
|
|
9
|
+
timing), plus reliability fixes you can use today. Home Assistant still uses
|
|
10
|
+
the same library entry points as 1.3.4. The older API remains available; a
|
|
11
|
+
future major release will remove it in favour of the new discovery and refresh
|
|
12
|
+
API below.
|
|
13
|
+
|
|
14
|
+
## Summary
|
|
15
|
+
|
|
16
|
+
Since v1.3.4:
|
|
17
|
+
|
|
18
|
+
- HTTP connections are closed after each GET, which greatly reduces stalls on
|
|
19
|
+
some iZone bridges ([home-assistant/core#176536](https://github.com/home-assistant/core/issues/176536))
|
|
20
|
+
- A new discovery and refresh API is available for integrators (the older API
|
|
21
|
+
still works for now)
|
|
22
|
+
- iPower monitoring is off unless you opt in with `ENABLE_POWER = True`
|
|
23
|
+
- Bridges that are not paired to an air conditioner (UID `000000000`) are
|
|
24
|
+
ignored or rejected instead of showing up as broken controllers
|
|
25
|
+
|
|
26
|
+
## Bridge connection reliability
|
|
27
|
+
|
|
28
|
+
Some ASH bridges misbehave when HTTP connections are reused. This release:
|
|
29
|
+
|
|
30
|
+
- Sends `Connection: close` on every HTTP GET (polling and discovery probes)
|
|
31
|
+
- Closes TCP connections after each request when the library creates its own
|
|
32
|
+
aiohttp session
|
|
33
|
+
|
|
34
|
+
This reduces how often the bridge stops responding. It does not fix every
|
|
35
|
+
hub-side stall; replacing faulty hardware may still be needed.
|
|
36
|
+
|
|
37
|
+
## New discovery and refresh API (optional for now)
|
|
38
|
+
|
|
39
|
+
Integrators can adopt a clearer model without background poll loops inside the
|
|
40
|
+
library:
|
|
41
|
+
|
|
42
|
+
- `create_discovery()` with callbacks when a controller is found or its IP
|
|
43
|
+
changes
|
|
44
|
+
- `discover_by_host`, `discover_by_uid`, `discover_all` to find bridges on the
|
|
45
|
+
network
|
|
46
|
+
- `create_controller(uid, host)` to connect to a known bridge
|
|
47
|
+
- `refresh_all`, `refresh_system`, `refresh_zones`, and related methods so the
|
|
48
|
+
caller controls when to poll
|
|
49
|
+
|
|
50
|
+
The previous API (`discovery()`, `fetch_controller`, `Listener`, and so on)
|
|
51
|
+
still works and is what Home Assistant uses today.
|
|
52
|
+
|
|
53
|
+
## iPower opt-in
|
|
54
|
+
|
|
55
|
+
`ENABLE_POWER` now defaults to `False`. Power monitor HTTP traffic only runs
|
|
56
|
+
after you set `pizone.ENABLE_POWER = True` before creating or refreshing a
|
|
57
|
+
controller. This avoids extra requests on bridges that advertise iPower but are
|
|
58
|
+
not set up for it. The default may change if more field data shows power
|
|
59
|
+
monitoring is reliable enough to enable by default.
|
|
60
|
+
|
|
61
|
+
## Unpaired bridges
|
|
62
|
+
|
|
63
|
+
Wireless ASH bridges with no AC paired report device UID `000000000`. The
|
|
64
|
+
library now:
|
|
65
|
+
|
|
66
|
+
- Ignores them during passive UDP discovery
|
|
67
|
+
- Raises `UnpairedBridgeError` if you try to connect to that UID on purpose
|
|
68
|
+
- Refuses to cache them after an IP lookup
|
|
69
|
+
|
|
70
|
+
Paired bridges that temporarily report fault-shaped settings (AC off, reboot,
|
|
71
|
+
and so on) are unchanged: they keep their real UID and show as disconnected
|
|
72
|
+
until the AC data recovers.
|
|
73
|
+
|
|
74
|
+
## Other changes
|
|
75
|
+
|
|
76
|
+
- UDP `CHANGED_*` notifications are recognized and ignored (no spurious refresh)
|
|
77
|
+
- Discovery verifies bridge identity over HTTP before trusting UDP replies
|
|
78
|
+
- Development tooling moved to Ruff for lint and format
|
|
79
|
+
|
|
80
|
+
## Home Assistant
|
|
81
|
+
|
|
82
|
+
If you maintain the iZone integration or test against this library, pin
|
|
83
|
+
`python-izone==1.3.5` for the connection reuse fix on the current integration
|
|
84
|
+
code path. The new discovery API is included for upcoming integration work but
|
|
85
|
+
is not required for a version bump alone.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Release notes — v1.3.6
|
|
2
|
+
|
|
3
|
+
Released 2026-07-19.
|
|
4
|
+
|
|
5
|
+
**Compare range:** https://github.com/Swamp-Ig/pizone/compare/v1.3.5...v1.3.6
|
|
6
|
+
|
|
7
|
+
This is a **pre-release** for integrators (especially Home Assistant Phase 2
|
|
8
|
+
work). It is not aimed at end users as a standalone upgrade. Prefer waiting for
|
|
9
|
+
a future stable line unless you are developing against the new discovery API.
|
|
10
|
+
|
|
11
|
+
## Since v1.3.5
|
|
12
|
+
|
|
13
|
+
- Discovery scan wait (`SCAN_TIMEOUT`) reduced from 20s to 5s
|
|
14
|
+
- `close()` waits until the UDP transport has finished tearing down before
|
|
15
|
+
returning, so a following `create_discovery()` can rebind `:7005` immediately
|
|
16
|
+
(fixes delete → HomeKit rediscovery `EADDRINUSE` races)
|
|
17
|
+
- Failed `create_discovery` / `start_discovery` no longer leaves a sticky
|
|
18
|
+
process singleton that blocked retries until process restart
|
|
19
|
+
- `PLACEHOLDER_DEVICE_UID` is no longer exported from the public package API
|
|
20
|
+
(`UnpairedBridgeError` remains the supported signal)
|
|
21
|
+
|
|
22
|
+
Everything from v1.3.5 below still applies.
|
|
23
|
+
|
|
24
|
+
## Summary (since v1.3.4)
|
|
25
|
+
|
|
26
|
+
- HTTP connections are closed after each GET, which greatly reduces stalls on
|
|
27
|
+
some iZone bridges ([home-assistant/core#176536](https://github.com/home-assistant/core/issues/176536))
|
|
28
|
+
- A new discovery and refresh API is available for integrators (the older API
|
|
29
|
+
still works for now)
|
|
30
|
+
- iPower monitoring is off unless you opt in with `ENABLE_POWER = True`
|
|
31
|
+
- Bridges that are not paired to an air conditioner (UID `000000000`) are
|
|
32
|
+
ignored or rejected instead of showing up as broken controllers
|
|
33
|
+
- Faster discovery scans and reliable UDP close → recreate (this release)
|
|
34
|
+
|
|
35
|
+
## Bridge connection reliability
|
|
36
|
+
|
|
37
|
+
Some ASH bridges misbehave when HTTP connections are reused. This release
|
|
38
|
+
line:
|
|
39
|
+
|
|
40
|
+
- Sends `Connection: close` on every HTTP GET (polling and discovery probes)
|
|
41
|
+
- Closes TCP connections after each request when the library creates its own
|
|
42
|
+
aiohttp session
|
|
43
|
+
|
|
44
|
+
This reduces how often the bridge stops responding. It does not fix every
|
|
45
|
+
hub-side stall; replacing faulty hardware may still be needed.
|
|
46
|
+
|
|
47
|
+
## New discovery and refresh API (optional for now)
|
|
48
|
+
|
|
49
|
+
Integrators can adopt a clearer model without background poll loops inside the
|
|
50
|
+
library:
|
|
51
|
+
|
|
52
|
+
- `create_discovery()` with callbacks when a controller is found or its IP
|
|
53
|
+
changes
|
|
54
|
+
- `discover_by_host`, `discover_by_uid`, `discover_all` to find bridges on the
|
|
55
|
+
network
|
|
56
|
+
- `create_controller(uid, host)` to connect to a known bridge
|
|
57
|
+
- `refresh_all`, `refresh_system`, `refresh_zones`, and related methods so the
|
|
58
|
+
caller controls when to poll
|
|
59
|
+
|
|
60
|
+
`close()` on the discovery service now waits for UDP teardown so the next
|
|
61
|
+
`create_discovery()` can bind the discovery port again without racing the OS
|
|
62
|
+
socket release.
|
|
63
|
+
|
|
64
|
+
The previous API (`discovery()`, `fetch_controller`, `Listener`, and so on)
|
|
65
|
+
still works.
|
|
66
|
+
|
|
67
|
+
## iPower opt-in
|
|
68
|
+
|
|
69
|
+
`ENABLE_POWER` now defaults to `False`. Power monitor HTTP traffic only runs
|
|
70
|
+
after you set `pizone.ENABLE_POWER = True` before creating or refreshing a
|
|
71
|
+
controller. This avoids extra requests on bridges that advertise iPower but are
|
|
72
|
+
not set up for it. The default may change if more field data shows power
|
|
73
|
+
monitoring is reliable enough to enable by default.
|
|
74
|
+
|
|
75
|
+
## Unpaired bridges
|
|
76
|
+
|
|
77
|
+
Wireless ASH bridges with no AC paired report device UID `000000000`. The
|
|
78
|
+
library now:
|
|
79
|
+
|
|
80
|
+
- Ignores them during passive UDP discovery
|
|
81
|
+
- Raises `UnpairedBridgeError` if you try to connect to that UID on purpose
|
|
82
|
+
- Refuses to cache them after an IP lookup
|
|
83
|
+
|
|
84
|
+
Paired bridges that temporarily report fault-shaped settings (AC off, reboot,
|
|
85
|
+
and so on) are unchanged: they keep their real UID and show as disconnected
|
|
86
|
+
until the AC data recovers.
|
|
87
|
+
|
|
88
|
+
## Other changes
|
|
89
|
+
|
|
90
|
+
- UDP `CHANGED_*` notifications are recognized and ignored (no spurious refresh)
|
|
91
|
+
- Discovery verifies bridge identity over HTTP before trusting UDP replies
|
|
92
|
+
- Development tooling moved to Ruff for lint and format
|
|
93
|
+
|
|
94
|
+
## Home Assistant
|
|
95
|
+
|
|
96
|
+
Pin `python-izone==1.3.6` when adopting the Phase 2 discovery / coordinator
|
|
97
|
+
runtime. This pre-release is intended for that integration work, not as a
|
|
98
|
+
general end-user library upgrade on the legacy path alone.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Interface to the iZone air conditioner controller.
|
|
2
|
+
|
|
3
|
+
Prefer :func:`~pizone.discovery.create_discovery` or
|
|
4
|
+
:func:`~pizone.discovery.discovery` to obtain a discovery service, then create
|
|
5
|
+
or discover :class:`~pizone.controller.Controller` instances through that
|
|
6
|
+
service. Zones and power monitors are owned by their controller.
|
|
7
|
+
|
|
8
|
+
Synchronous property reads return cached device data and do not raise
|
|
9
|
+
:exc:`ConnectionError`. Async command and refresh methods perform HTTP I/O and
|
|
10
|
+
raise :exc:`ConnectionError` when the device cannot be reached. They raise
|
|
11
|
+
:exc:`~pizone.exceptions.ControllerCommandError` when the device responds but
|
|
12
|
+
rejects the request (``{ERROR...}`` body or HTTP 4xx).
|
|
13
|
+
|
|
14
|
+
Layered connection state on :class:`~pizone.controller.Controller`:
|
|
15
|
+
|
|
16
|
+
- :attr:`~pizone.controller.Controller.bridge_connected` — ASH bridge HTTP transport
|
|
17
|
+
- :attr:`~pizone.controller.Controller.connected` — bridge plus valid iZone AC data
|
|
18
|
+
- :attr:`~pizone.power.Power.connected` — power monitor I/O (when enabled)
|
|
19
|
+
|
|
20
|
+
Power support is off by default. Enable at runtime before create/refresh when
|
|
21
|
+
the consumer needs iPower (for example future Home Assistant power entities)::
|
|
22
|
+
|
|
23
|
+
import pizone
|
|
24
|
+
pizone.ENABLE_POWER = True
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import sys
|
|
28
|
+
from types import ModuleType
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
from . import power as power_mod
|
|
32
|
+
from .controller import Controller
|
|
33
|
+
from .discovery import DiscoveryService, Listener, create_discovery, discovery
|
|
34
|
+
from .exceptions import ControllerCommandError, ResponseDecodeError, UnpairedBridgeError
|
|
35
|
+
from .power import BatteryLevel, Power, PowerChannel, PowerDevice, PowerGroup
|
|
36
|
+
from .types import ControllerEndpoint
|
|
37
|
+
from .zone import Zone
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def __getattr__(name: str) -> Any:
|
|
41
|
+
if name == "ENABLE_POWER":
|
|
42
|
+
return power_mod.ENABLE_POWER
|
|
43
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _PizoneModule(ModuleType):
|
|
47
|
+
"""Allow ``pizone.ENABLE_POWER = True`` to update :mod:`pizone.power`."""
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def ENABLE_POWER(self) -> bool:
|
|
51
|
+
return power_mod.ENABLE_POWER
|
|
52
|
+
|
|
53
|
+
@ENABLE_POWER.setter
|
|
54
|
+
def ENABLE_POWER(self, value: bool) -> None:
|
|
55
|
+
power_mod.ENABLE_POWER = bool(value)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
sys.modules[__name__].__class__ = _PizoneModule
|
|
59
|
+
|
|
60
|
+
__all__ = [
|
|
61
|
+
"ENABLE_POWER",
|
|
62
|
+
"BatteryLevel",
|
|
63
|
+
"Controller",
|
|
64
|
+
"ControllerCommandError",
|
|
65
|
+
"ControllerEndpoint",
|
|
66
|
+
"DiscoveryService",
|
|
67
|
+
"Listener",
|
|
68
|
+
"Power",
|
|
69
|
+
"PowerChannel",
|
|
70
|
+
"PowerDevice",
|
|
71
|
+
"PowerGroup",
|
|
72
|
+
"ResponseDecodeError",
|
|
73
|
+
"UnpairedBridgeError",
|
|
74
|
+
"Zone",
|
|
75
|
+
"create_discovery",
|
|
76
|
+
"discovery",
|
|
77
|
+
]
|