quilt-hp-python 0.5.4__tar.gz → 0.5.5__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 (122) hide show
  1. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/CHANGELOG.md +2 -0
  2. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/PKG-INFO +1 -1
  3. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/reference/client.md +1 -1
  4. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/pyproject.toml +1 -1
  5. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/__init__.py +1 -1
  6. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/transport.py +2 -2
  7. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_cli_surfaces_extra.py +1 -1
  8. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_transport_interceptor_extra.py +82 -0
  9. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/.github/copilot-instructions.md +0 -0
  10. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/.github/workflows/ci.yml +0 -0
  11. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/.github/workflows/docs-deploy.yml +0 -0
  12. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/.github/workflows/release.yml +0 -0
  13. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/.gitignore +0 -0
  14. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/LICENSE +0 -0
  15. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/README.md +0 -0
  16. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/explanation/architecture.md +0 -0
  17. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/explanation/authentication.md +0 -0
  18. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/explanation/grpc-and-protobuf.md +0 -0
  19. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/explanation/snapshot-and-stream.md +0 -0
  20. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/explanation/streaming-protocol.md +0 -0
  21. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/how-to/authenticate.md +0 -0
  22. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/how-to/automation-daemon.md +0 -0
  23. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/how-to/cli-scripting.md +0 -0
  24. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/how-to/configure-comfort-settings.md +0 -0
  25. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/how-to/configure-schedules.md +0 -0
  26. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/how-to/contribute.md +0 -0
  27. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/how-to/control-spaces.md +0 -0
  28. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/how-to/home-assistant.md +0 -0
  29. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/how-to/regenerate-protos.md +0 -0
  30. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/how-to/stream-updates.md +0 -0
  31. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/how-to/tui-app.md +0 -0
  32. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/index.md +0 -0
  33. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/reference/api-reference.md +0 -0
  34. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/reference/documentation-standards.md +0 -0
  35. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/reference/grpc-services-matrix.md +0 -0
  36. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/reference/hds-entities.md +0 -0
  37. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/reference/models.md +0 -0
  38. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/reference/token-management.md +0 -0
  39. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/docs/tutorial/get-started.md +0 -0
  40. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/mkdocs.yml +0 -0
  41. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/proto/cleaned/quilt_device_pairing.proto +0 -0
  42. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/proto/cleaned/quilt_hds.proto +0 -0
  43. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/proto/cleaned/quilt_notifier.proto +0 -0
  44. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/proto/cleaned/quilt_services.proto +0 -0
  45. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/proto/cleaned/quilt_system.proto +0 -0
  46. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/scripts/bump_version.py +0 -0
  47. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/scripts/check_docs_nav.py +0 -0
  48. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/scripts/generate_public_api_reference.py +0 -0
  49. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/scripts/regen_protos.sh +0 -0
  50. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_paths.py +0 -0
  51. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/__init__.py +0 -0
  52. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_device_pairing_pb2.py +0 -0
  53. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_device_pairing_pb2.pyi +0 -0
  54. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_device_pairing_pb2_grpc.py +0 -0
  55. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_hds_pb2.py +0 -0
  56. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_hds_pb2.pyi +0 -0
  57. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_hds_pb2_grpc.py +0 -0
  58. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_notifier_pb2.py +0 -0
  59. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_notifier_pb2.pyi +0 -0
  60. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_notifier_pb2_grpc.py +0 -0
  61. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_services_pb2.py +0 -0
  62. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_services_pb2.pyi +0 -0
  63. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_services_pb2_grpc.py +0 -0
  64. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_system_pb2.py +0 -0
  65. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_system_pb2.pyi +0 -0
  66. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/_proto/quilt_system_pb2_grpc.py +0 -0
  67. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/auth.py +0 -0
  68. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/cli/__init__.py +0 -0
  69. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/cli/constants.py +0 -0
  70. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/cli/main.py +0 -0
  71. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/cli/settings.py +0 -0
  72. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/cli/store.py +0 -0
  73. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/cli/tui.py +0 -0
  74. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/client.py +0 -0
  75. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/const.py +0 -0
  76. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/exceptions.py +0 -0
  77. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/__init__.py +0 -0
  78. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/_helpers.py +0 -0
  79. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/comfort.py +0 -0
  80. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/controller.py +0 -0
  81. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/energy.py +0 -0
  82. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/enums.py +0 -0
  83. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/indoor_unit.py +0 -0
  84. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/outdoor_unit.py +0 -0
  85. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/qsm.py +0 -0
  86. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/schedule.py +0 -0
  87. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/sensor.py +0 -0
  88. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/software_update.py +0 -0
  89. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/space.py +0 -0
  90. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/models/system.py +0 -0
  91. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/py.typed +0 -0
  92. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/services/__init__.py +0 -0
  93. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/services/hds.py +0 -0
  94. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/services/streaming.py +0 -0
  95. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/services/system.py +0 -0
  96. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/services/user.py +0 -0
  97. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/src/quilt_hp/tokens.py +0 -0
  98. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/__init__.py +0 -0
  99. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/conftest.py +0 -0
  100. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_auth.py +0 -0
  101. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_auth_store_settings_edges.py +0 -0
  102. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_cli_feature_completion.py +0 -0
  103. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_cli_login.py +0 -0
  104. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_client_cache.py +0 -0
  105. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_client_service_error_paths.py +0 -0
  106. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_grpc_retry.py +0 -0
  107. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_hds_payloads.py +0 -0
  108. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_hds_schedule_mapping.py +0 -0
  109. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_hds_service_branches.py +0 -0
  110. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_models.py +0 -0
  111. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_models_extra.py +0 -0
  112. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_models_from_proto.py +0 -0
  113. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_models_real_proto_merge.py +0 -0
  114. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_settings_store.py +0 -0
  115. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_streaming.py +0 -0
  116. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_streaming_concurrency.py +0 -0
  117. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_streaming_debounce.py +0 -0
  118. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_streaming_health.py +0 -0
  119. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_streaming_reconnect_dispatch_extra.py +0 -0
  120. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_tokens.py +0 -0
  121. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_transport.py +0 -0
  122. {quilt_hp_python-0.5.4 → quilt_hp_python-0.5.5}/tests/test_tui_bindings.py +0 -0
