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.
Files changed (51) hide show
  1. {python_izone-1.3.4 → python_izone-1.3.6}/.github/workflows/main.yml +4 -2
  2. python_izone-1.3.6/.pre-commit-config.yaml +7 -0
  3. python_izone-1.3.6/.vscode/extensions.json +7 -0
  4. python_izone-1.3.6/.vscode/settings.json +34 -0
  5. {python_izone-1.3.4 → python_izone-1.3.6}/PKG-INFO +3 -2
  6. {python_izone-1.3.4 → python_izone-1.3.6}/README.md +2 -1
  7. python_izone-1.3.6/docs/release-notes/v1.3.5.md +85 -0
  8. python_izone-1.3.6/docs/release-notes/v1.3.6.md +98 -0
  9. python_izone-1.3.6/pizone/__init__.py +77 -0
  10. python_izone-1.3.6/pizone/const.py +4 -0
  11. {python_izone-1.3.4 → python_izone-1.3.6}/pizone/controller.py +445 -171
  12. python_izone-1.3.6/pizone/discovery.py +1019 -0
  13. python_izone-1.3.6/pizone/exceptions.py +37 -0
  14. {python_izone-1.3.4 → python_izone-1.3.6}/pizone/power.py +29 -19
  15. python_izone-1.3.6/pizone/types.py +11 -0
  16. {python_izone-1.3.4 → python_izone-1.3.6}/pizone/version.py +2 -2
  17. {python_izone-1.3.4 → python_izone-1.3.6}/pizone/zone.py +25 -13
  18. python_izone-1.3.6/pyproject.toml +261 -0
  19. {python_izone-1.3.4 → python_izone-1.3.6}/scripts/check +2 -1
  20. {python_izone-1.3.4 → python_izone-1.3.6}/tests/conftest.py +65 -29
  21. {python_izone-1.3.4 → python_izone-1.3.6}/tests/http_fakes.py +13 -2
  22. {python_izone-1.3.4 → python_izone-1.3.6}/tests/power_data.py +21 -3
  23. {python_izone-1.3.4 → python_izone-1.3.6}/tests/resources.py +65 -0
  24. {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_controller.py +332 -59
  25. {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_discovery.py +52 -18
  26. python_izone-1.3.6/tests/test_discovery_api.py +862 -0
  27. {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_fullstack.py +25 -26
  28. {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_http.py +16 -10
  29. {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_power.py +6 -1
  30. {python_izone-1.3.4 → python_izone-1.3.6}/tests/test_zone.py +25 -11
  31. {python_izone-1.3.4 → python_izone-1.3.6}/uv.lock +24 -71
  32. python_izone-1.3.4/.pre-commit-config.yaml +0 -6
  33. python_izone-1.3.4/.pylintrc +0 -609
  34. python_izone-1.3.4/.vscode/settings.json +0 -26
  35. python_izone-1.3.4/pizone/__init__.py +0 -38
  36. python_izone-1.3.4/pizone/discovery.py +0 -521
  37. python_izone-1.3.4/pizone/exceptions.py +0 -18
  38. python_izone-1.3.4/pyproject.toml +0 -103
  39. {python_izone-1.3.4 → python_izone-1.3.6}/.gitattributes +0 -0
  40. {python_izone-1.3.4 → python_izone-1.3.6}/.gitignore +0 -0
  41. {python_izone-1.3.4 → python_izone-1.3.6}/.python-version +0 -0
  42. {python_izone-1.3.4 → python_izone-1.3.6}/.vscode/.ropeproject/config.py +0 -0
  43. {python_izone-1.3.4 → python_izone-1.3.6}/.vscode/launch.json +0 -0
  44. {python_izone-1.3.4 → python_izone-1.3.6}/api_docs/AC-DOC-1401-11_iZoneEthernetInterface.pdf +0 -0
  45. {python_izone-1.3.4 → python_izone-1.3.6}/api_docs/iPower_JSON_datastrings_Out.h +0 -0
  46. {python_izone-1.3.4 → python_izone-1.3.6}/api_docs/iZone_JSON_datastrings.h +0 -0
  47. {python_izone-1.3.4 → python_izone-1.3.6}/docs/release-notes/v1.3.4.md +0 -0
  48. {python_izone-1.3.4 → python_izone-1.3.6}/licence.txt +0 -0
  49. {python_izone-1.3.4 → python_izone-1.3.6}/pizone/py.typed +0 -0
  50. {python_izone-1.3.4 → python_izone-1.3.6}/scripts/coverage +0 -0
  51. {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 Pylint
27
- run: uv run pylint pizone
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,7 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.15.21
4
+ hooks:
5
+ - id: ruff-check
6
+ args: [--fix]
7
+ - id: ruff-format
@@ -0,0 +1,7 @@
1
+ {
2
+ "recommendations": [
3
+ "charliermarsh.ruff",
4
+ "ms-python.python",
5
+ "ms-python.mypy-type-checker"
6
+ ]
7
+ }
@@ -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.4
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 pylint pizone`
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
 
@@ -42,7 +42,8 @@ The iZone Ethernet interface is documented in
42
42
  Individual commands:
43
43
 
44
44
  - `uv run pytest tests/`
45
- - `uv run pylint pizone`
45
+ - `uv run ruff check pizone tests`
46
+ - `uv run ruff format pizone tests`
46
47
  - `uv run mypy pizone`
47
48
  - `uv build`
48
49
 
@@ -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
+ ]
@@ -0,0 +1,4 @@
1
+ """Shared constants for pizone."""
2
+
3
+ # Exact AirStreamDeviceUId when an ASH bridge has no paired AC (desk evidence).
4
+ PLACEHOLDER_DEVICE_UID = "000000000"