span-panel-api 2.5.1__tar.gz → 2.5.3__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.
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/CHANGELOG.md +45 -21
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/PKG-INFO +1 -1
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/pyproject.toml +1 -1
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/accumulator.py +44 -4
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/homie.py +7 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_accumulator.py +131 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_mqtt_homie.py +28 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/uv.lock +41 -41
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.codefactor +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.codefactor.yml +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.deps-installed +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.github/dependabot.yml +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.github/workflows/ci.yml +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.github/workflows/dependabot-auto-approve.yml +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.github/workflows/dependabot-auto-merge.yml +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.github/workflows/release.yml +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.gitignore +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.markdownlint-cli2.jsonc +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.markdownlint.json +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.pre-commit-config.yaml +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.prettierrc.json +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.vscode/extensions.json +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/.vscode/tasks.json +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/DEVELOPMENT.md +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/LICENSE +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/README.md +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/SECURITY.md +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/conftest.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/developer_attribute_readme.md +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/openapi.json +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/pytest.ini +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/pytest_output.log +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/scripts/__init__.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/scripts/coverage.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/scripts/format.sh +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/scripts/format_markdown.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/scripts/test_live_auth.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/setup-hooks.sh +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/__init__.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/_http.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/auth.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/const.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/detection.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/exceptions.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/factory.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/models.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/__init__.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/async_client.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/client.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/connection.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/const.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/field_metadata.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/models.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/phase_validation.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/protocol.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/src/span_panel_api/py.typed +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/conftest.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/fixtures/configs/simulation_config_32_circuit.yaml +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/fixtures/configs/simulation_config_40_circuit_with_battery.yaml +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/fixtures/configs/simulation_config_8_tab_workshop.yaml +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/fixtures/v2/README.md +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/fixtures/v2/homie_schema.json +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/fixtures/v2/status.json +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/simulation_fixtures/circuits.response.txt +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/simulation_fixtures/panel.response.txt +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/simulation_fixtures/soe.response.txt +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/simulation_fixtures/status.response.txt +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_async_mqtt_client.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_auth_and_homie_helpers.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_detection_auth.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_field_metadata.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_mqtt_bridge.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_mqtt_connect_flow.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_mqtt_debounce.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_phase_validation_configs.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_phase_validation_errors.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_protocol_conformance.py +0 -0
- {span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/test_protocol_models.py +0 -0
|
@@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [2.5.3] - 04/2026
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Preserve property values on lifecycle reset** — the 2.5.2 property clear caused `_parse_float('')` to return `0.0` for energy counters during panel reboots or network interruptions, triggering false dip-compensation offsets in the integration that
|
|
12
|
+
permanently inflated energy sensor values. Removed the property/timestamp/target clearing from `_handle_description()`. Pre-reboot values now serve as safe placeholders until the panel re-publishes fresh data.
|
|
13
|
+
- **Snapshot cache invalidated on reboot** — the generation counter still increments on lifecycle resets, forcing consumers to discard cached snapshots and rebuild from current accumulator state.
|
|
14
|
+
|
|
15
|
+
## [2.5.2] - 04/2026 (retired)
|
|
16
|
+
|
|
17
|
+
> **Retired:** The property-clearing behavior introduced in this release caused false energy dip spikes. Superseded by 2.5.3.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Clear stale property values on panel reboot** — after a panel reboot, snapshots could mix pre-reboot and post-reboot data. The accumulator now detects reboots (including fast reboots where the broker LWT is skipped) and clears stale state before
|
|
22
|
+
building the next snapshot.
|
|
23
|
+
- **Snapshot cache invalidated on reboot** — the snapshot cache is now discarded when a reboot is detected, forcing a full rebuild from fresh data.
|
|
24
|
+
|
|
7
25
|
## [2.5.1] - 04/2026
|
|
8
26
|
|
|
9
27
|
### Fixed
|
|
@@ -326,24 +344,30 @@ Package versions prior to 2.0.0 depend on the SPAN v1 REST API. SPAN will sunset
|
|
|
326
344
|
|
|
327
345
|
## Version History Summary
|
|
328
346
|
|
|
329
|
-
| Version | Date | Transport | Summary
|
|
330
|
-
| ---------- | ------- | ---------- |
|
|
331
|
-
| **2.
|
|
332
|
-
| **2.
|
|
333
|
-
| **2.
|
|
334
|
-
| **2.
|
|
335
|
-
| **2.2
|
|
336
|
-
| **2.
|
|
337
|
-
| **2.0
|
|
338
|
-
| **2.
|
|
339
|
-
| **2.
|
|
340
|
-
| **
|
|
341
|
-
| **
|
|
342
|
-
| **
|
|
343
|
-
| **
|
|
344
|
-
| **
|
|
345
|
-
| **
|
|
346
|
-
| **1.1.
|
|
347
|
-
| **1.1.
|
|
348
|
-
| **1.1.
|
|
349
|
-
| **1.1.
|
|
347
|
+
| Version | Date | Transport | Summary |
|
|
348
|
+
| ---------- | ------- | ---------- | ----------------------------------------------------------------------------------------- |
|
|
349
|
+
| **2.5.3** | 04/2026 | MQTT/Homie | Preserve property values on lifecycle reset; fix false energy dip spikes from 2.5.2 clear |
|
|
350
|
+
| **2.5.2** | 04/2026 | MQTT/Homie | _(retired)_ Clear stale property values on panel reboot; caused false energy dip spikes |
|
|
351
|
+
| **2.5.1** | 04/2026 | MQTT/Homie | Replace assert with RuntimeError; fix bandit pre-commit hook |
|
|
352
|
+
| **2.5.0** | 03/2026 | MQTT/Homie | Homie accumulator layer, $target support, dirty-node snapshot caching |
|
|
353
|
+
| **2.4.2** | 03/2026 | MQTT/Homie | SSL context creation moved to executor |
|
|
354
|
+
| **2.4.1** | 03/2026 | MQTT/Homie | License metadata, loosened httpx constraint |
|
|
355
|
+
| **2.4.0** | 03/2026 | MQTT/Homie | proximityProven, injected HTTP client, executor file I/O, type alias, test cleanup |
|
|
356
|
+
| **2.3.2** | 03/2026 | MQTT/Homie | FQDN management endpoints |
|
|
357
|
+
| **2.3.1** | 03/2026 | MQTT/Homie | MQTT connection errors wrapped as SpanPanelConnectionError |
|
|
358
|
+
| **2.3.0** | 03/2026 | MQTT/Homie | Simulation engine removed |
|
|
359
|
+
| **2.2.4** | 03/2026 | MQTT/Homie | Negative zero fix on idle circuits |
|
|
360
|
+
| **2.2.3** | 03/2026 | MQTT/Homie | Panel size from Homie schema; `panel_size` always populated on snapshot |
|
|
361
|
+
| **2.0.2** | 03/2026 | MQTT/Homie | EVSE (EV charger) snapshot model, Homie parsing, simulation support |
|
|
362
|
+
| **2.0.1** | 03/2026 | MQTT/Homie | Full BESS metadata parsing, README documentation |
|
|
363
|
+
| **2.0.0** | 02/2026 | MQTT/Homie | Ground-up rewrite: MQTT-only, protocol-based API, real-time push, PV/BESS metadata |
|
|
364
|
+
| **1.1.14** | 12/2025 | REST | Keep-Alive and RemoteProtocolError handling |
|
|
365
|
+
| **1.1.9** | 9/2025 | REST | Simulation sign corrections |
|
|
366
|
+
| **1.1.8** | 2024 | REST | Simulation power sign fix |
|
|
367
|
+
| **1.1.6** | 2024 | REST | YAML simulation API, battery simulation |
|
|
368
|
+
| **1.1.5** | 2024 | REST | Simulation edge cases |
|
|
369
|
+
| **1.1.4** | 2024 | REST | Formatting and linting |
|
|
370
|
+
| **1.1.3** | 2024 | REST | Test and lint fixes |
|
|
371
|
+
| **1.1.2** | 2024 | REST | Simulation mode added |
|
|
372
|
+
| **1.1.1** | 2024 | REST | Dependency updates |
|
|
373
|
+
| **1.1.0** | 2024 | REST | Initial release |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: span-panel-api
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.3
|
|
4
4
|
Summary: A client library for SPAN Panel API
|
|
5
5
|
Project-URL: Homepage, https://github.com/SpanPanel/span-panel-api
|
|
6
6
|
Project-URL: Issues, https://github.com/SpanPanel/span-panel-api/issues
|
|
@@ -56,6 +56,11 @@ class HomiePropertyAccumulator:
|
|
|
56
56
|
# Node type mapping from $description
|
|
57
57
|
self._node_types: dict[str, str] = {}
|
|
58
58
|
|
|
59
|
+
# Generation counter — incremented whenever a new lifecycle's
|
|
60
|
+
# $description is accepted (including initial boot) so consumers
|
|
61
|
+
# can invalidate snapshot caches built from prior-lifecycle data.
|
|
62
|
+
self._generation: int = 0
|
|
63
|
+
|
|
59
64
|
# Dirty tracking
|
|
60
65
|
self._dirty_nodes: set[str] = set()
|
|
61
66
|
|
|
@@ -79,6 +84,11 @@ class HomiePropertyAccumulator:
|
|
|
79
84
|
"""Monotonic timestamp of the last READY transition, 0.0 if never ready."""
|
|
80
85
|
return self._ready_since
|
|
81
86
|
|
|
87
|
+
@property
|
|
88
|
+
def generation(self) -> int:
|
|
89
|
+
"""Counter incremented on each new lifecycle's ``$description`` to invalidate consumer caches."""
|
|
90
|
+
return self._generation
|
|
91
|
+
|
|
82
92
|
def is_ready(self) -> bool:
|
|
83
93
|
"""True when lifecycle is READY."""
|
|
84
94
|
return self._lifecycle == HomieLifecycle.READY
|
|
@@ -191,10 +201,17 @@ class HomiePropertyAccumulator:
|
|
|
191
201
|
self._received_state_ready = False
|
|
192
202
|
self._received_description = False
|
|
193
203
|
else:
|
|
194
|
-
# init, sleeping, alert, etc. — connected but not ready
|
|
195
|
-
|
|
196
|
-
|
|
204
|
+
# init, sleeping, alert, etc. — connected but not ready.
|
|
205
|
+
# Always move out of READY/DESCRIPTION_RECEIVED into a
|
|
206
|
+
# non-ready connected lifecycle state.
|
|
207
|
+
#
|
|
208
|
+
# Reset _received_description so that the upcoming $description
|
|
209
|
+
# triggers a property clear. This covers fast reboots where
|
|
210
|
+
# the broker's LWT ($state=disconnected) may not reach us
|
|
211
|
+
# before the panel publishes $state=init.
|
|
212
|
+
self._lifecycle = HomieLifecycle.CONNECTED
|
|
197
213
|
self._received_state_ready = False
|
|
214
|
+
self._received_description = False
|
|
198
215
|
|
|
199
216
|
_LOGGER.debug("Homie $state: %s → lifecycle=%s", payload, self._lifecycle.value)
|
|
200
217
|
|
|
@@ -206,6 +223,25 @@ class HomiePropertyAccumulator:
|
|
|
206
223
|
_LOGGER.warning("Invalid $description JSON")
|
|
207
224
|
return
|
|
208
225
|
|
|
226
|
+
# _handle_state() already reset _received_description to False due to
|
|
227
|
+
# a state change that starts a new panel lifecycle, including
|
|
228
|
+
# $state=disconnected/lost and other non-ready states such as init.
|
|
229
|
+
# Increment the generation counter to invalidate consumer snapshot
|
|
230
|
+
# caches, but preserve property values — pre-reboot readings serve
|
|
231
|
+
# as safe placeholders until the panel re-publishes. Clearing values
|
|
232
|
+
# would emit 0.0 for energy counters via _parse_float(""), triggering
|
|
233
|
+
# false dip-compensation offsets in the integration.
|
|
234
|
+
#
|
|
235
|
+
# On a pure MQTT reconnect (no panel reboot), _received_description
|
|
236
|
+
# is still True from the previous session so we skip this block —
|
|
237
|
+
# the retained property messages carry the correct (unchanged) values.
|
|
238
|
+
if not self._received_description:
|
|
239
|
+
self._generation += 1
|
|
240
|
+
_LOGGER.debug(
|
|
241
|
+
"Panel reboot detected (generation %d); preserving property values as placeholders",
|
|
242
|
+
self._generation,
|
|
243
|
+
)
|
|
244
|
+
|
|
209
245
|
self._received_description = True
|
|
210
246
|
self._node_types.clear()
|
|
211
247
|
|
|
@@ -220,7 +256,11 @@ class HomiePropertyAccumulator:
|
|
|
220
256
|
# Mark all known nodes dirty
|
|
221
257
|
self._dirty_nodes.update(self._node_types.keys())
|
|
222
258
|
|
|
223
|
-
_LOGGER.debug(
|
|
259
|
+
_LOGGER.debug(
|
|
260
|
+
"Parsed $description with %d nodes (generation %d)",
|
|
261
|
+
len(self._node_types),
|
|
262
|
+
self._generation,
|
|
263
|
+
)
|
|
224
264
|
|
|
225
265
|
# Lifecycle transition
|
|
226
266
|
if self._received_state_ready:
|
|
@@ -64,6 +64,7 @@ class HomieDeviceConsumer:
|
|
|
64
64
|
self._acc = accumulator
|
|
65
65
|
self._panel_size = panel_size
|
|
66
66
|
self._cached_snapshot: SpanPanelSnapshot | None = None
|
|
67
|
+
self._cache_generation: int = 0
|
|
67
68
|
|
|
68
69
|
# -- Delegation to accumulator -------------------------------------------
|
|
69
70
|
# These thin wrappers allow SpanMqttClient (and legacy test code) to
|
|
@@ -118,6 +119,12 @@ class HomieDeviceConsumer:
|
|
|
118
119
|
|
|
119
120
|
Must be called after accumulator is_ready() returns True.
|
|
120
121
|
"""
|
|
122
|
+
# Invalidate cache when the accumulator generation advances
|
|
123
|
+
# (panel reboot cleared all property values).
|
|
124
|
+
if self._acc.generation != self._cache_generation:
|
|
125
|
+
self._cached_snapshot = None
|
|
126
|
+
self._cache_generation = self._acc.generation
|
|
127
|
+
|
|
121
128
|
dirty = self._acc.dirty_node_ids()
|
|
122
129
|
|
|
123
130
|
if not dirty and self._cached_snapshot is not None:
|
|
@@ -163,6 +163,137 @@ class TestLifecycleDisconnection:
|
|
|
163
163
|
assert acc.lifecycle == HomieLifecycle.CONNECTED
|
|
164
164
|
|
|
165
165
|
|
|
166
|
+
# ---------------------------------------------------------------------------
|
|
167
|
+
# Panel reboot: $description preserves property values as placeholders
|
|
168
|
+
# ---------------------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class TestDescriptionOnReboot:
|
|
172
|
+
"""Verify that a panel reboot preserves property values as placeholders.
|
|
173
|
+
|
|
174
|
+
On lifecycle reset ($state=disconnected/lost or non-ready states like init),
|
|
175
|
+
_received_description is set to False. The subsequent $description increments
|
|
176
|
+
the generation counter (invalidating consumer snapshot caches) but does NOT
|
|
177
|
+
clear property values — pre-reboot values serve as safe placeholders until
|
|
178
|
+
the panel re-publishes fresh data.
|
|
179
|
+
|
|
180
|
+
A re-delivered retained $description on a pure network reconnect does NOT
|
|
181
|
+
increment generation, because _received_description is still True.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
def _simulate_reboot(self, acc: HomiePropertyAccumulator) -> None:
|
|
185
|
+
"""Simulate the panel reboot lifecycle transition."""
|
|
186
|
+
acc.handle_message(f"{PREFIX}/$state", "disconnected")
|
|
187
|
+
acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
|
|
188
|
+
acc.handle_message(f"{PREFIX}/$state", "ready")
|
|
189
|
+
|
|
190
|
+
def test_reboot_preserves_property_values(self):
|
|
191
|
+
"""A panel reboot must preserve property values as placeholders."""
|
|
192
|
+
acc = HomiePropertyAccumulator(SERIAL)
|
|
193
|
+
_make_ready(acc)
|
|
194
|
+
acc.handle_message(f"{PREFIX}/circuit-1/exported-energy", "1000")
|
|
195
|
+
assert acc.get_prop("circuit-1", "exported-energy") == "1000"
|
|
196
|
+
|
|
197
|
+
# Panel reboots — $state=disconnected resets lifecycle, then $description preserves
|
|
198
|
+
self._simulate_reboot(acc)
|
|
199
|
+
assert acc.get_prop("circuit-1", "exported-energy") == "1000"
|
|
200
|
+
|
|
201
|
+
def test_reboot_preserves_timestamps(self):
|
|
202
|
+
"""Timestamps must be preserved on reboot so callers can detect stale data if needed."""
|
|
203
|
+
acc = HomiePropertyAccumulator(SERIAL)
|
|
204
|
+
_make_ready(acc)
|
|
205
|
+
acc.handle_message(f"{PREFIX}/circuit-1/exported-energy", "1000")
|
|
206
|
+
ts_before = acc.get_timestamp("circuit-1", "exported-energy")
|
|
207
|
+
assert ts_before > 0
|
|
208
|
+
|
|
209
|
+
self._simulate_reboot(acc)
|
|
210
|
+
assert acc.get_timestamp("circuit-1", "exported-energy") == ts_before
|
|
211
|
+
|
|
212
|
+
def test_reboot_preserves_target_values(self):
|
|
213
|
+
"""Target values must also be preserved on panel reboot."""
|
|
214
|
+
acc = HomiePropertyAccumulator(SERIAL)
|
|
215
|
+
_make_ready(acc)
|
|
216
|
+
acc.handle_message(f"{PREFIX}/circuit-1/relay/$target", "OPEN")
|
|
217
|
+
assert acc.get_target("circuit-1", "relay") == "OPEN"
|
|
218
|
+
|
|
219
|
+
self._simulate_reboot(acc)
|
|
220
|
+
assert acc.get_target("circuit-1", "relay") == "OPEN"
|
|
221
|
+
|
|
222
|
+
def test_reboot_increments_generation(self):
|
|
223
|
+
"""Each panel reboot must advance the generation counter."""
|
|
224
|
+
acc = HomiePropertyAccumulator(SERIAL)
|
|
225
|
+
assert acc.generation == 0
|
|
226
|
+
|
|
227
|
+
# First boot
|
|
228
|
+
acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
|
|
229
|
+
assert acc.generation == 1
|
|
230
|
+
|
|
231
|
+
# Reboot
|
|
232
|
+
acc.handle_message(f"{PREFIX}/$state", "disconnected")
|
|
233
|
+
acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
|
|
234
|
+
assert acc.generation == 2
|
|
235
|
+
|
|
236
|
+
def test_retained_redescription_preserves_values_without_generation_bump(self):
|
|
237
|
+
"""A re-delivered retained $description without a disconnect must NOT bump generation."""
|
|
238
|
+
acc = HomiePropertyAccumulator(SERIAL)
|
|
239
|
+
_make_ready(acc)
|
|
240
|
+
acc.handle_message(f"{PREFIX}/circuit-1/exported-energy", "1000")
|
|
241
|
+
|
|
242
|
+
# Simulate network reconnect — $description re-delivered without $state=disconnected
|
|
243
|
+
acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
|
|
244
|
+
|
|
245
|
+
# Property values should be preserved and generation must not increment
|
|
246
|
+
assert acc.get_prop("circuit-1", "exported-energy") == "1000"
|
|
247
|
+
assert acc.generation == 1 # still 1 from initial boot, no increment on re-delivery
|
|
248
|
+
|
|
249
|
+
def test_fresh_properties_overwrite_preserved_after_reboot(self):
|
|
250
|
+
"""Post-reboot properties should overwrite the preserved placeholder values."""
|
|
251
|
+
acc = HomiePropertyAccumulator(SERIAL)
|
|
252
|
+
_make_ready(acc)
|
|
253
|
+
acc.handle_message(f"{PREFIX}/circuit-1/exported-energy", "1000")
|
|
254
|
+
|
|
255
|
+
self._simulate_reboot(acc)
|
|
256
|
+
|
|
257
|
+
# Fresh post-reboot value overwrites preserved placeholder
|
|
258
|
+
acc.handle_message(f"{PREFIX}/circuit-1/exported-energy", "50")
|
|
259
|
+
assert acc.get_prop("circuit-1", "exported-energy") == "50"
|
|
260
|
+
|
|
261
|
+
def test_fresh_target_values_overwrite_preserved_after_reboot(self):
|
|
262
|
+
"""Post-reboot target values should overwrite the preserved placeholder values."""
|
|
263
|
+
acc = HomiePropertyAccumulator(SERIAL)
|
|
264
|
+
_make_ready(acc)
|
|
265
|
+
acc.handle_message(f"{PREFIX}/circuit-1/relay/$target", "OPEN")
|
|
266
|
+
assert acc.get_target("circuit-1", "relay") == "OPEN"
|
|
267
|
+
|
|
268
|
+
self._simulate_reboot(acc)
|
|
269
|
+
|
|
270
|
+
# Fresh post-reboot target value overwrites preserved placeholder
|
|
271
|
+
acc.handle_message(f"{PREFIX}/circuit-1/relay/$target", "CLOSED")
|
|
272
|
+
assert acc.get_target("circuit-1", "relay") == "CLOSED"
|
|
273
|
+
|
|
274
|
+
def test_fast_reboot_without_lwt_preserves_values(self):
|
|
275
|
+
"""Panel reboots so fast that $state=disconnected (LWT) is skipped.
|
|
276
|
+
|
|
277
|
+
The panel goes directly from ready -> init -> description -> ready.
|
|
278
|
+
$state=init must reset _received_description so the subsequent
|
|
279
|
+
$description increments the generation counter but does NOT clear
|
|
280
|
+
property values — pre-reboot values serve as safe placeholders.
|
|
281
|
+
"""
|
|
282
|
+
acc = HomiePropertyAccumulator(SERIAL)
|
|
283
|
+
_make_ready(acc)
|
|
284
|
+
acc.handle_message(f"{PREFIX}/circuit-1/exported-energy", "1000")
|
|
285
|
+
gen_before = acc.generation
|
|
286
|
+
|
|
287
|
+
# Fast reboot: no $state=disconnected, straight to init
|
|
288
|
+
acc.handle_message(f"{PREFIX}/$state", "init")
|
|
289
|
+
acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
|
|
290
|
+
acc.handle_message(f"{PREFIX}/$state", "ready")
|
|
291
|
+
|
|
292
|
+
# Property values should be preserved
|
|
293
|
+
assert acc.get_prop("circuit-1", "exported-energy") == "1000"
|
|
294
|
+
assert acc.generation == gen_before + 1
|
|
295
|
+
|
|
296
|
+
|
|
166
297
|
# ---------------------------------------------------------------------------
|
|
167
298
|
# Lifecycle: invalid JSON description
|
|
168
299
|
# ---------------------------------------------------------------------------
|
|
@@ -905,6 +905,34 @@ class TestSnapshotCaching:
|
|
|
905
905
|
snap2 = consumer.build_snapshot()
|
|
906
906
|
assert snap2 is not snap1
|
|
907
907
|
|
|
908
|
+
def test_description_invalidates_cache_via_generation(self):
|
|
909
|
+
"""Panel reboot ($description) must invalidate cached snapshot but preserve values."""
|
|
910
|
+
acc, consumer = _build_ready_consumer()
|
|
911
|
+
node = "aabbccdd-1122-3344-5566-778899001122"
|
|
912
|
+
|
|
913
|
+
# Pre-reboot: circuit has energy = 1000
|
|
914
|
+
acc.handle_message(f"{PREFIX}/{node}/exported-energy", "1000")
|
|
915
|
+
snap_pre = consumer.build_snapshot()
|
|
916
|
+
circuit_id = "aabbccdd112233445566778899001122"
|
|
917
|
+
assert snap_pre.circuits[circuit_id].consumed_energy_wh == 1000.0
|
|
918
|
+
|
|
919
|
+
# Panel reboots — $state=disconnected resets lifecycle, $description preserves values
|
|
920
|
+
acc.handle_message(f"{PREFIX}/$state", "disconnected")
|
|
921
|
+
acc.handle_message(f"{PREFIX}/$description", _make_description(_full_description()))
|
|
922
|
+
acc.handle_message(f"{PREFIX}/$state", "ready")
|
|
923
|
+
|
|
924
|
+
# Before new property messages: snapshot uses preserved pre-reboot value
|
|
925
|
+
snap_preserved = consumer.build_snapshot()
|
|
926
|
+
assert snap_preserved.circuits[circuit_id].consumed_energy_wh == 1000.0
|
|
927
|
+
assert snap_preserved is not snap_pre # cache was invalidated
|
|
928
|
+
|
|
929
|
+
# Post-reboot: circuit publishes new value
|
|
930
|
+
acc.handle_message(f"{PREFIX}/{node}/exported-energy", "50")
|
|
931
|
+
snap_post = consumer.build_snapshot()
|
|
932
|
+
|
|
933
|
+
# Must reflect post-reboot value
|
|
934
|
+
assert snap_post.circuits[circuit_id].consumed_energy_wh == 50.0
|
|
935
|
+
|
|
908
936
|
|
|
909
937
|
# ---------------------------------------------------------------------------
|
|
910
938
|
# HomieDeviceConsumer — property callbacks
|
|
@@ -427,51 +427,51 @@ toml = [
|
|
|
427
427
|
|
|
428
428
|
[[package]]
|
|
429
429
|
name = "cryptography"
|
|
430
|
-
version = "46.0.
|
|
430
|
+
version = "46.0.7"
|
|
431
431
|
source = { registry = "https://pypi.org/simple" }
|
|
432
432
|
dependencies = [
|
|
433
433
|
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
|
434
434
|
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
|
435
435
|
]
|
|
436
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
437
|
-
wheels = [
|
|
438
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
439
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
440
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
441
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
442
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
443
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
444
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
445
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
446
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
447
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
448
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
449
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
450
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
451
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
452
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
453
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
454
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
455
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
456
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
457
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
458
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
459
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
460
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
461
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
462
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
463
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
464
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
465
|
-
{ url = "https://files.pythonhosted.org/packages/01/
|
|
466
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
467
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
468
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
469
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
470
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
471
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
472
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
473
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
474
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
436
|
+
sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" }
|
|
437
|
+
wheels = [
|
|
438
|
+
{ url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" },
|
|
439
|
+
{ url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" },
|
|
440
|
+
{ url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" },
|
|
441
|
+
{ url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" },
|
|
442
|
+
{ url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" },
|
|
443
|
+
{ url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" },
|
|
444
|
+
{ url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" },
|
|
445
|
+
{ url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" },
|
|
446
|
+
{ url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" },
|
|
447
|
+
{ url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" },
|
|
448
|
+
{ url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" },
|
|
449
|
+
{ url = "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", size = 4268551, upload-time = "2026-04-08T01:56:46.071Z" },
|
|
450
|
+
{ url = "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", size = 4408887, upload-time = "2026-04-08T01:56:47.718Z" },
|
|
451
|
+
{ url = "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", size = 4271354, upload-time = "2026-04-08T01:56:49.312Z" },
|
|
452
|
+
{ url = "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", size = 4905845, upload-time = "2026-04-08T01:56:50.916Z" },
|
|
453
|
+
{ url = "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", size = 4444641, upload-time = "2026-04-08T01:56:52.882Z" },
|
|
454
|
+
{ url = "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", size = 3967749, upload-time = "2026-04-08T01:56:54.597Z" },
|
|
455
|
+
{ url = "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", size = 4270942, upload-time = "2026-04-08T01:56:56.416Z" },
|
|
456
|
+
{ url = "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", size = 4871079, upload-time = "2026-04-08T01:56:58.31Z" },
|
|
457
|
+
{ url = "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", size = 4443999, upload-time = "2026-04-08T01:57:00.508Z" },
|
|
458
|
+
{ url = "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", size = 4399191, upload-time = "2026-04-08T01:57:02.654Z" },
|
|
459
|
+
{ url = "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", size = 4655782, upload-time = "2026-04-08T01:57:04.592Z" },
|
|
460
|
+
{ url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" },
|
|
461
|
+
{ url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" },
|
|
462
|
+
{ url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" },
|
|
463
|
+
{ url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" },
|
|
464
|
+
{ url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" },
|
|
465
|
+
{ url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" },
|
|
466
|
+
{ url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" },
|
|
467
|
+
{ url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" },
|
|
468
|
+
{ url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" },
|
|
469
|
+
{ url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" },
|
|
470
|
+
{ url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" },
|
|
471
|
+
{ url = "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", size = 4219700, upload-time = "2026-04-08T01:57:40.625Z" },
|
|
472
|
+
{ url = "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", size = 4385982, upload-time = "2026-04-08T01:57:42.725Z" },
|
|
473
|
+
{ url = "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", size = 4219115, upload-time = "2026-04-08T01:57:44.939Z" },
|
|
474
|
+
{ url = "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", size = 4385479, upload-time = "2026-04-08T01:57:46.86Z" },
|
|
475
475
|
]
|
|
476
476
|
|
|
477
477
|
[[package]]
|
|
@@ -1310,7 +1310,7 @@ wheels = [
|
|
|
1310
1310
|
|
|
1311
1311
|
[[package]]
|
|
1312
1312
|
name = "span-panel-api"
|
|
1313
|
-
version = "2.5.
|
|
1313
|
+
version = "2.5.3"
|
|
1314
1314
|
source = { editable = "." }
|
|
1315
1315
|
dependencies = [
|
|
1316
1316
|
{ name = "httpx" },
|
|
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
|
|
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
|
{span_panel_api-2.5.1 → span_panel_api-2.5.3}/tests/simulation_fixtures/circuits.response.txt
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
|