quilt-hp-python 0.5.0__tar.gz → 0.5.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.
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/CHANGELOG.md +25 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/PKG-INFO +5 -1
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/README.md +4 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/reference/client.md +1 -1
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/pyproject.toml +1 -1
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/__init__.py +1 -1
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/client.py +2 -1
- quilt_hp_python-0.5.1/src/quilt_hp/models/_helpers.py +52 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/enums.py +15 -10
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/space.py +1 -6
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/system.py +8 -22
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/services/streaming.py +2 -18
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/tokens.py +41 -1
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/transport.py +7 -28
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_cli_surfaces_extra.py +1 -1
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_streaming.py +1 -1
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_transport.py +3 -3
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_transport_interceptor_extra.py +13 -13
- quilt_hp_python-0.5.0/src/quilt_hp/models/_helpers.py +0 -31
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/.github/copilot-instructions.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/.github/workflows/ci.yml +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/.github/workflows/docs-deploy.yml +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/.github/workflows/release.yml +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/.gitignore +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/LICENSE +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/explanation/architecture.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/explanation/authentication.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/explanation/grpc-and-protobuf.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/explanation/snapshot-and-stream.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/explanation/streaming-protocol.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/how-to/authenticate.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/how-to/automation-daemon.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/how-to/cli-scripting.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/how-to/configure-comfort-settings.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/how-to/configure-schedules.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/how-to/contribute.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/how-to/control-spaces.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/how-to/home-assistant.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/how-to/regenerate-protos.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/how-to/stream-updates.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/how-to/tui-app.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/index.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/reference/api-reference.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/reference/documentation-standards.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/reference/grpc-services-matrix.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/reference/hds-entities.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/reference/models.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/reference/token-management.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/docs/tutorial/get-started.md +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/mkdocs.yml +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/proto/cleaned/quilt_device_pairing.proto +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/proto/cleaned/quilt_hds.proto +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/proto/cleaned/quilt_notifier.proto +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/proto/cleaned/quilt_services.proto +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/proto/cleaned/quilt_system.proto +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/scripts/bump_version.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/scripts/check_docs_nav.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/scripts/generate_public_api_reference.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/scripts/regen_protos.sh +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_paths.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/__init__.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_device_pairing_pb2.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_device_pairing_pb2.pyi +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_device_pairing_pb2_grpc.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_hds_pb2.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_hds_pb2.pyi +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_hds_pb2_grpc.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_notifier_pb2.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_notifier_pb2.pyi +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_notifier_pb2_grpc.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_services_pb2.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_services_pb2.pyi +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_services_pb2_grpc.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_system_pb2.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_system_pb2.pyi +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_system_pb2_grpc.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/auth.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/cli/__init__.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/cli/main.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/cli/settings.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/cli/store.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/cli/tui.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/const.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/exceptions.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/__init__.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/comfort.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/controller.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/energy.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/indoor_unit.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/outdoor_unit.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/qsm.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/schedule.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/sensor.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/models/software_update.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/py.typed +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/services/__init__.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/services/hds.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/services/system.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/services/user.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/__init__.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/conftest.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_auth.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_auth_store_settings_edges.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_cli_feature_completion.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_cli_login.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_client_cache.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_client_service_error_paths.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_grpc_retry.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_hds_payloads.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_hds_schedule_mapping.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_hds_service_branches.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_models.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_models_extra.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_models_from_proto.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_settings_store.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_streaming_concurrency.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_streaming_debounce.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_streaming_health.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_streaming_reconnect_dispatch_extra.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_tokens.py +0 -0
- {quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_tui_bindings.py +0 -0
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.5.1] - 2026-06-30
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- `SpaceControls.display_setpoint_str()` — removed dead unreachable code in the
|
|
9
|
+
fallback branch; `temperature_setpoint_c` is typed `float` so the `None`-guard
|
|
10
|
+
lines were never executed (confirmed by coverage)
|
|
11
|
+
- `SystemSnapshot.apply_outdoor_unit()` — refactored to collect field patches into
|
|
12
|
+
an `updates` dict and call `dataclasses.replace()` once, consistent with all other
|
|
13
|
+
`apply_*` methods; previously called `replace()` twice in sequence, creating an
|
|
14
|
+
unnecessary intermediate object
|
|
15
|
+
- `QuiltClient.close()` now sets `self._token = None`; previously left a stale token
|
|
16
|
+
accessible via `get_current_token()` after the channel was closed
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- `invoke_refresh_callback` (formerly `_invoke_refresh_callback`) extracted from
|
|
20
|
+
`transport.py` and `services/streaming.py` into a single shared implementation in
|
|
21
|
+
`tokens.py`; the streaming copy lacked the `WeakKeyDictionary` signature cache,
|
|
22
|
+
causing `inspect.signature()` to be called on every token-refresh event
|
|
23
|
+
- `FanSpeed.to_wire()` and `LouverAngle.to_wire()` now reference module-level
|
|
24
|
+
constant dicts (`_FAN_SPEED_WIRE_MAP`, `_LOUVER_ANGLE_WIRE_MAP`) instead of
|
|
25
|
+
re-allocating the mapping on every call
|
|
26
|
+
- `_id_variants()` moved from `models/system.py` into `models/_helpers.py` and
|
|
27
|
+
reused by `lookup_hardware()`; eliminates duplicated ID-normalisation logic
|
|
28
|
+
- `QuiltClient.invalidate_snapshot()` log level changed from `WARNING` to `DEBUG`
|
|
29
|
+
|
|
5
30
|
## [0.5.0] - 2026-06-04
|
|
6
31
|
|
|
7
32
|
### Added protocol support
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quilt-hp-python
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Async Python client for Quilt mini-split HVAC systems
|
|
5
5
|
Project-URL: Repository, https://github.com/eman/quilt-hp-python
|
|
6
6
|
Project-URL: Issues, https://github.com/eman/quilt-hp-python/issues
|
|
@@ -139,6 +139,10 @@ quilt energy --period week
|
|
|
139
139
|
quilt set "Living Room" --mode cool --cool 22
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
+
## Related Projects
|
|
143
|
+
|
|
144
|
+
- **[homeassistant-quilt-hp](https://github.com/eman/homeassistant-quilt-hp)** — Home Assistant custom integration built on this library. Exposes Quilt spaces, indoor/outdoor units, sensors, and schedules as HA entities with full climate control.
|
|
145
|
+
|
|
142
146
|
## Development
|
|
143
147
|
|
|
144
148
|
```bash
|
|
@@ -74,6 +74,10 @@ quilt energy --period week
|
|
|
74
74
|
quilt set "Living Room" --mode cool --cool 22
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
## Related Projects
|
|
78
|
+
|
|
79
|
+
- **[homeassistant-quilt-hp](https://github.com/eman/homeassistant-quilt-hp)** — Home Assistant custom integration built on this library. Exposes Quilt spaces, indoor/outdoor units, sensors, and schedules as HA entities with full climate control.
|
|
80
|
+
|
|
77
81
|
## Development
|
|
78
82
|
|
|
79
83
|
```bash
|
|
@@ -273,7 +273,7 @@ class QuiltClient:
|
|
|
273
273
|
|
|
274
274
|
def invalidate_snapshot(self) -> None:
|
|
275
275
|
"""Discard the cached snapshot so the next call fetches fresh data."""
|
|
276
|
-
logger.
|
|
276
|
+
logger.debug("Invalidating snapshot cache")
|
|
277
277
|
self._snapshot_cache = None
|
|
278
278
|
self._snapshot_cached_at = 0.0
|
|
279
279
|
|
|
@@ -640,6 +640,7 @@ class QuiltClient:
|
|
|
640
640
|
if self._channel is not None:
|
|
641
641
|
await self._channel.close()
|
|
642
642
|
self._channel = None
|
|
643
|
+
self._token = None
|
|
643
644
|
self._hds = None
|
|
644
645
|
self._sysinfo = None
|
|
645
646
|
self._user_svc = None
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _id_variant_keys(raw: str) -> tuple[str, ...]:
|
|
5
|
+
"""Return ID variant keys in deterministic priority order (exact → tail → casefold)."""
|
|
6
|
+
tail_slash = raw.rsplit("/", 1)[-1]
|
|
7
|
+
tail_colon = raw.rsplit(":", 1)[-1]
|
|
8
|
+
return (
|
|
9
|
+
raw,
|
|
10
|
+
tail_slash,
|
|
11
|
+
tail_colon,
|
|
12
|
+
raw.casefold(),
|
|
13
|
+
tail_slash.casefold(),
|
|
14
|
+
tail_colon.casefold(),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _id_variants(value: str | None) -> set[str]:
|
|
19
|
+
"""Return raw and normalized ID variants for matching resource IDs."""
|
|
20
|
+
if not value:
|
|
21
|
+
return set()
|
|
22
|
+
raw = value.strip()
|
|
23
|
+
if not raw:
|
|
24
|
+
return set()
|
|
25
|
+
return {v for v in _id_variant_keys(raw) if v}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def lookup_hardware(hw_map: dict[str, object], hardware_id: str | None) -> object | None:
|
|
29
|
+
"""Resolve hardware objects across common ID formats.
|
|
30
|
+
|
|
31
|
+
Keys are tried in deterministic priority order: exact → tail (after last
|
|
32
|
+
``/`` or ``:`` separator) → casefold variants, matching the behaviour of
|
|
33
|
+
the original implementation.
|
|
34
|
+
"""
|
|
35
|
+
if not hardware_id:
|
|
36
|
+
return None
|
|
37
|
+
raw = hardware_id.strip()
|
|
38
|
+
if not raw:
|
|
39
|
+
return None
|
|
40
|
+
for key in _id_variant_keys(raw):
|
|
41
|
+
hw = hw_map.get(key)
|
|
42
|
+
if hw is not None:
|
|
43
|
+
return hw
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def parse_wifi_state(proto: object) -> tuple[str | None, str | None, int | None]:
|
|
48
|
+
"""Extract WiFi fields while preserving explicit zero signal values."""
|
|
49
|
+
ssid = getattr(proto, "ssid", "") or None
|
|
50
|
+
ip = getattr(proto, "ipv4_address", None) or None
|
|
51
|
+
signal = getattr(proto, "signal_level_dbm", None)
|
|
52
|
+
return ssid, ip, signal if signal is not None else None
|
|
@@ -59,15 +59,7 @@ class FanSpeed(IntEnum):
|
|
|
59
59
|
|
|
60
60
|
def to_wire(self) -> tuple[int, float]:
|
|
61
61
|
"""Return (fan_speed_mode, fan_speed_percent) for the wire protocol."""
|
|
62
|
-
|
|
63
|
-
FanSpeed.AUTO: (1, 0.0), # FAN_SPEED_MODE_AUTO
|
|
64
|
-
FanSpeed.QUIET: (2, 0.20), # FAN_SPEED_MODE_SETPOINT
|
|
65
|
-
FanSpeed.LOW: (2, 0.40),
|
|
66
|
-
FanSpeed.MEDIUM: (2, 0.60),
|
|
67
|
-
FanSpeed.HIGH: (2, 0.80),
|
|
68
|
-
FanSpeed.BLAST: (2, 1.00),
|
|
69
|
-
}
|
|
70
|
-
return _MAP[self]
|
|
62
|
+
return _FAN_SPEED_WIRE_MAP[self.value]
|
|
71
63
|
|
|
72
64
|
@classmethod
|
|
73
65
|
def from_wire(cls, mode: int, percent: float) -> FanSpeed:
|
|
@@ -85,6 +77,16 @@ class FanSpeed(IntEnum):
|
|
|
85
77
|
return cls.BLAST
|
|
86
78
|
|
|
87
79
|
|
|
80
|
+
_FAN_SPEED_WIRE_MAP: dict[int, tuple[int, float]] = {
|
|
81
|
+
0: (1, 0.0), # AUTO → FAN_SPEED_MODE_AUTO
|
|
82
|
+
1: (2, 0.20), # QUIET → FAN_SPEED_MODE_SETPOINT
|
|
83
|
+
2: (2, 0.40), # LOW
|
|
84
|
+
3: (2, 0.60), # MEDIUM
|
|
85
|
+
4: (2, 0.80), # HIGH
|
|
86
|
+
5: (2, 1.00), # BLAST
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
88
90
|
class LouverMode(IntEnum):
|
|
89
91
|
"""Indoor unit louver mode."""
|
|
90
92
|
|
|
@@ -129,7 +131,7 @@ class LouverAngle(IntEnum):
|
|
|
129
131
|
|
|
130
132
|
def to_wire(self) -> float:
|
|
131
133
|
"""Return the louver_fixed_position float for the wire."""
|
|
132
|
-
return
|
|
134
|
+
return _LOUVER_ANGLE_WIRE_MAP[self.value]
|
|
133
135
|
|
|
134
136
|
@classmethod
|
|
135
137
|
def from_wire(cls, position: float) -> LouverAngle:
|
|
@@ -145,6 +147,9 @@ class LouverAngle(IntEnum):
|
|
|
145
147
|
return cls.ANGLE5
|
|
146
148
|
|
|
147
149
|
|
|
150
|
+
_LOUVER_ANGLE_WIRE_MAP: dict[int, float] = {1: 0.20, 2: 0.40, 3: 0.60, 4: 0.80, 5: 1.00}
|
|
151
|
+
|
|
152
|
+
|
|
148
153
|
class LightPreset(IntEnum):
|
|
149
154
|
"""Built-in LED color presets (RGBW packed int32)."""
|
|
150
155
|
|
|
@@ -85,12 +85,7 @@ class SpaceControls:
|
|
|
85
85
|
return fmt(self.heating_setpoint_c)
|
|
86
86
|
if mode == HVACMode.AUTO:
|
|
87
87
|
return f"{fmt(self.heating_setpoint_c)}–{fmt(self.cooling_setpoint_c)}"
|
|
88
|
-
|
|
89
|
-
if best is None:
|
|
90
|
-
best = self.cooling_setpoint_c
|
|
91
|
-
if best is None:
|
|
92
|
-
best = self.heating_setpoint_c
|
|
93
|
-
return fmt(best) if best is not None else "--"
|
|
88
|
+
return fmt(self.temperature_setpoint_c)
|
|
94
89
|
|
|
95
90
|
@property
|
|
96
91
|
def has_standby_sentinel_setpoints(self) -> bool:
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from typing import Any, cast
|
|
7
7
|
|
|
8
|
+
from quilt_hp.models._helpers import _id_variants
|
|
8
9
|
from quilt_hp.models.comfort import ComfortSetting
|
|
9
10
|
from quilt_hp.models.controller import Controller
|
|
10
11
|
from quilt_hp.models.enums import (
|
|
@@ -24,21 +25,6 @@ from quilt_hp.models.software_update import SoftwareUpdateInfo
|
|
|
24
25
|
from quilt_hp.models.space import Space
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
def _id_variants(value: str | None) -> set[str]:
|
|
28
|
-
"""Return raw and normalized ID variants for matching resource IDs."""
|
|
29
|
-
if not value:
|
|
30
|
-
return set()
|
|
31
|
-
raw = value.strip()
|
|
32
|
-
if not raw:
|
|
33
|
-
return set()
|
|
34
|
-
tail_slash = raw.rsplit("/", 1)[-1]
|
|
35
|
-
tail_colon = raw.rsplit(":", 1)[-1]
|
|
36
|
-
variants = {raw, tail_slash, tail_colon, raw.casefold()}
|
|
37
|
-
variants.add(tail_slash.casefold())
|
|
38
|
-
variants.add(tail_colon.casefold())
|
|
39
|
-
return {v for v in variants if v}
|
|
40
|
-
|
|
41
|
-
|
|
42
28
|
@dataclass(slots=True)
|
|
43
29
|
class Location:
|
|
44
30
|
"""A Quilt location with global settings like schedule execution state."""
|
|
@@ -283,17 +269,17 @@ class SystemSnapshot:
|
|
|
283
269
|
|
|
284
270
|
for i, u in enumerate(self.outdoor_units):
|
|
285
271
|
if u.id == odu.id:
|
|
272
|
+
updates: dict[str, Any] = {}
|
|
286
273
|
# Preserve hvac_state when stream diff has a default-zero state
|
|
287
274
|
if not odu.hvac_state and u.hvac_state:
|
|
288
|
-
|
|
275
|
+
updates["hvac_state"] = u.hvac_state
|
|
289
276
|
# Preserve hardware info — stream diffs are parsed without hw_map
|
|
290
277
|
if odu.model_sku is None and u.model_sku is not None:
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
)
|
|
278
|
+
updates["model_sku"] = u.model_sku
|
|
279
|
+
updates["serial_number"] = u.serial_number
|
|
280
|
+
updates["firmware_version"] = u.firmware_version
|
|
281
|
+
if updates:
|
|
282
|
+
odu = replace(odu, **updates)
|
|
297
283
|
self.outdoor_units[i] = odu
|
|
298
284
|
return odu
|
|
299
285
|
self.outdoor_units.append(odu)
|
|
@@ -9,7 +9,6 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import asyncio
|
|
11
11
|
import contextlib
|
|
12
|
-
import inspect
|
|
13
12
|
import logging
|
|
14
13
|
import time
|
|
15
14
|
from collections.abc import AsyncIterator, Awaitable, Callable, Sequence
|
|
@@ -30,7 +29,7 @@ from quilt_hp.models.qsm import QuiltSmartModule
|
|
|
30
29
|
from quilt_hp.models.sensor import ControllerRemoteSensor, RemoteSensor
|
|
31
30
|
from quilt_hp.models.software_update import SoftwareUpdateInfo
|
|
32
31
|
from quilt_hp.models.space import Space
|
|
33
|
-
from quilt_hp.tokens import TokenRefreshContext, TokenRefreshReason
|
|
32
|
+
from quilt_hp.tokens import TokenRefreshContext, TokenRefreshReason, invoke_refresh_callback
|
|
34
33
|
|
|
35
34
|
logger = logging.getLogger(__name__)
|
|
36
35
|
|
|
@@ -60,21 +59,6 @@ type _EventKey = tuple[str, str]
|
|
|
60
59
|
type _AnyCallback = Callable[[Any], Awaitable[None] | None]
|
|
61
60
|
|
|
62
61
|
|
|
63
|
-
async def _invoke_refresh_callback(
|
|
64
|
-
refresh_callback: RefreshCallback, context: TokenRefreshContext
|
|
65
|
-
) -> None:
|
|
66
|
-
try:
|
|
67
|
-
has_params = bool(inspect.signature(refresh_callback).parameters)
|
|
68
|
-
except TypeError:
|
|
69
|
-
has_params = False
|
|
70
|
-
except ValueError:
|
|
71
|
-
has_params = False
|
|
72
|
-
if has_params:
|
|
73
|
-
await cast("Callable[[TokenRefreshContext], Awaitable[None]]", refresh_callback)(context)
|
|
74
|
-
return
|
|
75
|
-
await cast("Callable[[], Awaitable[None]]", refresh_callback)()
|
|
76
|
-
|
|
77
|
-
|
|
78
62
|
def _parse_varint(data: bytes, pos: int) -> tuple[int, int]:
|
|
79
63
|
"""Parse a protobuf varint from raw bytes."""
|
|
80
64
|
result, shift = 0, 0
|
|
@@ -624,7 +608,7 @@ class NotifierStream:
|
|
|
624
608
|
source="streaming",
|
|
625
609
|
attempt=attempt + 1,
|
|
626
610
|
)
|
|
627
|
-
await
|
|
611
|
+
await invoke_refresh_callback(self._authenticate, context)
|
|
628
612
|
except Exception:
|
|
629
613
|
logger.exception("Token refresh failed; giving up stream")
|
|
630
614
|
self._error = exc
|
|
@@ -7,13 +7,20 @@ protocol. Persistence is the caller's responsibility — the CLI provides
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import inspect
|
|
10
11
|
import time
|
|
12
|
+
import weakref
|
|
13
|
+
from collections.abc import Awaitable, Callable
|
|
11
14
|
from dataclasses import dataclass
|
|
12
15
|
from enum import StrEnum
|
|
13
|
-
from typing import Protocol
|
|
16
|
+
from typing import Protocol, cast
|
|
14
17
|
|
|
15
18
|
_TOKEN_BUFFER_S = 300 # treat tokens as expired 5 min before actual expiry
|
|
16
19
|
|
|
20
|
+
# Cache whether a refresh callback accepts a TokenRefreshContext argument,
|
|
21
|
+
# so inspect.signature is only called once per unique callable.
|
|
22
|
+
_REFRESH_CALLBACK_HAS_PARAMS: weakref.WeakKeyDictionary[object, bool] = weakref.WeakKeyDictionary()
|
|
23
|
+
|
|
17
24
|
|
|
18
25
|
@dataclass(slots=True)
|
|
19
26
|
class CachedTokens:
|
|
@@ -117,3 +124,36 @@ class TokenRefreshPolicy(Protocol):
|
|
|
117
124
|
) -> RefreshFailureAction:
|
|
118
125
|
"""Return fallback strategy when refresh fails."""
|
|
119
126
|
...
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
type _RefreshCallback = (
|
|
130
|
+
Callable[[], Awaitable[None]] | Callable[[TokenRefreshContext], Awaitable[None]]
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
async def invoke_refresh_callback(
|
|
135
|
+
refresh_callback: _RefreshCallback, context: TokenRefreshContext
|
|
136
|
+
) -> None:
|
|
137
|
+
"""Invoke a refresh callback, passing context only if it accepts a parameter.
|
|
138
|
+
|
|
139
|
+
Whether each callback accepts a ``TokenRefreshContext`` argument is cached
|
|
140
|
+
per-callable in a WeakKeyDictionary so that ``inspect.signature`` is only
|
|
141
|
+
called once per unique callback object.
|
|
142
|
+
"""
|
|
143
|
+
try:
|
|
144
|
+
has_params = _REFRESH_CALLBACK_HAS_PARAMS.get(refresh_callback)
|
|
145
|
+
except TypeError:
|
|
146
|
+
has_params = None # non-weakrefable callable — skip cache
|
|
147
|
+
if has_params is None:
|
|
148
|
+
try:
|
|
149
|
+
has_params = bool(inspect.signature(refresh_callback).parameters)
|
|
150
|
+
except TypeError, ValueError:
|
|
151
|
+
has_params = False
|
|
152
|
+
try:
|
|
153
|
+
_REFRESH_CALLBACK_HAS_PARAMS[refresh_callback] = has_params
|
|
154
|
+
except TypeError:
|
|
155
|
+
pass # non-weakrefable callable — skip caching
|
|
156
|
+
if has_params:
|
|
157
|
+
await cast("Callable[[TokenRefreshContext], Awaitable[None]]", refresh_callback)(context)
|
|
158
|
+
return
|
|
159
|
+
await cast("Callable[[], Awaitable[None]]", refresh_callback)()
|
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import inspect
|
|
6
5
|
import logging
|
|
7
|
-
import weakref
|
|
8
6
|
from collections.abc import Awaitable, Callable
|
|
9
|
-
from typing import cast
|
|
10
7
|
|
|
11
8
|
import grpc
|
|
12
9
|
import grpc.aio
|
|
@@ -18,7 +15,12 @@ from quilt_hp.const import (
|
|
|
18
15
|
grpc_host,
|
|
19
16
|
)
|
|
20
17
|
from quilt_hp.exceptions import QuiltAuthError
|
|
21
|
-
from quilt_hp.tokens import
|
|
18
|
+
from quilt_hp.tokens import (
|
|
19
|
+
CurrentTokenProvider,
|
|
20
|
+
TokenRefreshContext,
|
|
21
|
+
TokenRefreshReason,
|
|
22
|
+
invoke_refresh_callback,
|
|
23
|
+
)
|
|
22
24
|
|
|
23
25
|
type RefreshCallback = (
|
|
24
26
|
Callable[[], Awaitable[None]] | Callable[[TokenRefreshContext], Awaitable[None]]
|
|
@@ -26,7 +28,6 @@ type RefreshCallback = (
|
|
|
26
28
|
type TokenProviderLike = Callable[[], str] | CurrentTokenProvider
|
|
27
29
|
|
|
28
30
|
logger = logging.getLogger(__name__)
|
|
29
|
-
_REFRESH_CALLBACK_HAS_PARAMS: weakref.WeakKeyDictionary[object, bool] = weakref.WeakKeyDictionary()
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
def _resolve_token_provider(token_provider: TokenProviderLike) -> Callable[[], str]:
|
|
@@ -35,28 +36,6 @@ def _resolve_token_provider(token_provider: TokenProviderLike) -> Callable[[], s
|
|
|
35
36
|
return token_provider.get_current_token
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
async def _invoke_refresh_callback(
|
|
39
|
-
refresh_callback: RefreshCallback, context: TokenRefreshContext
|
|
40
|
-
) -> None:
|
|
41
|
-
try:
|
|
42
|
-
has_params = _REFRESH_CALLBACK_HAS_PARAMS.get(refresh_callback)
|
|
43
|
-
except TypeError:
|
|
44
|
-
has_params = None # non-weakrefable callable — skip cache
|
|
45
|
-
if has_params is None:
|
|
46
|
-
try:
|
|
47
|
-
has_params = bool(inspect.signature(refresh_callback).parameters)
|
|
48
|
-
except TypeError, ValueError:
|
|
49
|
-
has_params = False
|
|
50
|
-
try:
|
|
51
|
-
_REFRESH_CALLBACK_HAS_PARAMS[refresh_callback] = has_params
|
|
52
|
-
except TypeError:
|
|
53
|
-
pass # non-weakrefable callable — skip caching
|
|
54
|
-
if has_params:
|
|
55
|
-
await cast("Callable[[TokenRefreshContext], Awaitable[None]]", refresh_callback)(context)
|
|
56
|
-
return
|
|
57
|
-
await cast("Callable[[], Awaitable[None]]", refresh_callback)()
|
|
58
|
-
|
|
59
|
-
|
|
60
39
|
class _AuthInterceptor(
|
|
61
40
|
grpc.aio.UnaryUnaryClientInterceptor, # type: ignore[misc]
|
|
62
41
|
grpc.aio.UnaryStreamClientInterceptor, # type: ignore[misc]
|
|
@@ -109,7 +88,7 @@ class _AuthInterceptor(
|
|
|
109
88
|
or the credentials are otherwise invalid.
|
|
110
89
|
"""
|
|
111
90
|
if self._refresh_callback is not None:
|
|
112
|
-
await
|
|
91
|
+
await invoke_refresh_callback(
|
|
113
92
|
self._refresh_callback,
|
|
114
93
|
TokenRefreshContext(
|
|
115
94
|
reason=TokenRefreshReason.TRANSPORT_UNAUTHENTICATED,
|
|
@@ -16,7 +16,7 @@ runner = CliRunner()
|
|
|
16
16
|
def test_version_option_outputs_package_version() -> None:
|
|
17
17
|
result = runner.invoke(cli_main.app, ["--version"])
|
|
18
18
|
assert result.exit_code == 0
|
|
19
|
-
assert result.stdout.strip() == "0.5.
|
|
19
|
+
assert result.stdout.strip() == "0.5.1"
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class _FakeClient:
|
|
@@ -12,10 +12,10 @@ from quilt_hp.services.streaming import (
|
|
|
12
12
|
NotifierStream,
|
|
13
13
|
_dispatch,
|
|
14
14
|
_get_len_field,
|
|
15
|
-
_invoke_refresh_callback,
|
|
16
15
|
_parse_varint,
|
|
17
16
|
)
|
|
18
17
|
from quilt_hp.tokens import TokenRefreshContext, TokenRefreshReason
|
|
18
|
+
from quilt_hp.tokens import invoke_refresh_callback as _invoke_refresh_callback
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class _FakeRpcError(grpc.aio.AioRpcError):
|
|
@@ -6,7 +6,7 @@ import pytest
|
|
|
6
6
|
|
|
7
7
|
from quilt_hp import transport
|
|
8
8
|
from quilt_hp.const import APP_VERSION, Environment, grpc_host
|
|
9
|
-
from quilt_hp.tokens import TokenRefreshContext, TokenRefreshReason
|
|
9
|
+
from quilt_hp.tokens import TokenRefreshContext, TokenRefreshReason, invoke_refresh_callback
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def test_grpc_host_prod() -> None:
|
|
@@ -45,7 +45,7 @@ async def test_invoke_refresh_callback_passes_context() -> None:
|
|
|
45
45
|
reason=TokenRefreshReason.TRANSPORT_UNAUTHENTICATED,
|
|
46
46
|
source="test",
|
|
47
47
|
)
|
|
48
|
-
await
|
|
48
|
+
await invoke_refresh_callback(_with_context, context)
|
|
49
49
|
assert captured == [context]
|
|
50
50
|
|
|
51
51
|
|
|
@@ -60,5 +60,5 @@ async def test_invoke_refresh_callback_supports_legacy_signature() -> None:
|
|
|
60
60
|
reason=TokenRefreshReason.TRANSPORT_UNAUTHENTICATED,
|
|
61
61
|
source="test",
|
|
62
62
|
)
|
|
63
|
-
await
|
|
63
|
+
await invoke_refresh_callback(_legacy, context)
|
|
64
64
|
assert calls == ["called"]
|
|
@@ -6,7 +6,7 @@ from unittest.mock import MagicMock
|
|
|
6
6
|
import grpc
|
|
7
7
|
import pytest
|
|
8
8
|
|
|
9
|
-
from quilt_hp import transport
|
|
9
|
+
from quilt_hp import tokens, transport
|
|
10
10
|
from quilt_hp.const import Environment
|
|
11
11
|
from quilt_hp.exceptions import QuiltAuthError
|
|
12
12
|
|
|
@@ -32,12 +32,12 @@ async def test_invoke_refresh_callback_handles_signature_fallback(
|
|
|
32
32
|
async def _legacy() -> None:
|
|
33
33
|
called.append("legacy")
|
|
34
34
|
|
|
35
|
-
monkeypatch.setattr(
|
|
35
|
+
monkeypatch.setattr(tokens.inspect, "signature", MagicMock(side_effect=TypeError("bad")))
|
|
36
36
|
|
|
37
|
-
await
|
|
37
|
+
await tokens.invoke_refresh_callback(
|
|
38
38
|
_legacy,
|
|
39
|
-
|
|
40
|
-
reason=
|
|
39
|
+
tokens.TokenRefreshContext(
|
|
40
|
+
reason=tokens.TokenRefreshReason.TRANSPORT_UNAUTHENTICATED,
|
|
41
41
|
source="test",
|
|
42
42
|
),
|
|
43
43
|
)
|
|
@@ -48,10 +48,10 @@ async def test_invoke_refresh_callback_handles_signature_fallback(
|
|
|
48
48
|
async def test_invoke_refresh_callback_caches_signature(
|
|
49
49
|
monkeypatch: pytest.MonkeyPatch,
|
|
50
50
|
) -> None:
|
|
51
|
-
|
|
52
|
-
called: list[
|
|
51
|
+
tokens._REFRESH_CALLBACK_HAS_PARAMS.clear()
|
|
52
|
+
called: list[tokens.TokenRefreshContext] = []
|
|
53
53
|
|
|
54
|
-
async def _with_context(context:
|
|
54
|
+
async def _with_context(context: tokens.TokenRefreshContext) -> None:
|
|
55
55
|
called.append(context)
|
|
56
56
|
|
|
57
57
|
signature = inspect.Signature(
|
|
@@ -63,14 +63,14 @@ async def test_invoke_refresh_callback_caches_signature(
|
|
|
63
63
|
]
|
|
64
64
|
)
|
|
65
65
|
inspect_signature = MagicMock(return_value=signature)
|
|
66
|
-
monkeypatch.setattr(
|
|
66
|
+
monkeypatch.setattr(tokens.inspect, "signature", inspect_signature)
|
|
67
67
|
|
|
68
|
-
context =
|
|
69
|
-
reason=
|
|
68
|
+
context = tokens.TokenRefreshContext(
|
|
69
|
+
reason=tokens.TokenRefreshReason.TRANSPORT_UNAUTHENTICATED,
|
|
70
70
|
source="test",
|
|
71
71
|
)
|
|
72
|
-
await
|
|
73
|
-
await
|
|
72
|
+
await tokens.invoke_refresh_callback(_with_context, context)
|
|
73
|
+
await tokens.invoke_refresh_callback(_with_context, context)
|
|
74
74
|
|
|
75
75
|
assert called == [context, context]
|
|
76
76
|
assert inspect_signature.call_count == 1
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def lookup_hardware(hw_map: dict[str, object], hardware_id: str | None) -> object | None:
|
|
5
|
-
"""Resolve hardware objects across common ID formats."""
|
|
6
|
-
if not hardware_id:
|
|
7
|
-
return None
|
|
8
|
-
raw = hardware_id.strip()
|
|
9
|
-
if not raw:
|
|
10
|
-
return None
|
|
11
|
-
keys = (
|
|
12
|
-
raw,
|
|
13
|
-
raw.rsplit("/", 1)[-1],
|
|
14
|
-
raw.rsplit(":", 1)[-1],
|
|
15
|
-
raw.casefold(),
|
|
16
|
-
raw.rsplit("/", 1)[-1].casefold(),
|
|
17
|
-
raw.rsplit(":", 1)[-1].casefold(),
|
|
18
|
-
)
|
|
19
|
-
for key in keys:
|
|
20
|
-
hw = hw_map.get(key)
|
|
21
|
-
if hw is not None:
|
|
22
|
-
return hw
|
|
23
|
-
return None
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def parse_wifi_state(proto: object) -> tuple[str | None, str | None, int | None]:
|
|
27
|
-
"""Extract WiFi fields while preserving explicit zero signal values."""
|
|
28
|
-
ssid = getattr(proto, "ssid", "") or None
|
|
29
|
-
ip = getattr(proto, "ipv4_address", None) or None
|
|
30
|
-
signal = getattr(proto, "signal_level_dbm", None)
|
|
31
|
-
return ssid, ip, signal if signal is not None else None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_device_pairing_pb2.py
RENAMED
|
File without changes
|
{quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_device_pairing_pb2.pyi
RENAMED
|
File without changes
|
{quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_device_pairing_pb2_grpc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_notifier_pb2_grpc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_services_pb2_grpc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/src/quilt_hp/_proto/quilt_system_pb2_grpc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quilt_hp_python-0.5.0 → quilt_hp_python-0.5.1}/tests/test_streaming_reconnect_dispatch_extra.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|