python-bsblan 6.1.6__tar.gz → 6.1.8__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.
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/codeql.yaml +3 -3
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/dependency-review.yaml +1 -1
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/docs.yaml +2 -2
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/labels.yaml +1 -1
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/linting.yaml +5 -5
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/release-drafter.yaml +1 -1
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/release.yaml +3 -3
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/scorecard.yml +3 -3
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/stale.yaml +1 -1
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/tests.yaml +4 -4
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/typing.yaml +2 -2
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/PKG-INFO +1 -1
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/docs/api/exceptions.md +8 -0
- python_bsblan-6.1.8/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/.openspec.yaml +3 -0
- python_bsblan-6.1.8/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/design.md +101 -0
- python_bsblan-6.1.8/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/proposal.md +49 -0
- python_bsblan-6.1.8/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/specs/error-classification/spec.md +50 -0
- python_bsblan-6.1.8/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/tasks.md +28 -0
- python_bsblan-6.1.8/openspec/changes/archive/2026-07-16-fix-partial-validation-state/.openspec.yaml +3 -0
- python_bsblan-6.1.8/openspec/changes/archive/2026-07-16-fix-partial-validation-state/design.md +86 -0
- python_bsblan-6.1.8/openspec/changes/archive/2026-07-16-fix-partial-validation-state/proposal.md +38 -0
- python_bsblan-6.1.8/openspec/changes/archive/2026-07-16-fix-partial-validation-state/specs/lazy-validation-completeness/spec.md +63 -0
- python_bsblan-6.1.8/openspec/changes/archive/2026-07-16-fix-partial-validation-state/tasks.md +29 -0
- python_bsblan-6.1.8/openspec/specs/error-classification/spec.md +59 -0
- python_bsblan-6.1.8/openspec/specs/lazy-validation-completeness/spec.md +71 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/package-lock.json +4 -4
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/package.json +1 -1
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/pyproject.toml +2 -2
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/__init__.py +4 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/_hot_water.py +7 -2
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/_schedules.py +4 -3
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/_transport.py +5 -3
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/_validation.py +104 -51
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/exceptions.py +17 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/models.py +7 -2
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/utility.py +31 -6
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_api_validation.py +130 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_bsblan_edge_cases.py +11 -2
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_entity_info.py +2 -1
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_heating_schedule.py +9 -3
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_hot_water_additional.py +193 -12
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_include_parameter.py +3 -2
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_reset_validation.py +12 -0
- python_bsblan-6.1.6/.github/hooks/run-validation-after-edits.json +0 -11
- python_bsblan-6.1.6/.github/hooks/run_validation_after_edits.sh +0 -92
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.editorconfig +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.gitattributes +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/CODE_OF_CONDUCT.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/CONTRIBUTING.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/ISSUE_TEMPLATE/PULL_REQUEST_TEMPLATE.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/SECURITY.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/agents/pr-review.agent.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/agents/security-reviewer.agent.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/agents/tdd-refactor.agent.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/copilot-instructions.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/labels.yml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/prompts/add-parameter.prompt.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/prompts/code-review.prompt.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/prompts/opsx-apply.prompt.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/prompts/opsx-archive.prompt.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/prompts/opsx-explore.prompt.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/prompts/opsx-propose.prompt.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/prompts/opsx-sync.prompt.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/release-drafter.yml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/renovate.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/skills/bsblan-parameters/SKILL.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/skills/bsblan-testing/SKILL.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/skills/feature-doc-updates/SKILL.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/skills/openspec-apply-change/SKILL.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/skills/openspec-archive-change/SKILL.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/skills/openspec-explore/SKILL.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/skills/openspec-propose/SKILL.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/skills/openspec-sync-specs/SKILL.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/auto-approve-renovate.yml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/lock.yaml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/workflows/pr-labels.yaml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.github/zizmor.yml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.gitignore +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.nvmrc +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.pre-commit-config.yaml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.prettierignore +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/.yamllint +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/AGENTS.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/CLAUDE.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/LICENSE.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/Makefile +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/README.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/docs/api/client.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/docs/api/constants.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/docs/api/models.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/docs/getting-started.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/docs/index.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/examples/control.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/examples/discovery.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/examples/fetch_param.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/examples/profile_init.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/examples/ruff.toml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/examples/speed_test.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/mkdocs.yml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/.openspec.yaml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/design.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/proposal.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/specs/cooling-operating-mode/spec.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/tasks.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/.openspec.yaml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/design.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/proposal.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/specs/circuit-discovery/spec.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/tasks.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/.openspec.yaml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/design.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/proposal.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/specs/client-retry-policy/spec.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/tasks.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/config.yaml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/specs/circuit-discovery/spec.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/specs/client-retry-policy/spec.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/openspec/specs/cooling-operating-mode/spec.md +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/sonar-project.properties +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/_parameters.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/_temperature.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/_thermostat.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/_version.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/bsblan.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/constants.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/src/bsblan/py.typed +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/__init__.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/conftest.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/device.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/dict_version.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/hot_water_state.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/info.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/password.txt +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/pps_device.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/pps_state.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/pps_static_values.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/sensor.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/state.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/state_circuit2.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/static_state.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/static_state_circuit2.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/thermostat_hvac.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/thermostat_temp.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/fixtures/time.json +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/ruff.toml +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_api_initialization.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_auth.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_backoff_retry.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_bsblan.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_circuit.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_configuration.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_constants.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_context_manager.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_device.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_dhw_time_switch.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_entity_info_ha.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_hotwater_state.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_info.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_initialization.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_pps.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_read_parameters.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_response_encoding.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_schedule_models.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_sensor.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_set_heating_schedule.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_set_hot_water_schedule.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_set_hotwater.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_state.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_static_state.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_temperature_unit.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_temperature_validation.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_thermostat.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_time.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_utility.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_utility_additional.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_utility_edge_cases.py +0 -0
- {python_bsblan-6.1.6 → python_bsblan-6.1.8}/tests/test_version_errors.py +0 -0
|
@@ -23,10 +23,10 @@ jobs:
|
|
|
23
23
|
security-events: write
|
|
24
24
|
steps:
|
|
25
25
|
- name: ⤵️ Check out code from GitHub
|
|
26
|
-
uses: actions/checkout@
|
|
26
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
27
27
|
with:
|
|
28
28
|
persist-credentials: false
|
|
29
29
|
- name: 🏗 Initialize CodeQL
|
|
30
|
-
uses: github/codeql-action/init@
|
|
30
|
+
uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
|
31
31
|
- name: 🚀 Perform CodeQL Analysis
|
|
32
|
-
uses: github/codeql-action/analyze@
|
|
32
|
+
uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
|
@@ -13,7 +13,7 @@ jobs:
|
|
|
13
13
|
steps:
|
|
14
14
|
- name: ⤵️ Check out code from GitHub
|
|
15
15
|
# yamllint disable-line rule:line-length
|
|
16
|
-
uses: actions/checkout@
|
|
16
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
17
17
|
with:
|
|
18
18
|
persist-credentials: false
|
|
19
19
|
- name: 👀 Dependency review
|
|
@@ -23,11 +23,11 @@ jobs:
|
|
|
23
23
|
runs-on: ubuntu-latest
|
|
24
24
|
steps:
|
|
25
25
|
- name: ⤵️ Check out code from GitHub
|
|
26
|
-
uses: actions/checkout@
|
|
26
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
27
27
|
with:
|
|
28
28
|
persist-credentials: false
|
|
29
29
|
- name: 🏗 Set up uv
|
|
30
|
-
uses: astral-sh/setup-uv@
|
|
30
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
31
31
|
with:
|
|
32
32
|
enable-cache: true
|
|
33
33
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -21,7 +21,7 @@ jobs:
|
|
|
21
21
|
issues: write
|
|
22
22
|
steps:
|
|
23
23
|
- name: ⤵️ Check out code from GitHub
|
|
24
|
-
uses: actions/checkout@
|
|
24
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
25
25
|
with:
|
|
26
26
|
persist-credentials: false
|
|
27
27
|
- name: 🚀 Run Label Syncer
|
|
@@ -21,11 +21,11 @@ jobs:
|
|
|
21
21
|
runs-on: ubuntu-latest
|
|
22
22
|
steps:
|
|
23
23
|
- name: ⤵️ Check out code from GitHub
|
|
24
|
-
uses: actions/checkout@
|
|
24
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
25
25
|
with:
|
|
26
26
|
persist-credentials: false
|
|
27
27
|
- name: 🏗 Set up uv
|
|
28
|
-
uses: astral-sh/setup-uv@
|
|
28
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
29
29
|
with:
|
|
30
30
|
enable-cache: true
|
|
31
31
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -45,11 +45,11 @@ jobs:
|
|
|
45
45
|
runs-on: ubuntu-latest
|
|
46
46
|
steps:
|
|
47
47
|
- name: ⤵️ Check out code from GitHub
|
|
48
|
-
uses: actions/checkout@
|
|
48
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
49
49
|
with:
|
|
50
50
|
persist-credentials: false
|
|
51
51
|
- name: 🏗 Set up uv
|
|
52
|
-
uses: astral-sh/setup-uv@
|
|
52
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
53
53
|
with:
|
|
54
54
|
enable-cache: true
|
|
55
55
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -60,7 +60,7 @@ jobs:
|
|
|
60
60
|
- name: 🏗 Install Python dependencies
|
|
61
61
|
run: uv sync --dev
|
|
62
62
|
- name: 🏗 Set up Node.js
|
|
63
|
-
uses: actions/setup-node@
|
|
63
|
+
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
|
64
64
|
with:
|
|
65
65
|
node-version-file: ".nvmrc"
|
|
66
66
|
cache: "npm"
|
|
@@ -36,7 +36,7 @@ jobs:
|
|
|
36
36
|
steps:
|
|
37
37
|
- name: 🚀 Run Release Drafter
|
|
38
38
|
# yamllint disable-line rule:line-length
|
|
39
|
-
uses: release-drafter/release-drafter@
|
|
39
|
+
uses: release-drafter/release-drafter@eada3c96a64734dd381cfbda23511034e328ddb0 # v7.6.0
|
|
40
40
|
with:
|
|
41
41
|
prerelease: ${{ github.event.inputs.prerelease == 'true' }}
|
|
42
42
|
prerelease-identifier: ${{ github.event.inputs.prerelease_identifier }}
|
|
@@ -25,11 +25,11 @@ jobs:
|
|
|
25
25
|
id-token: write
|
|
26
26
|
steps:
|
|
27
27
|
- name: ⤵️ Check out code from GitHub
|
|
28
|
-
uses: actions/checkout@
|
|
28
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
29
29
|
with:
|
|
30
30
|
persist-credentials: false
|
|
31
31
|
- name: 🏗 Set up uv
|
|
32
|
-
uses: astral-sh/setup-uv@
|
|
32
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
33
33
|
with:
|
|
34
34
|
enable-cache: false
|
|
35
35
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -54,7 +54,7 @@ jobs:
|
|
|
54
54
|
with:
|
|
55
55
|
subject-path: ./dist/*
|
|
56
56
|
- name: 🚀 Publish to PyPi
|
|
57
|
-
uses: pypa/gh-action-pypi-publish@
|
|
57
|
+
uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
|
|
58
58
|
with:
|
|
59
59
|
verbose: true
|
|
60
60
|
print-hash: true
|
|
@@ -34,13 +34,13 @@ jobs:
|
|
|
34
34
|
steps:
|
|
35
35
|
- name: "Checkout code"
|
|
36
36
|
# yamllint disable-line rule:line-length
|
|
37
|
-
uses: actions/checkout@
|
|
37
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
38
38
|
with:
|
|
39
39
|
persist-credentials: false
|
|
40
40
|
|
|
41
41
|
- name: "Run analysis"
|
|
42
42
|
# yamllint disable-line rule:line-length
|
|
43
|
-
uses: ossf/scorecard-action@
|
|
43
|
+
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
|
|
44
44
|
with:
|
|
45
45
|
results_file: results.sarif
|
|
46
46
|
results_format: sarif
|
|
@@ -66,6 +66,6 @@ jobs:
|
|
|
66
66
|
# Upload the results to GitHub's code scanning dashboard (optional).
|
|
67
67
|
- name: "Upload to code-scanning"
|
|
68
68
|
# yamllint disable-line rule:line-length
|
|
69
|
-
uses: github/codeql-action/upload-sarif@
|
|
69
|
+
uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
|
70
70
|
with:
|
|
71
71
|
sarif_file: results.sarif
|
|
@@ -18,7 +18,7 @@ jobs:
|
|
|
18
18
|
pull-requests: write
|
|
19
19
|
steps:
|
|
20
20
|
- name: 🚀 Run stale
|
|
21
|
-
uses: actions/stale@
|
|
21
|
+
uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0
|
|
22
22
|
with:
|
|
23
23
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
24
24
|
days-before-stale: 30
|
|
@@ -29,11 +29,11 @@ jobs:
|
|
|
29
29
|
python: ["3.12", "3.13", "3.14"]
|
|
30
30
|
steps:
|
|
31
31
|
- name: ⤵️ Check out code from GitHub
|
|
32
|
-
uses: actions/checkout@
|
|
32
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
33
33
|
with:
|
|
34
34
|
persist-credentials: false
|
|
35
35
|
- name: 🏗 Set up uv
|
|
36
|
-
uses: astral-sh/setup-uv@
|
|
36
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
37
37
|
with:
|
|
38
38
|
enable-cache: true
|
|
39
39
|
- name: 🏗 Set up Python ${{ matrix.python }}
|
|
@@ -61,14 +61,14 @@ jobs:
|
|
|
61
61
|
pull-requests: read
|
|
62
62
|
steps:
|
|
63
63
|
- name: ⤵️ Check out code from GitHub
|
|
64
|
-
uses: actions/checkout@
|
|
64
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
65
65
|
with:
|
|
66
66
|
fetch-depth: 0
|
|
67
67
|
persist-credentials: false
|
|
68
68
|
- name: ⬇️ Download coverage data
|
|
69
69
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
70
70
|
- name: 🏗 Set up uv
|
|
71
|
-
uses: astral-sh/setup-uv@
|
|
71
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
72
72
|
with:
|
|
73
73
|
enable-cache: true
|
|
74
74
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -22,12 +22,12 @@ jobs:
|
|
|
22
22
|
steps:
|
|
23
23
|
- name: ⤵️ Check out code from GitHub
|
|
24
24
|
# yamllint disable-line rule:line-length
|
|
25
|
-
uses: actions/checkout@
|
|
25
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
26
26
|
with:
|
|
27
27
|
persist-credentials: false
|
|
28
28
|
- name: 🏗 Set up uv
|
|
29
29
|
# yamllint disable-line rule:line-length
|
|
30
|
-
uses: astral-sh/setup-uv@
|
|
30
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
31
31
|
with:
|
|
32
32
|
enable-cache: true
|
|
33
33
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -13,3 +13,11 @@ Custom exceptions raised by the library.
|
|
|
13
13
|
## BSBLANAuthError
|
|
14
14
|
|
|
15
15
|
::: bsblan.BSBLANAuthError
|
|
16
|
+
|
|
17
|
+
## BSBLANUnsupportedFeatureError
|
|
18
|
+
|
|
19
|
+
::: bsblan.BSBLANUnsupportedFeatureError
|
|
20
|
+
|
|
21
|
+
## BSBLANMalformedResponseError
|
|
22
|
+
|
|
23
|
+
::: bsblan.BSBLANMalformedResponseError
|
python_bsblan-6.1.8/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/design.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
## Context
|
|
2
|
+
|
|
3
|
+
`python-bsblan` currently exposes a single generic `BSBLANError` for a wide
|
|
4
|
+
range of failure modes. Two of them are semantically opposite from a retry
|
|
5
|
+
standpoint:
|
|
6
|
+
|
|
7
|
+
- **Unsupported schedule** — the device does not expose the requested
|
|
8
|
+
time-program parameters. In `_schedules.heating_schedule()` this raises
|
|
9
|
+
`BSBLANError(ErrorMsg.NO_HEATING_SCHEDULE_PARAMS)`, and in
|
|
10
|
+
`_hot_water._get_filtered_data()` it raises `BSBLANError(error_msg)` for the
|
|
11
|
+
hot-water schedule group. This is a **permanent** condition.
|
|
12
|
+
- **Malformed response** — the transport cannot decode or JSON-parse the body.
|
|
13
|
+
In `_transport.request_with_retry()` this is caught from `ValueError` /
|
|
14
|
+
`UnicodeDecodeError` and re-raised as
|
|
15
|
+
`BSBLANError(ErrorMsg.INVALID_RESPONSE.format(e))`. This is a **transient**
|
|
16
|
+
condition.
|
|
17
|
+
|
|
18
|
+
Because both surface as `BSBLANError`, a downstream integration (Home Assistant)
|
|
19
|
+
cannot decide whether to retry. It must either stop retrying (losing recovery
|
|
20
|
+
from transient malformed responses) or retry forever (uselessly hammering a
|
|
21
|
+
device that will never support a schedule after a write).
|
|
22
|
+
|
|
23
|
+
The library's own `backoff` decorator only retries `TimeoutError` and
|
|
24
|
+
`aiohttp.ClientError`, so `BSBLANError` is never retried inside the library; the
|
|
25
|
+
retry decision lives in the consumer. The fix is therefore an additive exception
|
|
26
|
+
taxonomy, not a change to the transport retry policy.
|
|
27
|
+
|
|
28
|
+
## Goals / Non-Goals
|
|
29
|
+
|
|
30
|
+
**Goals:**
|
|
31
|
+
- Let callers distinguish permanent unsupported-feature failures from transient
|
|
32
|
+
malformed-response failures by `except` type.
|
|
33
|
+
- Keep the change fully backward compatible with existing `except BSBLANError`
|
|
34
|
+
handlers.
|
|
35
|
+
- Reuse existing `ErrorMsg` strings; no message wording changes.
|
|
36
|
+
|
|
37
|
+
**Non-Goals:**
|
|
38
|
+
- Changing the transport-level `backoff` retry policy or which HTTP statuses are
|
|
39
|
+
retried (`client-retry-policy` is unchanged).
|
|
40
|
+
- Adding automatic retry logic inside the library for schedules.
|
|
41
|
+
- Reclassifying other existing `BSBLANError` raise sites beyond schedule reads
|
|
42
|
+
and malformed-response parsing.
|
|
43
|
+
|
|
44
|
+
## Decisions
|
|
45
|
+
|
|
46
|
+
**Decision: Two new subclasses of `BSBLANError`.**
|
|
47
|
+
Add `BSBLANUnsupportedFeatureError` (permanent) and
|
|
48
|
+
`BSBLANMalformedResponseError` (transient), both subclassing `BSBLANError`.
|
|
49
|
+
Subclassing preserves backward compatibility: any existing `except BSBLANError`
|
|
50
|
+
keeps catching them.
|
|
51
|
+
- *Alternative considered:* a single new error with a `retryable: bool`
|
|
52
|
+
attribute. Rejected — attribute inspection is less idiomatic than `except`
|
|
53
|
+
type matching and easier to get wrong at call sites.
|
|
54
|
+
- *Alternative considered:* schedule-specific name (e.g.
|
|
55
|
+
`BSBLANUnsupportedScheduleError`). Chose the more general
|
|
56
|
+
`BSBLANUnsupportedFeatureError` because the same permanent semantics apply to
|
|
57
|
+
both heating and hot-water schedule reads and can extend to other
|
|
58
|
+
unsupported-parameter cases later.
|
|
59
|
+
|
|
60
|
+
**Decision: Raise the permanent error at the schedule-read guard sites.**
|
|
61
|
+
Replace the bare `BSBLANError(...)` at
|
|
62
|
+
`_schedules.heating_schedule()` (no mapped data) and the hot-water schedule
|
|
63
|
+
branch in `_hot_water._get_filtered_data()` with
|
|
64
|
+
`BSBLANUnsupportedFeatureError`, keeping the existing `ErrorMsg` text. The
|
|
65
|
+
hot-water helper is shared by state/config/schedule reads, so scope the new
|
|
66
|
+
error to the schedule group only (via `group_name == "schedule"`) to avoid
|
|
67
|
+
reclassifying unrelated state/config failures.
|
|
68
|
+
|
|
69
|
+
**Decision: Raise the transient error at the transport parse site.**
|
|
70
|
+
In `_transport.request_with_retry()`, replace the
|
|
71
|
+
`raise BSBLANError(msg)` in the `(ValueError, UnicodeDecodeError)` handler with
|
|
72
|
+
`raise BSBLANMalformedResponseError(msg)`, reusing
|
|
73
|
+
`ErrorMsg.INVALID_RESPONSE`.
|
|
74
|
+
|
|
75
|
+
**Decision: Export the new exceptions from the package.**
|
|
76
|
+
Add both classes to `bsblan.exceptions` and to the package `__all__` /
|
|
77
|
+
re-exports so consumers can import them from the top-level `bsblan` namespace.
|
|
78
|
+
|
|
79
|
+
## Risks / Trade-offs
|
|
80
|
+
|
|
81
|
+
- **[Consumers catching the narrow generic message string]** → Mitigation:
|
|
82
|
+
messages are unchanged; only the concrete type is narrower. Broad
|
|
83
|
+
`except BSBLANError` still works.
|
|
84
|
+
- **[Over-scoping the hot-water helper]** → Mitigation: gate the new error on the
|
|
85
|
+
schedule group so state/config reads keep raising the existing generic error
|
|
86
|
+
and existing tests stay green.
|
|
87
|
+
- **[Missed raise site]** → Mitigation: grep for `NO_HEATING_SCHEDULE_PARAMS`,
|
|
88
|
+
the hot-water schedule `error_msg`, and `INVALID_RESPONSE` to confirm all
|
|
89
|
+
targeted sites are updated; add tests asserting the concrete types.
|
|
90
|
+
|
|
91
|
+
## Migration Plan
|
|
92
|
+
|
|
93
|
+
Additive, no migration required. Release as a minor version. Consumers may
|
|
94
|
+
optionally add narrower `except BSBLANUnsupportedFeatureError` /
|
|
95
|
+
`except BSBLANMalformedResponseError` handlers; existing code is unaffected.
|
|
96
|
+
|
|
97
|
+
## Open Questions
|
|
98
|
+
|
|
99
|
+
- Final class names: `BSBLANUnsupportedFeatureError` vs
|
|
100
|
+
`BSBLANUnsupportedScheduleError`. Proposed the broader name; confirm during
|
|
101
|
+
review.
|
python_bsblan-6.1.8/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/proposal.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
## Why
|
|
2
|
+
|
|
3
|
+
In python-bsblan 6.1.6 both an *unsupported schedule* (a device that will never
|
|
4
|
+
return the requested time-program parameters) and a *malformed response* (a
|
|
5
|
+
transient decode/parse failure) are surfaced as the same generic `BSBLANError`.
|
|
6
|
+
Downstream integrations such as Home Assistant cannot tell them apart by
|
|
7
|
+
exception type, so they must choose between stopping retries on a transient
|
|
8
|
+
malformed response or retrying an unsupported schedule indefinitely after a
|
|
9
|
+
write. A dedicated exception taxonomy is needed at the library level to make the
|
|
10
|
+
permanent-vs-transient distinction explicit.
|
|
11
|
+
|
|
12
|
+
## What Changes
|
|
13
|
+
|
|
14
|
+
- Add a dedicated **permanent** exception `BSBLANUnsupportedFeatureError`
|
|
15
|
+
(subclass of `BSBLANError`) raised when a device does not expose the requested
|
|
16
|
+
schedule parameters, so callers know retrying will never succeed.
|
|
17
|
+
- Add a dedicated **transient** exception `BSBLANMalformedResponseError`
|
|
18
|
+
(subclass of `BSBLANError`) raised when a response body cannot be decoded or
|
|
19
|
+
parsed as valid JSON, signalling that a retry may succeed.
|
|
20
|
+
- Raise `BSBLANUnsupportedFeatureError` in the heating- and hot-water-schedule
|
|
21
|
+
read paths where a bare `BSBLANError` is currently raised for absent schedule
|
|
22
|
+
parameters.
|
|
23
|
+
- Raise `BSBLANMalformedResponseError` in the transport layer where invalid
|
|
24
|
+
JSON / decode errors are currently wrapped in a bare `BSBLANError`.
|
|
25
|
+
- Both new exceptions subclass `BSBLANError`, so existing `except BSBLANError`
|
|
26
|
+
handlers keep working (non-breaking).
|
|
27
|
+
|
|
28
|
+
## Capabilities
|
|
29
|
+
|
|
30
|
+
### New Capabilities
|
|
31
|
+
- `error-classification`: A library exception taxonomy that distinguishes
|
|
32
|
+
permanent unsupported-feature failures from transient malformed-response
|
|
33
|
+
failures, letting callers make correct retry decisions.
|
|
34
|
+
|
|
35
|
+
### Modified Capabilities
|
|
36
|
+
<!-- No existing spec's requirements change; client-retry-policy behavior is unaffected. -->
|
|
37
|
+
|
|
38
|
+
## Impact
|
|
39
|
+
|
|
40
|
+
- `src/bsblan/exceptions.py`: two new exception classes.
|
|
41
|
+
- `src/bsblan/_schedules.py` and `src/bsblan/_hot_water.py`: raise the new
|
|
42
|
+
unsupported-feature error for absent schedule parameters.
|
|
43
|
+
- `src/bsblan/_transport.py`: raise the new malformed-response error for
|
|
44
|
+
decode/parse failures.
|
|
45
|
+
- `src/bsblan/constants.py`: no new messages required; existing `ErrorMsg`
|
|
46
|
+
entries are reused.
|
|
47
|
+
- `tests/`: new coverage asserting the specific exception types are raised.
|
|
48
|
+
- Public API: additive only. Consumers relying on `BSBLANError` are unaffected;
|
|
49
|
+
consumers can opt into finer-grained handling.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Unsupported schedules raise a permanent error
|
|
4
|
+
|
|
5
|
+
The client SHALL raise `BSBLANUnsupportedFeatureError` (a subclass of
|
|
6
|
+
`BSBLANError`) when a schedule read cannot proceed because the device exposes no
|
|
7
|
+
schedule parameters for the requested feature. This error MUST signal a
|
|
8
|
+
permanent condition so callers know that retrying the same request will never
|
|
9
|
+
succeed.
|
|
10
|
+
|
|
11
|
+
#### Scenario: Heating schedule parameters absent
|
|
12
|
+
|
|
13
|
+
- **WHEN** `heating_schedule()` is called for a circuit whose time-program
|
|
14
|
+
parameters are not present in the device response (no schedule parameters can
|
|
15
|
+
be mapped)
|
|
16
|
+
- **THEN** the client raises `BSBLANUnsupportedFeatureError`
|
|
17
|
+
- **AND** the raised exception is also an instance of `BSBLANError`
|
|
18
|
+
|
|
19
|
+
#### Scenario: Hot water schedule parameters absent
|
|
20
|
+
|
|
21
|
+
- **WHEN** `schedule()` is called on a device that exposes no hot-water
|
|
22
|
+
time-program parameters after include filtering
|
|
23
|
+
- **THEN** the client raises `BSBLANUnsupportedFeatureError`
|
|
24
|
+
- **AND** the raised exception is also an instance of `BSBLANError`
|
|
25
|
+
|
|
26
|
+
### Requirement: Malformed responses raise a transient error
|
|
27
|
+
|
|
28
|
+
The client SHALL raise `BSBLANMalformedResponseError` (a subclass of
|
|
29
|
+
`BSBLANError`) when a device response body cannot be decoded or parsed as valid
|
|
30
|
+
JSON. This error MUST signal a transient condition so callers know that a retry
|
|
31
|
+
may succeed.
|
|
32
|
+
|
|
33
|
+
#### Scenario: Response body is not valid JSON
|
|
34
|
+
|
|
35
|
+
- **WHEN** a request receives an HTTP `200` response whose body cannot be parsed
|
|
36
|
+
as JSON
|
|
37
|
+
- **THEN** the client raises `BSBLANMalformedResponseError`
|
|
38
|
+
- **AND** the raised exception is also an instance of `BSBLANError`
|
|
39
|
+
|
|
40
|
+
### Requirement: New exceptions preserve backward compatibility
|
|
41
|
+
|
|
42
|
+
Both `BSBLANUnsupportedFeatureError` and `BSBLANMalformedResponseError` SHALL
|
|
43
|
+
subclass `BSBLANError` so that existing `except BSBLANError` handlers continue to
|
|
44
|
+
catch them without modification.
|
|
45
|
+
|
|
46
|
+
#### Scenario: Existing broad handler still catches new errors
|
|
47
|
+
|
|
48
|
+
- **WHEN** code catches `BSBLANError` around a call that raises either
|
|
49
|
+
`BSBLANUnsupportedFeatureError` or `BSBLANMalformedResponseError`
|
|
50
|
+
- **THEN** the exception is caught by the `BSBLANError` handler
|
python_bsblan-6.1.8/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/tasks.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
## 1. Exceptions
|
|
2
|
+
|
|
3
|
+
- [x] 1.1 Add `BSBLANUnsupportedFeatureError(BSBLANError)` to `src/bsblan/exceptions.py` with a docstring describing it as a permanent (non-retryable) condition.
|
|
4
|
+
- [x] 1.2 Add `BSBLANMalformedResponseError(BSBLANError)` to `src/bsblan/exceptions.py` with a docstring describing it as a transient (retryable) condition.
|
|
5
|
+
- [x] 1.3 Import and re-export both new exceptions in `src/bsblan/__init__.py` and add them to `__all__`.
|
|
6
|
+
|
|
7
|
+
## 2. Schedule read paths (permanent error)
|
|
8
|
+
|
|
9
|
+
- [x] 2.1 In `src/bsblan/_schedules.py` `heating_schedule()`, raise `BSBLANUnsupportedFeatureError(ErrorMsg.NO_HEATING_SCHEDULE_PARAMS)` when no schedule parameters are mapped (replace the bare `BSBLANError`).
|
|
10
|
+
- [x] 2.2 In `src/bsblan/_hot_water.py` `fetch_data()`, raise `BSBLANUnsupportedFeatureError(error_msg)` when the schedule group (`group_name == "schedule"`) has no filtered params, keeping other groups on the existing generic error.
|
|
11
|
+
- [x] 2.3 Update the `Raises:` docstrings on the affected schedule methods to reference the new exception type.
|
|
12
|
+
|
|
13
|
+
## 3. Transport parse path (transient error)
|
|
14
|
+
|
|
15
|
+
- [x] 3.1 In `src/bsblan/_transport.py` `request_with_retry()`, raise `BSBLANMalformedResponseError(msg)` from the `(ValueError, UnicodeDecodeError)` handler (replace the bare `BSBLANError`), reusing `ErrorMsg.INVALID_RESPONSE`.
|
|
16
|
+
- [x] 3.2 Update the `Raises:` docstring on `request_with_retry()` to reference the new exception type.
|
|
17
|
+
|
|
18
|
+
## 4. Tests
|
|
19
|
+
|
|
20
|
+
- [x] 4.1 Add a test asserting `heating_schedule()` raises `BSBLANUnsupportedFeatureError` when schedule params are absent, and that it is also a `BSBLANError`.
|
|
21
|
+
- [x] 4.2 Add a test asserting hot-water `schedule()` raises `BSBLANUnsupportedFeatureError` when schedule params are absent.
|
|
22
|
+
- [x] 4.3 Add a test asserting a non-JSON / undecodable response raises `BSBLANMalformedResponseError` and that it is also a `BSBLANError`.
|
|
23
|
+
- [x] 4.4 Verify existing state/config hot-water tests still raise the generic `BSBLANError` (no regression from the group gating).
|
|
24
|
+
|
|
25
|
+
## 5. Documentation & validation
|
|
26
|
+
|
|
27
|
+
- [x] 5.1 Document the two new exceptions in `docs/api/exceptions.md`.
|
|
28
|
+
- [x] 5.2 Run `uv run prek run --all-files` and `uv run pytest --cov=src/bsblan --cov-report=term-missing`; ensure total coverage stays >= 95% and patch coverage is 100%.
|
python_bsblan-6.1.8/openspec/changes/archive/2026-07-16-fix-partial-validation-state/design.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
## Context
|
|
2
|
+
|
|
3
|
+
`SectionValidator` lazily validates device parameter support before a section or
|
|
4
|
+
hot-water group is read. It currently stores completion as a section-wide set in
|
|
5
|
+
`APIValidator` and a group-wide set in `SectionValidator`. An `include` request
|
|
6
|
+
validates only a subset but records that coarse completion state, so a later
|
|
7
|
+
request for different names or all names can skip validation.
|
|
8
|
+
|
|
9
|
+
The resulting configuration has not established whether every requested
|
|
10
|
+
parameter is supported. For normal sections, an unvalidated parameter can remain
|
|
11
|
+
in the configuration and later produce a strict response-mapping failure. For
|
|
12
|
+
hot water, the cache contains only the first subset and later reads silently use
|
|
13
|
+
that incomplete cache.
|
|
14
|
+
|
|
15
|
+
## Goals / Non-Goals
|
|
16
|
+
|
|
17
|
+
**Goals:**
|
|
18
|
+
|
|
19
|
+
- Record lazy-validation coverage by parameter ID for sections and hot-water
|
|
20
|
+
groups.
|
|
21
|
+
- Revalidate only the parameter IDs required by a later request when they have
|
|
22
|
+
not already been covered.
|
|
23
|
+
- Retain the existing public method signatures, include-filter validation, and
|
|
24
|
+
per-section/group locking model.
|
|
25
|
+
- Add regression tests for sequential filtered and unfiltered access.
|
|
26
|
+
|
|
27
|
+
**Non-Goals:**
|
|
28
|
+
|
|
29
|
+
- Change the public `include` API or make it accept parameter IDs.
|
|
30
|
+
- Alter device-response parsing, model serialization, or unsupported-parameter
|
|
31
|
+
rules.
|
|
32
|
+
- Retry or cache failed validation requests.
|
|
33
|
+
|
|
34
|
+
## Decisions
|
|
35
|
+
|
|
36
|
+
### Track covered parameter IDs per validation owner
|
|
37
|
+
|
|
38
|
+
`APIValidator` will maintain a mapping from section name to the set of parameter
|
|
39
|
+
IDs whose support was checked. `SectionValidator` will maintain the equivalent
|
|
40
|
+
mapping for hot-water group names. Completion for a request is determined by
|
|
41
|
+
whether its requested IDs are a subset of that owner’s covered IDs.
|
|
42
|
+
|
|
43
|
+
Parameter IDs are chosen over parameter names because they are the identifiers
|
|
44
|
+
sent to BSB-LAN and remain unambiguous if a configuration ever aliases a name.
|
|
45
|
+
When a response establishes an ID is unsupported, it is removed from the active
|
|
46
|
+
configuration/cache but remains covered, preventing needless repeated probes.
|
|
47
|
+
|
|
48
|
+
Alternative considered: avoid persisting any completion after an `include`
|
|
49
|
+
request. This avoids the defect but discards useful knowledge and repeatedly
|
|
50
|
+
validates already checked parameters. Parameter-level coverage keeps lazy
|
|
51
|
+
loading efficient while remaining correct.
|
|
52
|
+
|
|
53
|
+
### Derive requested IDs before the one-time gate
|
|
54
|
+
|
|
55
|
+
Each ensure method will first resolve the configured IDs for its section or
|
|
56
|
+
group, applying `include` when present. Its lock’s fast and post-lock checks will
|
|
57
|
+
compare that specific requested-ID set to coverage rather than use a coarse
|
|
58
|
+
section/group marker. A full request resolves all currently configured IDs and
|
|
59
|
+
therefore completes only after each is covered.
|
|
60
|
+
|
|
61
|
+
An include filter that matches no IDs will not add coverage or mark a group or
|
|
62
|
+
section complete. Existing downstream include validation remains responsible for
|
|
63
|
+
returning the appropriate invalid-include error.
|
|
64
|
+
|
|
65
|
+
Alternative considered: retain a boolean for full validation plus a separate
|
|
66
|
+
partial set. This duplicates state and makes configuration changes harder to
|
|
67
|
+
reason about; one coverage mapping expresses both cases.
|
|
68
|
+
|
|
69
|
+
### Preserve locks and recheck coverage after waiting
|
|
70
|
+
|
|
71
|
+
The existing per-section and per-group locks remain in place. The completion
|
|
72
|
+
predicate will calculate coverage for the current requested IDs both before and
|
|
73
|
+
after lock acquisition. Concurrent calls requesting different subsets therefore
|
|
74
|
+
serialize safely, and the second call still validates its uncovered IDs.
|
|
75
|
+
|
|
76
|
+
## Risks / Trade-offs
|
|
77
|
+
|
|
78
|
+
- [Configuration mutation during validation] → Derive the requested IDs from
|
|
79
|
+
the current configuration at each completion check; removed unsupported IDs
|
|
80
|
+
no longer need validation.
|
|
81
|
+
- [Stale internal tests inspect completion sets] → Update tests to assert
|
|
82
|
+
parameter coverage and externally observable request/cache behavior instead
|
|
83
|
+
of obsolete whole-group completion.
|
|
84
|
+
- [Additional first-read requests] → A later request for previously unrequested
|
|
85
|
+
parameters necessarily makes one validation request; already covered IDs are
|
|
86
|
+
not re-requested.
|
python_bsblan-6.1.8/openspec/changes/archive/2026-07-16-fix-partial-validation-state/proposal.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
## Why
|
|
2
|
+
|
|
3
|
+
Lazy validation currently records a section or hot-water group as fully
|
|
4
|
+
validated after a request filtered with `include`. This can leave unsupported
|
|
5
|
+
parameters in a section or leave the hot-water cache incomplete, causing later
|
|
6
|
+
unfiltered reads to skip necessary validation and return incomplete data or fail
|
|
7
|
+
while mapping a device response.
|
|
8
|
+
|
|
9
|
+
## What Changes
|
|
10
|
+
|
|
11
|
+
- Track validation coverage at the parameter level for sections and hot-water
|
|
12
|
+
groups instead of treating any successful filtered request as complete.
|
|
13
|
+
- Ensure a later request validates parameter names that an earlier `include`
|
|
14
|
+
request did not cover.
|
|
15
|
+
- Keep validation completion correct when an include filter has no matching
|
|
16
|
+
parameters.
|
|
17
|
+
- Add focused regression coverage for filtered-then-unfiltered and
|
|
18
|
+
differently-filtered reads.
|
|
19
|
+
|
|
20
|
+
## Capabilities
|
|
21
|
+
|
|
22
|
+
### New Capabilities
|
|
23
|
+
|
|
24
|
+
- `lazy-validation-completeness`: Lazy section and hot-water validation covers
|
|
25
|
+
every parameter required by each request before that request uses the cached
|
|
26
|
+
configuration.
|
|
27
|
+
|
|
28
|
+
### Modified Capabilities
|
|
29
|
+
|
|
30
|
+
- None.
|
|
31
|
+
|
|
32
|
+
## Impact
|
|
33
|
+
|
|
34
|
+
- Affected code: `src/bsblan/_validation.py`, `src/bsblan/utility.py`, and the
|
|
35
|
+
lazy section and hot-water read paths that use their validation state.
|
|
36
|
+
- Affected tests: validation, include-filter, and hot-water regression tests.
|
|
37
|
+
- Public API: no signature changes; `include` calls may perform an additional
|
|
38
|
+
validation request when they need parameters not checked by a prior call.
|