span-panel-api 3.0.0b8__tar.gz → 3.0.0b9__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 (85) hide show
  1. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/CHANGELOG.md +8 -0
  2. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/PKG-INFO +1 -1
  3. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/pyproject.toml +1 -1
  4. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/mqtt/client.py +14 -2
  5. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_redispatch_on_reconnect.py +32 -1
  6. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/.gitignore +0 -0
  7. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/LICENSE +0 -0
  8. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/README.md +0 -0
  9. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/__init__.py +0 -0
  10. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/_http.py +0 -0
  11. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/adapters.py +0 -0
  12. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/auth.py +0 -0
  13. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/const.py +0 -0
  14. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/detection.py +0 -0
  15. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/dispatch.py +0 -0
  16. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/exceptions.py +0 -0
  17. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/factory.py +0 -0
  18. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/models.py +0 -0
  19. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/mqtt/__init__.py +0 -0
  20. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/mqtt/async_client.py +0 -0
  21. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/mqtt/connection.py +0 -0
  22. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/mqtt/const.py +0 -0
  23. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/mqtt/models.py +0 -0
  24. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/phase_validation.py +0 -0
  25. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/protocol.py +0 -0
  26. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/py.typed +0 -0
  27. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/reference_payloads/README.md +0 -0
  28. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/reference_payloads/__init__.py +0 -0
  29. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/reference_payloads/homie_schema.json +0 -0
  30. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/src/span_panel_api/schema_drift.py +0 -0
  31. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/conftest.py +0 -0
  32. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/fixtures/configs/simulation_config_32_circuit.yaml +0 -0
  33. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/fixtures/configs/simulation_config_40_circuit_with_battery.yaml +0 -0
  34. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/fixtures/configs/simulation_config_8_tab_workshop.yaml +0 -0
  35. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/fixtures/flat_wire.json +0 -0
  36. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/fixtures/panelbench_unvalued_by_both.json +0 -0
  37. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/fixtures/v2/README.md +0 -0
  38. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/fixtures/v2/status.json +0 -0
  39. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/simulation_fixtures/circuits.response.txt +0 -0
  40. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/simulation_fixtures/panel.response.txt +0 -0
  41. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/simulation_fixtures/soe.response.txt +0 -0
  42. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/simulation_fixtures/status.response.txt +0 -0
  43. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_accumulator.py +0 -0
  44. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_adapters_discovery.py +0 -0
  45. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_adopted_control.py +0 -0
  46. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_adoption.py +0 -0
  47. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_async_mqtt_client.py +0 -0
  48. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_auth_and_homie_helpers.py +0 -0
  49. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_catalog_divergence.py +0 -0
  50. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_detection_auth.py +0 -0
  51. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_exceptions.py +0 -0
  52. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_factory_dispatch.py +0 -0
  53. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_field_metadata.py +0 -0
  54. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_live_flat_differential.py +0 -0
  55. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_mqtt_bridge.py +0 -0
  56. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_mqtt_client_connection.py +0 -0
  57. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_mqtt_connect_flow.py +0 -0
  58. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_mqtt_debounce.py +0 -0
  59. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_mqtt_homie.py +0 -0
  60. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_packaging.py +0 -0
  61. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_phase_validation_configs.py +0 -0
  62. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_phase_validation_errors.py +0 -0
  63. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_protocol_conformance.py +0 -0
  64. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_protocol_models.py +0 -0
  65. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_public_api_unchanged.py +0 -0
  66. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_reference_tree_values.py +0 -0
  67. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_generation_cross_check.py +0 -0
  68. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_migration_delta.py +0 -0
  69. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_adapter.py +0 -0
  70. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_against_simulator.py +0 -0
  71. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_charge_limit.py +0 -0
  72. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_circuits.py +0 -0
  73. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_conformance.py +0 -0
  74. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_connection_health.py +0 -0
  75. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_devices.py +0 -0
  76. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_discovery.py +0 -0
  77. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_panel.py +0 -0
  78. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_pcs.py +0 -0
  79. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_service_entrance.py +0 -0
  80. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_shed_forecast.py +0 -0
  81. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_snapshot.py +0 -0
  82. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_one_transport.py +0 -0
  83. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_provenance.py +0 -0
  84. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_schema_zero_adapter.py +0 -0
  85. {span_panel_api-3.0.0b8 → span_panel_api-3.0.0b9}/tests/test_shared_http_client.py +0 -0
