span-panel-api 2.5.2__tar.gz → 2.5.4__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.2 → span_panel_api-2.5.4}/CHANGELOG.md +47 -27
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/PKG-INFO +1 -1
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/pyproject.toml +1 -1
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/mqtt/accumulator.py +4 -38
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/mqtt/homie.py +0 -7
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_accumulator.py +68 -80
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_mqtt_homie.py +19 -14
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/uv.lock +35 -35
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.codefactor +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.codefactor.yml +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.deps-installed +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.github/dependabot.yml +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.github/workflows/ci.yml +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.github/workflows/dependabot-auto-approve.yml +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.github/workflows/dependabot-auto-merge.yml +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.github/workflows/release.yml +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.gitignore +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.markdownlint-cli2.jsonc +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.markdownlint.json +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.pre-commit-config.yaml +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.prettierrc.json +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.vscode/extensions.json +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/.vscode/tasks.json +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/DEVELOPMENT.md +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/LICENSE +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/README.md +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/SECURITY.md +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/conftest.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/developer_attribute_readme.md +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/openapi.json +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/pytest.ini +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/pytest_output.log +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/scripts/__init__.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/scripts/coverage.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/scripts/format.sh +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/scripts/format_markdown.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/scripts/test_live_auth.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/setup-hooks.sh +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/__init__.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/_http.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/auth.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/const.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/detection.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/exceptions.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/factory.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/models.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/mqtt/__init__.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/mqtt/async_client.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/mqtt/client.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/mqtt/connection.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/mqtt/const.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/mqtt/field_metadata.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/mqtt/models.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/phase_validation.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/protocol.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/src/span_panel_api/py.typed +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/conftest.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/fixtures/configs/simulation_config_32_circuit.yaml +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/fixtures/configs/simulation_config_40_circuit_with_battery.yaml +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/fixtures/configs/simulation_config_8_tab_workshop.yaml +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/fixtures/v2/README.md +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/fixtures/v2/homie_schema.json +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/fixtures/v2/status.json +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/simulation_fixtures/circuits.response.txt +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/simulation_fixtures/panel.response.txt +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/simulation_fixtures/soe.response.txt +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/simulation_fixtures/status.response.txt +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_async_mqtt_client.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_auth_and_homie_helpers.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_detection_auth.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_field_metadata.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_mqtt_bridge.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_mqtt_connect_flow.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_mqtt_debounce.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_phase_validation_configs.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_phase_validation_errors.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_protocol_conformance.py +0 -0
- {span_panel_api-2.5.2 → span_panel_api-2.5.4}/tests/test_protocol_models.py +0 -0
|
@@ -4,7 +4,25 @@ 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.
|
|
7
|
+
## [2.5.4] - 04/2026
|
|
8
|
+
|
|
9
|
+
### Reverted
|
|
10
|
+
|
|
11
|
+
- **Revert accumulator to 2.5.1 behavior** — the 2.5.2 lifecycle changes (property clearing, unconditional lifecycle transition on `$state=init`, generation counter) caused false energy dip spikes on panel reboots and network interruptions. The 2.5.3
|
|
12
|
+
partial fix (removing the clearing) was insufficient — the unconditional lifecycle disruption on transient `$state=init` events still triggered snapshot pipeline resets that produced 0.0 energy readings. Reverted `accumulator.py` and `homie.py` to their
|
|
13
|
+
stable 2.5.1 state. The existing dirty-node tracking handles reboot transitions correctly without special-case lifecycle management.
|
|
14
|
+
|
|
15
|
+
## [2.5.3] - 04/2026 (retired)
|
|
16
|
+
|
|
17
|
+
> **Retired:** Partial fix for 2.5.2 — removed property clearing but kept the lifecycle disruption that still caused false dips. Superseded by 2.5.4.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Preserve property values on lifecycle reset** — removed the property/timestamp/target clearing from `_handle_description()`.
|
|
22
|
+
|
|
23
|
+
## [2.5.2] - 04/2026 (retired)
|
|
24
|
+
|
|
25
|
+
> **Retired:** Lifecycle changes caused false energy dip spikes. Superseded by 2.5.4.
|
|
8
26
|
|
|
9
27
|
### Fixed
|
|
10
28
|
|
|
@@ -334,29 +352,31 @@ Package versions prior to 2.0.0 depend on the SPAN v1 REST API. SPAN will sunset
|
|
|
334
352
|
|
|
335
353
|
## Version History Summary
|
|
336
354
|
|
|
337
|
-
| Version | Date | Transport | Summary
|
|
338
|
-
| ---------- | ------- | ---------- |
|
|
339
|
-
| **2.5.
|
|
340
|
-
| **2.5.
|
|
341
|
-
| **2.5.
|
|
342
|
-
| **2.
|
|
343
|
-
| **2.
|
|
344
|
-
| **2.4.
|
|
345
|
-
| **2.
|
|
346
|
-
| **2.
|
|
347
|
-
| **2.3.
|
|
348
|
-
| **2.
|
|
349
|
-
| **2.
|
|
350
|
-
| **2.
|
|
351
|
-
| **2.
|
|
352
|
-
| **2.0.
|
|
353
|
-
| **
|
|
354
|
-
| **
|
|
355
|
-
| **1.1.
|
|
356
|
-
| **1.1.
|
|
357
|
-
| **1.1.
|
|
358
|
-
| **1.1.
|
|
359
|
-
| **1.1.
|
|
360
|
-
| **1.1.
|
|
361
|
-
| **1.1.
|
|
362
|
-
| **1.1.
|
|
355
|
+
| Version | Date | Transport | Summary |
|
|
356
|
+
| ---------- | ------- | ---------- | ---------------------------------------------------------------------------------- |
|
|
357
|
+
| **2.5.4** | 04/2026 | MQTT/Homie | Revert accumulator to stable 2.5.1 behavior; fixes false energy dip spikes |
|
|
358
|
+
| **2.5.3** | 04/2026 | MQTT/Homie | _(retired)_ Partial fix — still caused false dips from lifecycle disruption |
|
|
359
|
+
| **2.5.2** | 04/2026 | MQTT/Homie | _(retired)_ Lifecycle changes caused false energy dip spikes |
|
|
360
|
+
| **2.5.1** | 04/2026 | MQTT/Homie | Replace assert with RuntimeError; fix bandit pre-commit hook |
|
|
361
|
+
| **2.5.0** | 03/2026 | MQTT/Homie | Homie accumulator layer, $target support, dirty-node snapshot caching |
|
|
362
|
+
| **2.4.2** | 03/2026 | MQTT/Homie | SSL context creation moved to executor |
|
|
363
|
+
| **2.4.1** | 03/2026 | MQTT/Homie | License metadata, loosened httpx constraint |
|
|
364
|
+
| **2.4.0** | 03/2026 | MQTT/Homie | proximityProven, injected HTTP client, executor file I/O, type alias, test cleanup |
|
|
365
|
+
| **2.3.2** | 03/2026 | MQTT/Homie | FQDN management endpoints |
|
|
366
|
+
| **2.3.1** | 03/2026 | MQTT/Homie | MQTT connection errors wrapped as SpanPanelConnectionError |
|
|
367
|
+
| **2.3.0** | 03/2026 | MQTT/Homie | Simulation engine removed |
|
|
368
|
+
| **2.2.4** | 03/2026 | MQTT/Homie | Negative zero fix on idle circuits |
|
|
369
|
+
| **2.2.3** | 03/2026 | MQTT/Homie | Panel size from Homie schema; `panel_size` always populated on snapshot |
|
|
370
|
+
| **2.0.2** | 03/2026 | MQTT/Homie | EVSE (EV charger) snapshot model, Homie parsing, simulation support |
|
|
371
|
+
| **2.0.1** | 03/2026 | MQTT/Homie | Full BESS metadata parsing, README documentation |
|
|
372
|
+
| **2.0.0** | 02/2026 | MQTT/Homie | Ground-up rewrite: MQTT-only, protocol-based API, real-time push, PV/BESS metadata |
|
|
373
|
+
| **1.1.14** | 12/2025 | REST | Keep-Alive and RemoteProtocolError handling |
|
|
374
|
+
| **1.1.9** | 9/2025 | REST | Simulation sign corrections |
|
|
375
|
+
| **1.1.8** | 2024 | REST | Simulation power sign fix |
|
|
376
|
+
| **1.1.6** | 2024 | REST | YAML simulation API, battery simulation |
|
|
377
|
+
| **1.1.5** | 2024 | REST | Simulation edge cases |
|
|
378
|
+
| **1.1.4** | 2024 | REST | Formatting and linting |
|
|
379
|
+
| **1.1.3** | 2024 | REST | Test and lint fixes |
|
|
380
|
+
| **1.1.2** | 2024 | REST | Simulation mode added |
|
|
381
|
+
| **1.1.1** | 2024 | REST | Dependency updates |
|
|
382
|
+
| **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.4
|
|
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,10 +56,6 @@ class HomiePropertyAccumulator:
|
|
|
56
56
|
# Node type mapping from $description
|
|
57
57
|
self._node_types: dict[str, str] = {}
|
|
58
58
|
|
|
59
|
-
# Generation counter — incremented when $description clears property
|
|
60
|
-
# values so consumers can invalidate caches built from stale data.
|
|
61
|
-
self._generation: int = 0
|
|
62
|
-
|
|
63
59
|
# Dirty tracking
|
|
64
60
|
self._dirty_nodes: set[str] = set()
|
|
65
61
|
|
|
@@ -83,11 +79,6 @@ class HomiePropertyAccumulator:
|
|
|
83
79
|
"""Monotonic timestamp of the last READY transition, 0.0 if never ready."""
|
|
84
80
|
return self._ready_since
|
|
85
81
|
|
|
86
|
-
@property
|
|
87
|
-
def generation(self) -> int:
|
|
88
|
-
"""Counter incremented on the initial $description and after lifecycle resets."""
|
|
89
|
-
return self._generation
|
|
90
|
-
|
|
91
82
|
def is_ready(self) -> bool:
|
|
92
83
|
"""True when lifecycle is READY."""
|
|
93
84
|
return self._lifecycle == HomieLifecycle.READY
|
|
@@ -200,17 +191,10 @@ class HomiePropertyAccumulator:
|
|
|
200
191
|
self._received_state_ready = False
|
|
201
192
|
self._received_description = False
|
|
202
193
|
else:
|
|
203
|
-
# init, sleeping, alert, etc. — connected but not ready
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
#
|
|
207
|
-
# Reset _received_description so that the upcoming $description
|
|
208
|
-
# triggers a property clear. This covers fast reboots where
|
|
209
|
-
# the broker's LWT ($state=disconnected) may not reach us
|
|
210
|
-
# before the panel publishes $state=init.
|
|
211
|
-
self._lifecycle = HomieLifecycle.CONNECTED
|
|
194
|
+
# init, sleeping, alert, etc. — connected but not ready
|
|
195
|
+
if self._lifecycle == HomieLifecycle.DISCONNECTED:
|
|
196
|
+
self._lifecycle = HomieLifecycle.CONNECTED
|
|
212
197
|
self._received_state_ready = False
|
|
213
|
-
self._received_description = False
|
|
214
198
|
|
|
215
199
|
_LOGGER.debug("Homie $state: %s → lifecycle=%s", payload, self._lifecycle.value)
|
|
216
200
|
|
|
@@ -222,20 +206,6 @@ class HomiePropertyAccumulator:
|
|
|
222
206
|
_LOGGER.warning("Invalid $description JSON")
|
|
223
207
|
return
|
|
224
208
|
|
|
225
|
-
# _handle_state() already reset _received_description to False due to
|
|
226
|
-
# a state change that starts a new panel lifecycle, including
|
|
227
|
-
# $state=disconnected/lost and other non-ready states such as init.
|
|
228
|
-
# This means the panel rebooted while we were connected. On a pure
|
|
229
|
-
# MQTT reconnect (no panel reboot), _received_description is still
|
|
230
|
-
# True from the previous session so we skip the clear — the retained
|
|
231
|
-
# property messages will carry the correct (unchanged) values.
|
|
232
|
-
if not self._received_description:
|
|
233
|
-
self._property_values.clear()
|
|
234
|
-
self._property_timestamps.clear()
|
|
235
|
-
self._target_values.clear()
|
|
236
|
-
self._generation += 1
|
|
237
|
-
_LOGGER.debug("Cleared stale property values (generation %d)", self._generation)
|
|
238
|
-
|
|
239
209
|
self._received_description = True
|
|
240
210
|
self._node_types.clear()
|
|
241
211
|
|
|
@@ -250,11 +220,7 @@ class HomiePropertyAccumulator:
|
|
|
250
220
|
# Mark all known nodes dirty
|
|
251
221
|
self._dirty_nodes.update(self._node_types.keys())
|
|
252
222
|
|
|
253
|
-
_LOGGER.debug(
|
|
254
|
-
"Parsed $description with %d nodes (generation %d)",
|
|
255
|
-
len(self._node_types),
|
|
256
|
-
self._generation,
|
|
257
|
-
)
|
|
223
|
+
_LOGGER.debug("Parsed $description with %d nodes", len(self._node_types))
|
|
258
224
|
|
|
259
225
|
# Lifecycle transition
|
|
260
226
|
if self._received_state_ready:
|
|
@@ -64,7 +64,6 @@ 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
|
|
68
67
|
|
|
69
68
|
# -- Delegation to accumulator -------------------------------------------
|
|
70
69
|
# These thin wrappers allow SpanMqttClient (and legacy test code) to
|
|
@@ -119,12 +118,6 @@ class HomieDeviceConsumer:
|
|
|
119
118
|
|
|
120
119
|
Must be called after accumulator is_ready() returns True.
|
|
121
120
|
"""
|
|
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
|
-
|
|
128
121
|
dirty = self._acc.dirty_node_ids()
|
|
129
122
|
|
|
130
123
|
if not dirty and self._cached_snapshot is not None:
|
|
@@ -164,128 +164,116 @@ class TestLifecycleDisconnection:
|
|
|
164
164
|
|
|
165
165
|
|
|
166
166
|
# ---------------------------------------------------------------------------
|
|
167
|
-
#
|
|
167
|
+
# Lifecycle: READY resilience (core 2.5.4 revert invariant)
|
|
168
168
|
# ---------------------------------------------------------------------------
|
|
169
169
|
|
|
170
170
|
|
|
171
|
-
class
|
|
172
|
-
"""
|
|
171
|
+
class TestLifecycleReadyResilience:
|
|
172
|
+
"""Transient $state values must NOT disrupt READY lifecycle.
|
|
173
173
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
174
|
+
This is the core behavioral invariant of the 2.5.1→2.5.4 revert:
|
|
175
|
+
only $state=disconnected/$state=lost should knock the device out
|
|
176
|
+
of READY. Transient states like init (e.g. from fast panel reboots
|
|
177
|
+
where the LWT may not arrive) must be ignored when already READY.
|
|
178
178
|
"""
|
|
179
179
|
|
|
180
|
-
def
|
|
181
|
-
"""Simulate the panel reboot lifecycle transition."""
|
|
182
|
-
acc.handle_message(f"{PREFIX}/$state", "disconnected")
|
|
183
|
-
acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
|
|
184
|
-
acc.handle_message(f"{PREFIX}/$state", "ready")
|
|
185
|
-
|
|
186
|
-
def test_description_clears_property_values_on_reboot(self):
|
|
187
|
-
"""A panel reboot must clear property values from the previous lifecycle."""
|
|
180
|
+
def test_init_state_when_ready_does_not_disrupt(self):
|
|
188
181
|
acc = HomiePropertyAccumulator(SERIAL)
|
|
189
182
|
_make_ready(acc)
|
|
190
|
-
acc.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
self._simulate_reboot(acc)
|
|
195
|
-
assert acc.get_prop("circuit-1", "exported-energy") == ""
|
|
183
|
+
assert acc.lifecycle == HomieLifecycle.READY
|
|
184
|
+
acc.handle_message(f"{PREFIX}/$state", "init")
|
|
185
|
+
assert acc.lifecycle == HomieLifecycle.READY
|
|
186
|
+
assert acc.is_ready()
|
|
196
187
|
|
|
197
|
-
def
|
|
198
|
-
"""Timestamps must also be cleared so stale timing data doesn't persist."""
|
|
188
|
+
def test_sleeping_state_when_ready_does_not_disrupt(self):
|
|
199
189
|
acc = HomiePropertyAccumulator(SERIAL)
|
|
200
190
|
_make_ready(acc)
|
|
201
|
-
acc.handle_message(f"{PREFIX}
|
|
202
|
-
assert acc.
|
|
203
|
-
|
|
204
|
-
self._simulate_reboot(acc)
|
|
205
|
-
assert acc.get_timestamp("circuit-1", "exported-energy") == 0
|
|
191
|
+
acc.handle_message(f"{PREFIX}/$state", "sleeping")
|
|
192
|
+
assert acc.lifecycle == HomieLifecycle.READY
|
|
206
193
|
|
|
207
|
-
def
|
|
208
|
-
"""Target values must also be cleared on panel reboot."""
|
|
194
|
+
def test_alert_state_when_ready_does_not_disrupt(self):
|
|
209
195
|
acc = HomiePropertyAccumulator(SERIAL)
|
|
210
196
|
_make_ready(acc)
|
|
211
|
-
acc.handle_message(f"{PREFIX}
|
|
212
|
-
assert acc.
|
|
213
|
-
|
|
214
|
-
self._simulate_reboot(acc)
|
|
215
|
-
assert acc.get_target("circuit-1", "relay") is None
|
|
197
|
+
acc.handle_message(f"{PREFIX}/$state", "alert")
|
|
198
|
+
assert acc.lifecycle == HomieLifecycle.READY
|
|
216
199
|
|
|
217
|
-
def
|
|
218
|
-
"""
|
|
200
|
+
def test_init_from_connected_stays_connected(self):
|
|
201
|
+
"""$state=init while CONNECTED should not regress lifecycle."""
|
|
219
202
|
acc = HomiePropertyAccumulator(SERIAL)
|
|
220
|
-
|
|
203
|
+
acc.handle_message(f"{PREFIX}/$state", "ready") # → CONNECTED (no desc yet)
|
|
204
|
+
assert acc.lifecycle == HomieLifecycle.CONNECTED
|
|
205
|
+
acc.handle_message(f"{PREFIX}/$state", "init")
|
|
206
|
+
assert acc.lifecycle == HomieLifecycle.CONNECTED
|
|
221
207
|
|
|
222
|
-
# First boot
|
|
223
|
-
acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
|
|
224
|
-
assert acc.generation == 1
|
|
225
208
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
209
|
+
# ---------------------------------------------------------------------------
|
|
210
|
+
# Lifecycle: reboot sequence (value preservation + dirty tracking)
|
|
211
|
+
# ---------------------------------------------------------------------------
|
|
212
|
+
|
|
230
213
|
|
|
231
|
-
|
|
232
|
-
|
|
214
|
+
class TestLifecycleReboot:
|
|
215
|
+
"""Reboot handling relies on dirty-node tracking, not property clearing."""
|
|
216
|
+
|
|
217
|
+
def test_reboot_preserves_property_values(self):
|
|
218
|
+
"""Properties stored before disconnect survive the full reboot cycle."""
|
|
233
219
|
acc = HomiePropertyAccumulator(SERIAL)
|
|
234
220
|
_make_ready(acc)
|
|
235
|
-
acc.handle_message(f"{PREFIX}/
|
|
221
|
+
acc.handle_message(f"{PREFIX}/core/power", "100")
|
|
222
|
+
acc.mark_clean()
|
|
236
223
|
|
|
237
|
-
#
|
|
224
|
+
# Full reboot sequence: disconnect → init → description → ready
|
|
225
|
+
acc.handle_message(f"{PREFIX}/$state", "disconnected")
|
|
226
|
+
acc.handle_message(f"{PREFIX}/$state", "init")
|
|
238
227
|
acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
|
|
228
|
+
acc.handle_message(f"{PREFIX}/$state", "ready")
|
|
239
229
|
|
|
240
|
-
|
|
241
|
-
assert acc.
|
|
242
|
-
assert acc.generation == 1 # still 1 from initial boot, no increment on re-delivery
|
|
230
|
+
assert acc.get_prop("core", "power") == "100"
|
|
231
|
+
assert acc.lifecycle == HomieLifecycle.READY
|
|
243
232
|
|
|
244
|
-
def
|
|
245
|
-
"""
|
|
233
|
+
def test_reboot_description_marks_all_nodes_dirty(self):
|
|
234
|
+
"""$description after reboot marks all nodes dirty for cache invalidation."""
|
|
246
235
|
acc = HomiePropertyAccumulator(SERIAL)
|
|
247
236
|
_make_ready(acc)
|
|
248
|
-
acc.
|
|
237
|
+
acc.mark_clean()
|
|
238
|
+
assert len(acc.dirty_node_ids()) == 0
|
|
249
239
|
|
|
250
|
-
|
|
240
|
+
acc.handle_message(f"{PREFIX}/$state", "disconnected")
|
|
241
|
+
acc.handle_message(f"{PREFIX}/$state", "init")
|
|
242
|
+
acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
|
|
251
243
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
assert
|
|
244
|
+
dirty = acc.dirty_node_ids()
|
|
245
|
+
assert "core" in dirty
|
|
246
|
+
assert "circuit-1" in dirty
|
|
247
|
+
assert "circuit-2" in dirty
|
|
248
|
+
assert "bess-0" in dirty
|
|
255
249
|
|
|
256
|
-
def
|
|
257
|
-
"""
|
|
250
|
+
def test_reboot_timestamps_preserved(self):
|
|
251
|
+
"""Timestamps are not cleared during reboot — only updated on new values."""
|
|
258
252
|
acc = HomiePropertyAccumulator(SERIAL)
|
|
259
253
|
_make_ready(acc)
|
|
260
|
-
acc.handle_message(f"{PREFIX}/
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
self._simulate_reboot(acc)
|
|
254
|
+
acc.handle_message(f"{PREFIX}/core/power", "100")
|
|
255
|
+
ts_before = acc.get_timestamp("core", "power")
|
|
256
|
+
assert ts_before > 0
|
|
264
257
|
|
|
265
|
-
|
|
266
|
-
acc.handle_message(f"{PREFIX}
|
|
267
|
-
|
|
258
|
+
acc.handle_message(f"{PREFIX}/$state", "disconnected")
|
|
259
|
+
acc.handle_message(f"{PREFIX}/$state", "init")
|
|
260
|
+
acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
|
|
261
|
+
acc.handle_message(f"{PREFIX}/$state", "ready")
|
|
268
262
|
|
|
269
|
-
|
|
270
|
-
"""Panel reboots so fast that $state=disconnected (LWT) is skipped.
|
|
263
|
+
assert acc.get_timestamp("core", "power") == ts_before
|
|
271
264
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
$description triggers the property clear.
|
|
275
|
-
"""
|
|
265
|
+
def test_reboot_target_values_preserved(self):
|
|
266
|
+
"""Target values survive the reboot cycle."""
|
|
276
267
|
acc = HomiePropertyAccumulator(SERIAL)
|
|
277
268
|
_make_ready(acc)
|
|
278
|
-
acc.handle_message(f"{PREFIX}/
|
|
279
|
-
gen_before = acc.generation
|
|
269
|
+
acc.handle_message(f"{PREFIX}/core/relay/$target", "OPEN")
|
|
280
270
|
|
|
281
|
-
|
|
271
|
+
acc.handle_message(f"{PREFIX}/$state", "disconnected")
|
|
282
272
|
acc.handle_message(f"{PREFIX}/$state", "init")
|
|
283
273
|
acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
|
|
284
274
|
acc.handle_message(f"{PREFIX}/$state", "ready")
|
|
285
275
|
|
|
286
|
-
|
|
287
|
-
assert acc.get_prop("circuit-1", "exported-energy") == ""
|
|
288
|
-
assert acc.generation == gen_before + 1
|
|
276
|
+
assert acc.get_target("core", "relay") == "OPEN"
|
|
289
277
|
|
|
290
278
|
|
|
291
279
|
# ---------------------------------------------------------------------------
|
|
@@ -905,28 +905,33 @@ class TestSnapshotCaching:
|
|
|
905
905
|
snap2 = consumer.build_snapshot()
|
|
906
906
|
assert snap2 is not snap1
|
|
907
907
|
|
|
908
|
-
def
|
|
909
|
-
"""
|
|
908
|
+
def test_reboot_dirty_nodes_invalidate_cache(self):
|
|
909
|
+
"""Post-reboot $description marks all nodes dirty → full rebuild, not cached."""
|
|
910
910
|
acc, consumer = _build_ready_consumer()
|
|
911
911
|
node = "aabbccdd-1122-3344-5566-778899001122"
|
|
912
|
+
acc.handle_message(f"{PREFIX}/{node}/active-power", "-100.0")
|
|
913
|
+
snap1 = consumer.build_snapshot()
|
|
912
914
|
|
|
913
|
-
#
|
|
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 clears values
|
|
915
|
+
# Reboot sequence
|
|
920
916
|
acc.handle_message(f"{PREFIX}/$state", "disconnected")
|
|
917
|
+
acc.handle_message(f"{PREFIX}/$state", "init")
|
|
921
918
|
acc.handle_message(f"{PREFIX}/$description", _make_description(_full_description()))
|
|
922
919
|
acc.handle_message(f"{PREFIX}/$state", "ready")
|
|
923
920
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
snap_post = consumer.build_snapshot()
|
|
921
|
+
snap2 = consumer.build_snapshot()
|
|
922
|
+
assert snap2 is not snap1 # cache invalidated by dirty nodes
|
|
927
923
|
|
|
928
|
-
|
|
929
|
-
|
|
924
|
+
def test_init_while_ready_does_not_invalidate_cache(self):
|
|
925
|
+
"""$state=init while READY must not disrupt snapshot caching."""
|
|
926
|
+
acc, consumer = _build_ready_consumer()
|
|
927
|
+
node = "aabbccdd-1122-3344-5566-778899001122"
|
|
928
|
+
acc.handle_message(f"{PREFIX}/{node}/active-power", "-100.0")
|
|
929
|
+
snap1 = consumer.build_snapshot()
|
|
930
|
+
|
|
931
|
+
acc.handle_message(f"{PREFIX}/$state", "init")
|
|
932
|
+
|
|
933
|
+
snap2 = consumer.build_snapshot()
|
|
934
|
+
assert snap2 is snap1 # same cached object — no disruption
|
|
930
935
|
|
|
931
936
|
|
|
932
937
|
# ---------------------------------------------------------------------------
|
|
@@ -415,45 +415,45 @@ toml = [
|
|
|
415
415
|
|
|
416
416
|
[[package]]
|
|
417
417
|
name = "cryptography"
|
|
418
|
-
version = "46.0.
|
|
418
|
+
version = "46.0.7"
|
|
419
419
|
source = { registry = "https://pypi.org/simple" }
|
|
420
420
|
dependencies = [
|
|
421
421
|
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
|
422
422
|
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
|
423
423
|
]
|
|
424
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
425
|
-
wheels = [
|
|
426
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
427
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
428
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
429
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
430
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
431
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
432
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
433
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
434
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
435
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
436
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
437
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
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/01/
|
|
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/
|
|
424
|
+
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" }
|
|
425
|
+
wheels = [
|
|
426
|
+
{ 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" },
|
|
427
|
+
{ 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" },
|
|
428
|
+
{ 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" },
|
|
429
|
+
{ 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" },
|
|
430
|
+
{ 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" },
|
|
431
|
+
{ 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" },
|
|
432
|
+
{ 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" },
|
|
433
|
+
{ 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" },
|
|
434
|
+
{ 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" },
|
|
435
|
+
{ 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" },
|
|
436
|
+
{ 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" },
|
|
437
|
+
{ 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" },
|
|
438
|
+
{ 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" },
|
|
439
|
+
{ 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" },
|
|
440
|
+
{ 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" },
|
|
441
|
+
{ 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" },
|
|
442
|
+
{ 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" },
|
|
443
|
+
{ 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" },
|
|
444
|
+
{ 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" },
|
|
445
|
+
{ 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" },
|
|
446
|
+
{ 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" },
|
|
447
|
+
{ 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" },
|
|
448
|
+
{ 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" },
|
|
449
|
+
{ 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" },
|
|
450
|
+
{ 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" },
|
|
451
|
+
{ 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" },
|
|
452
|
+
{ 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" },
|
|
453
|
+
{ 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" },
|
|
454
|
+
{ 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" },
|
|
455
|
+
{ 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" },
|
|
456
|
+
{ 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" },
|
|
457
457
|
]
|
|
458
458
|
|
|
459
459
|
[[package]]
|
|
@@ -1292,7 +1292,7 @@ wheels = [
|
|
|
1292
1292
|
|
|
1293
1293
|
[[package]]
|
|
1294
1294
|
name = "span-panel-api"
|
|
1295
|
-
version = "2.5.
|
|
1295
|
+
version = "2.5.4"
|
|
1296
1296
|
source = { editable = "." }
|
|
1297
1297
|
dependencies = [
|
|
1298
1298
|
{ 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.2 → span_panel_api-2.5.4}/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
|