@@ -2,6 +2,8 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.5.5] - 2026-07-03
6
+
5
7
  ## [0.5.4] - 2026-07-03
6
8
 
7
9
  Fixes all findings from a full architecture/code/performance/bug-hunt evaluation.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quilt-hp-python
3
- Version: 0.5.4
3
+ Version: 0.5.5
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
@@ -69,7 +69,7 @@ Raised when a requested resource does not exist (gRPC `NOT_FOUND`).
69
69
  ### `__version__`
70
70
 
71
71
  ```python
72
- __version__: str # e.g. "0.5.4"
72
+ __version__: str # e.g. "0.5.5"
73
73
  ```
74
74
 
75
75
  ---
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "quilt-hp-python"
7
- version = "0.5.4"
7
+ version = "0.5.5"
8
8
  description = "Async Python client for Quilt mini-split HVAC systems"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -22,7 +22,7 @@ from quilt_hp.tokens import (
22
22
  TokenStore,
23
23
  )
24
24
 
25
- __version__ = "0.5.4"
25
+ __version__ = "0.5.5"
26
26
 
27
27
  __all__ = [
28
28
  "CachedTokens",
@@ -135,7 +135,7 @@ class _AuthInterceptor(
135
135
  return await cast("Awaitable[object]", call)
136
136
  except grpc.aio.AioRpcError as exc:
137
137
  if exc.code() == grpc.StatusCode.UNAUTHENTICATED and self._refresh_callback:
138
- logger.warning("Retrying unary RPC after UNAUTHENTICATED response")
138
+ logger.info("Retrying unary RPC after UNAUTHENTICATED response")
139
139
  return await self._refresh_and_retry_unary(
140
140
  continuation, client_call_details, request
141
141
  )
@@ -155,7 +155,7 @@ class _AuthInterceptor(
155
155
  await cast("Any", call).wait_for_connection()
156
156
  except grpc.aio.AioRpcError as exc:
157
157
  if exc.code() == grpc.StatusCode.UNAUTHENTICATED and self._refresh_callback:
158
- logger.warning("Retrying streaming RPC setup after UNAUTHENTICATED response")
158
+ logger.info("Retrying streaming RPC setup after UNAUTHENTICATED response")
159
159
  await self._refresh()
160
160
  retried = await continuation(self._patch(client_call_details), request)
161
161
  try:
@@ -17,7 +17,7 @@ runner = CliRunner()
17
17
  def test_version_option_outputs_package_version() -> None:
18
18
  result = runner.invoke(cli_main.app, ["--version"])
19
19
  assert result.exit_code == 0
20
- assert result.stdout.strip() == "0.5.4"
20
+ assert result.stdout.strip() == "0.5.5"
21
21
 
22
22
 
23
23
  class _FakeClient:
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import inspect
4
+ import logging
4
5
  from unittest.mock import MagicMock
5
6
 
6
7
  import grpc
@@ -136,6 +137,87 @@ async def test_auth_interceptor_retry_paths() -> None:
136
137
  assert refreshed == ["yes"]
137
138
 
138
139
 
140
+ @pytest.mark.asyncio
141
+ async def test_auth_interceptor_logs_unary_retry_at_info(
142
+ caplog: pytest.LogCaptureFixture,
143
+ ) -> None:
144
+ refreshed: list[str] = []
145
+
146
+ async def _refresh(_context: transport.TokenRefreshContext) -> None:
147
+ refreshed.append("yes")
148
+
149
+ interceptor = transport._AuthInterceptor(lambda: "******", refresh_callback=_refresh)
150
+ details = grpc.aio.ClientCallDetails(
151
+ method="/svc/method",
152
+ timeout=1,
153
+ metadata=None,
154
+ credentials=None,
155
+ wait_for_ready=False,
156
+ )
157
+
158
+ calls = 0
159
+
160
+ async def _continuation(_call_details: grpc.aio.ClientCallDetails, request: object) -> object:
161
+ nonlocal calls
162
+ calls += 1
163
+ if calls == 1:
164
+ return _FakeCall(error=_FakeRpcError(grpc.StatusCode.UNAUTHENTICATED, "expired"))
165
+ return _FakeCall(result=request)
166
+
167
+ with caplog.at_level(logging.INFO):
168
+ assert await interceptor.intercept_unary_unary(_continuation, details, "req") == "req"
169
+
170
+ matching = [
171
+ record
172
+ for record in caplog.records
173
+ if record.getMessage() == "Retrying unary RPC after UNAUTHENTICATED response"
174
+ ]
175
+ assert matching
176
+ assert all(record.levelno == logging.INFO for record in matching)
177
+ assert refreshed == ["yes"]
178
+
179
+
180
+ @pytest.mark.asyncio
181
+ async def test_auth_interceptor_logs_stream_setup_retry_at_info(
182
+ caplog: pytest.LogCaptureFixture,
183
+ ) -> None:
184
+ refreshed: list[str] = []
185
+
186
+ async def _refresh(_context: transport.TokenRefreshContext) -> None:
187
+ refreshed.append("yes")
188
+
189
+ interceptor = transport._AuthInterceptor(lambda: "******", refresh_callback=_refresh)
190
+ details = grpc.aio.ClientCallDetails(
191
+ method="/svc/method",
192
+ timeout=1,
193
+ metadata=None,
194
+ credentials=None,
195
+ wait_for_ready=False,
196
+ )
197
+
198
+ calls = 0
199
+
200
+ async def _continuation(_call_details: grpc.aio.ClientCallDetails, request: object) -> object:
201
+ nonlocal calls
202
+ calls += 1
203
+ if calls == 1:
204
+ return _FakeCall(error=_FakeRpcError(grpc.StatusCode.UNAUTHENTICATED, "expired"))
205
+ return _FakeCall(result=request)
206
+
207
+ with caplog.at_level(logging.INFO):
208
+ stream_call = await interceptor.intercept_unary_stream(_continuation, details, "req")
209
+
210
+ matching = [
211
+ record
212
+ for record in caplog.records
213
+ if record.getMessage() == "Retrying streaming RPC setup after UNAUTHENTICATED response"
214
+ ]
215
+ assert matching
216
+ assert all(record.levelno == logging.INFO for record in matching)
217
+ assert await stream_call == "req" # type: ignore[misc]
218
+ assert refreshed == ["yes"]
219
+
220
+
139
221
  @pytest.mark.asyncio
140
222
  async def test_auth_interceptor_non_retry_paths() -> None:
141
223
  interceptor = transport._AuthInterceptor(lambda: "Bearer abc")
File without changes