@@ -4,6 +4,14 @@ 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
+ ## [3.0.0b9]
8
+
9
+ ### Fixed
10
+
11
+ - **The retry window for a rebooting panel is actually widened this time.** b8 taught the schema fetch to treat a `502` as "not ready yet" but shipped with the old five attempts capped at eight seconds — about twenty-three seconds against a panel observed
12
+ taking four minutes to come back, still answering 502 when the broker returned. The widening was written, lost to a failed edit in the same change, and shipped without it; nothing failed, because catching the 502 and giving up early looks exactly like
13
+ working. Now twelve attempts backing off to thirty seconds, a little over four minutes, and pinned by a test that asserts the total window outlasts the observed reboot rather than checking the constants individually.
14
+
7
15
  ## [3.0.0b8]
8
16
 
9
17
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: span-panel-api
3
- Version: 3.0.0b8
3
+ Version: 3.0.0b9
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "span-panel-api"
3
- version = "3.0.0b8"
3
+ version = "3.0.0b9"
4
4
  description = "A client library for SPAN Panel API"
5
5
  authors = [
6
6
  {name = "SpanPanel"}
@@ -48,9 +48,21 @@ _CIRCUIT_NAMES_POLL_INTERVAL_S = 0.25
48
48
  # Re-reading the schema after a suspected generation change. Bounded because the
49
49
  # caller is a fire-and-forget task on a live connection, and generous enough to
50
50
  # outlast a panel that is still binding its HTTP port after a restart.
51
- _REDISPATCH_RETRY_ATTEMPTS = 5
51
+ _REDISPATCH_RETRY_ATTEMPTS = 12
52
52
  _REDISPATCH_RETRY_INITIAL_S = 1.0
53
- _REDISPATCH_RETRY_MAX_S = 8.0
53
+ _REDISPATCH_RETRY_MAX_S = 30.0
54
+ """How long to wait for the panel's HTTP endpoint after it returns on MQTT.
55
+
56
+ Sized from a live firmware upgrade rather than guessed. The panel dropped MQTT at
57
+ 11:22:07 and the broker was back at 11:26:15 -- four minutes -- and its HTTP
58
+ front end was still answering 502 at that moment. Five attempts capped at 8s
59
+ gives up after about 23 seconds, which is not the same order of magnitude as a
60
+ device that is still booting: catching the 502 buys nothing if the loop stops
61
+ before the panel is ready.
62
+
63
+ Twelve attempts backing off to 30s is a little over four minutes. Each one is a
64
+ single GET, and the panel is the only thing that can end the wait.
65
+ """
54
66
 
55
67
 
56
68
  def _metadata_for_the_log() -> tuple[list[str], str]:
@@ -31,7 +31,12 @@ from unittest.mock import patch
31
31
  import pytest
32
32
 
33
33
  from span_panel_api.exceptions import SpanPanelConnectionError, SpanPanelServerError
34
- from span_panel_api.mqtt.client import _REDISPATCH_RETRY_ATTEMPTS, SpanMqttClient
34
+ from span_panel_api.mqtt.client import (
35
+ _REDISPATCH_RETRY_ATTEMPTS,
36
+ _REDISPATCH_RETRY_INITIAL_S,
37
+ _REDISPATCH_RETRY_MAX_S,
38
+ SpanMqttClient,
39
+ )
35
40
  from span_panel_api.mqtt.models import MqttClientConfig
36
41
 
37
42
  from conftest import SERIAL
@@ -364,3 +369,29 @@ async def test_an_unexpected_failure_leaves_a_usable_message_rather_than_a_bare_
364
369
  assert client.adapter is before
365
370
  assert "Reload the integration" in caplog.text
366
371
  assert "something nobody predicted" in caplog.text
372
+
373
+
374
+ def test_the_retry_window_outlasts_a_real_panel_reboot() -> None:
375
+ """Catching the 502 buys nothing if the loop gives up before the panel is ready.
376
+
377
+ Measured rather than assumed. On a live firmware upgrade the panel dropped
378
+ MQTT at 11:22:07 and the broker was back at 11:26:15 — four minutes — and its
379
+ HTTP front end was still answering 502 at that moment, which is when this
380
+ loop starts.
381
+
382
+ Pinned as a total because the three constants only mean something together,
383
+ and because the widening was written once, lost to a failed edit, and shipped
384
+ without it. Nothing failed: the 502 was caught and the loop still gave up
385
+ after twenty-three seconds. A test on the constants is the only thing that
386
+ would have noticed.
387
+ """
388
+ delay = _REDISPATCH_RETRY_INITIAL_S
389
+ total = 0.0
390
+ for _ in range(_REDISPATCH_RETRY_ATTEMPTS):
391
+ total += delay
392
+ delay = min(delay * 2, _REDISPATCH_RETRY_MAX_S)
393
+
394
+ observed_reboot_s = 4 * 60
395
+ assert total >= observed_reboot_s, (
396
+ f"the retry window is {total:.0f}s, shorter than the {observed_reboot_s}s reboot " "this loop exists to wait out"
397
+ )