python-bsblan 6.1.9__tar.gz → 6.1.11__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.9 → python_bsblan-6.1.11}/.github/workflows/codeql.yaml +2 -2
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/release-drafter.yaml +1 -1
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/scorecard.yml +2 -2
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/tests.yaml +1 -1
- python_bsblan-6.1.11/.nvmrc +1 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/PKG-INFO +1 -1
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/docs/getting-started.md +44 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/examples/fake_bsblan_server.py +6 -2
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/package-lock.json +4 -4
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/package.json +1 -1
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/pyproject.toml +6 -6
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/_thermostat.py +44 -9
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/_validation.py +8 -2
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/bsblan.py +43 -10
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/constants.py +26 -9
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/models.py +3 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/hot_water_state.json +13 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/state.json +13 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/state_circuit2.json +13 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_circuit.py +35 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_constants.py +9 -3
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_context_manager.py +76 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_hotwater_state.py +6 -2
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_pps.py +84 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_state.py +5 -1
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_temperature_unit.py +14 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_thermostat.py +131 -1
- python_bsblan-6.1.9/.nvmrc +0 -1
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.editorconfig +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.gitattributes +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/CODE_OF_CONDUCT.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/CONTRIBUTING.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/ISSUE_TEMPLATE/PULL_REQUEST_TEMPLATE.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/SECURITY.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/agents/pr-review.agent.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/agents/security-reviewer.agent.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/agents/tdd-refactor.agent.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/copilot-instructions.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/labels.yml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/prompts/add-parameter.prompt.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/prompts/code-review.prompt.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/prompts/opsx-apply.prompt.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/prompts/opsx-archive.prompt.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/prompts/opsx-explore.prompt.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/prompts/opsx-propose.prompt.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/prompts/opsx-sync.prompt.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/release-drafter.yml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/renovate.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/skills/bsblan-parameters/SKILL.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/skills/bsblan-testing/SKILL.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/skills/feature-doc-updates/SKILL.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/skills/openspec-apply-change/SKILL.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/skills/openspec-archive-change/SKILL.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/skills/openspec-explore/SKILL.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/skills/openspec-propose/SKILL.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/skills/openspec-sync-specs/SKILL.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/auto-approve-renovate.yml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/dependency-review.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/docs.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/labels.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/linting.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/lock.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/pr-labels.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/release.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/stale.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/workflows/typing.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.github/zizmor.yml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.gitignore +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.pre-commit-config.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.prettierignore +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/.yamllint +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/AGENTS.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/CLAUDE.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/LICENSE.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/Makefile +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/README.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/docs/api/client.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/docs/api/constants.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/docs/api/exceptions.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/docs/api/models.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/docs/index.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/examples/control.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/examples/discovery.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/examples/fetch_param.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/examples/profile_init.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/examples/ruff.toml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/examples/speed_test.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/mkdocs.yml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/.openspec.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/design.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/proposal.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/specs/cooling-operating-mode/spec.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/tasks.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/.openspec.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/design.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/proposal.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/specs/circuit-discovery/spec.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/tasks.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/.openspec.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/design.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/proposal.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/specs/client-retry-policy/spec.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/tasks.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/.openspec.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/design.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/proposal.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/specs/error-classification/spec.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/tasks.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/.openspec.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/design.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/proposal.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/specs/lazy-validation-completeness/spec.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/tasks.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/config.yaml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/specs/circuit-discovery/spec.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/specs/client-retry-policy/spec.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/specs/cooling-operating-mode/spec.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/specs/error-classification/spec.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/openspec/specs/lazy-validation-completeness/spec.md +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/sonar-project.properties +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/__init__.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/_hot_water.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/_parameters.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/_schedules.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/_temperature.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/_transport.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/_version.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/exceptions.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/py.typed +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/src/bsblan/utility.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/__init__.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/conftest.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/device.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/dict_version.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/info.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/password.txt +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/pps_device.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/pps_state.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/pps_static_values.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/sensor.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/static_state.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/static_state_circuit2.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/thermostat_hvac.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/thermostat_temp.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/fixtures/time.json +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/ruff.toml +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_api_initialization.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_api_validation.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_auth.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_backoff_retry.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_bsblan.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_bsblan_edge_cases.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_configuration.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_device.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_dhw_time_switch.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_entity_info.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_entity_info_ha.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_heating_schedule.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_hot_water_additional.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_include_parameter.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_info.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_initialization.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_read_parameters.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_reset_validation.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_response_encoding.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_schedule_models.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_sensor.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_set_heating_schedule.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_set_hot_water_schedule.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_set_hotwater.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_static_state.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_temperature_validation.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_time.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_utility.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_utility_additional.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_utility_edge_cases.py +0 -0
- {python_bsblan-6.1.9 → python_bsblan-6.1.11}/tests/test_version_errors.py +0 -0
|
@@ -27,6 +27,6 @@ jobs:
|
|
|
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@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
|
|
31
31
|
- name: 🚀 Perform CodeQL Analysis
|
|
32
|
-
uses: github/codeql-action/analyze@
|
|
32
|
+
uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
|
|
@@ -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@978bb0b30c5ee8cdd332ced253758157150d43f0 # v7.10.0
|
|
40
40
|
with:
|
|
41
41
|
prerelease: ${{ github.event.inputs.prerelease == 'true' }}
|
|
42
42
|
prerelease-identifier: ${{ github.event.inputs.prerelease_identifier }}
|
|
@@ -57,7 +57,7 @@ jobs:
|
|
|
57
57
|
# Upload the results as artifacts (optional).
|
|
58
58
|
- name: "Upload artifact"
|
|
59
59
|
# yamllint disable-line rule:line-length
|
|
60
|
-
uses: actions/upload-artifact@
|
|
60
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
61
61
|
with:
|
|
62
62
|
name: SARIF file
|
|
63
63
|
path: results.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@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
|
|
70
70
|
with:
|
|
71
71
|
sarif_file: results.sarif
|
|
@@ -46,7 +46,7 @@ jobs:
|
|
|
46
46
|
- name: 🚀 Run pytest
|
|
47
47
|
run: uv run pytest --cov=bsblan tests
|
|
48
48
|
- name: ⬆️ Upload coverage artifact
|
|
49
|
-
uses: actions/upload-artifact@
|
|
49
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
50
50
|
with:
|
|
51
51
|
name: coverage-${{ matrix.python }}
|
|
52
52
|
include-hidden-files: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24.22.0
|
|
@@ -95,6 +95,26 @@ example, on PPS devices). The upper heating bound is the comfort maximum (`716`
|
|
|
95
95
|
for circuit 1 and `1016` for circuit 2) when the device exposes those
|
|
96
96
|
parameters.
|
|
97
97
|
|
|
98
|
+
## Reduced setpoint and temporary override
|
|
99
|
+
|
|
100
|
+
The reduced room setpoint (`712` for circuit 1, `1012` for circuit 2) can be
|
|
101
|
+
written with `temp_reduced_setpoint`. It uses the same bounds as
|
|
102
|
+
`target_temperature`.
|
|
103
|
+
|
|
104
|
+
To switch a circuit to comfort or reduced only until the next time program
|
|
105
|
+
switch point, write `temporary_mode` (`10110` for circuit 1, `10111` for circuit
|
|
106
|
+
2): `0` (no override), `1` (reduced), or `2` (comfort). The override only has an
|
|
107
|
+
effect in automatic mode and cannot be read back.
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
async with BSBLAN(config) as client:
|
|
111
|
+
await client.thermostat(temp_reduced_setpoint="18.0")
|
|
112
|
+
await client.thermostat(temporary_mode=2)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Setting `temp_reduced_setpoint` and `temporary_mode` is not supported on PPS
|
|
116
|
+
devices.
|
|
117
|
+
|
|
98
118
|
## Cooling setpoint support
|
|
99
119
|
|
|
100
120
|
Some BSB/LPB controllers expose a cooling comfort setpoint for each heating
|
|
@@ -147,6 +167,24 @@ async with BSBLAN(config) as client:
|
|
|
147
167
|
|
|
148
168
|
Setting `cooling_operating_mode` is not supported on PPS devices.
|
|
149
169
|
|
|
170
|
+
## Active setpoints
|
|
171
|
+
|
|
172
|
+
In automatic mode the controller switches between the comfort setpoint
|
|
173
|
+
(`710`/`1010`) and the reduced setpoint (`712`/`1012`) following the time
|
|
174
|
+
program, and room units can shift it temporarily. `target_temperature` always
|
|
175
|
+
holds the comfort setpoint. The setpoint the controller currently applies is
|
|
176
|
+
exposed as `current_setpoint` (`8741` for circuit 1, `8771` for circuit 2).
|
|
177
|
+
|
|
178
|
+
Hot water works the same way: `nominal_setpoint` (`1610`) applies during the
|
|
179
|
+
release periods (`1620`) and `reduced_setpoint` (`1612`) outside them. The
|
|
180
|
+
setpoint currently applied is exposed as `dhw_current_setpoint` (`8831`).
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
async with BSBLAN(config) as client:
|
|
184
|
+
state = await client.state(include=["target_temperature", "current_setpoint"])
|
|
185
|
+
hw_state = await client.hot_water_state(include=["dhw_current_setpoint"])
|
|
186
|
+
```
|
|
187
|
+
|
|
150
188
|
## PPS bus support
|
|
151
189
|
|
|
152
190
|
PPS bus devices are detected from the device metadata returned by BSB-LAN. The
|
|
@@ -157,11 +195,17 @@ Supported PPS climate operations:
|
|
|
157
195
|
|
|
158
196
|
- `state()` for `hvac_mode`, `target_temperature`, and `current_temperature`
|
|
159
197
|
- `static_values()` for `min_temp` and `max_temp`
|
|
198
|
+
- `sensor()` for `outside_temperature` (`15030`) and `current_temperature` (`15008`)
|
|
160
199
|
- `thermostat()` for target temperature and HVAC mode
|
|
161
200
|
- `get_available_circuits()`, which returns `[1]` when the single PPS
|
|
162
201
|
climate circuit is available, otherwise `[]`; PPS devices only ever expose
|
|
163
202
|
circuit `1`
|
|
164
203
|
|
|
204
|
+
PPS sensor parameters are selected only when device metadata identifies the PPS
|
|
205
|
+
bus. BSB/LPB devices retain their existing sensor mappings. Sensor reads remain
|
|
206
|
+
on demand; `sensor(include=["outside_temperature"])` requests only `15030` on PPS
|
|
207
|
+
and only `8700` otherwise, without probing both parameter ranges.
|
|
208
|
+
|
|
165
209
|
PPS devices currently have these limitations:
|
|
166
210
|
|
|
167
211
|
- Only circuit `1` is supported.
|
|
@@ -128,6 +128,7 @@ def build_parameter_db(
|
|
|
128
128
|
# ---- Sensor params ----
|
|
129
129
|
db["8700"] = _param("Outside temp sensor local", "6.1", unit="°C", readonly=1)
|
|
130
130
|
db["8740"] = _param("Room temp 1 actual value", "21.3", unit="°C", readonly=1)
|
|
131
|
+
db["8741"] = _param("Room setpoint 1", "21.0", unit="°C", readonly=1)
|
|
131
132
|
db["3113"] = _param("Total energy", "7968", unit="kWh", readonly=1)
|
|
132
133
|
|
|
133
134
|
# ---- Time param (0) ----
|
|
@@ -161,6 +162,7 @@ def build_parameter_db(
|
|
|
161
162
|
db["1663"] = _param("DHW circulation setpoint", "40.0", unit="°C")
|
|
162
163
|
db["1680"] = _param("DHW changeover", "0", desc="---", data_type=1)
|
|
163
164
|
db["8830"] = _param("DHW actual top temp", "48.2", unit="°C", readonly=1)
|
|
165
|
+
db["8831"] = _param("DHW current setpoint", "50.0", unit="°C", readonly=1)
|
|
164
166
|
db["8820"] = _param(
|
|
165
167
|
"DHW pump state", "255", desc="Off", data_type=1, readonly=1
|
|
166
168
|
)
|
|
@@ -219,9 +221,10 @@ def build_parameter_db(
|
|
|
219
221
|
data_type=1,
|
|
220
222
|
readonly=1,
|
|
221
223
|
)
|
|
222
|
-
db["
|
|
224
|
+
db["8770"] = _param(
|
|
223
225
|
"Room temp 2 actual value", "19.8", unit="°C", readonly=1
|
|
224
226
|
)
|
|
227
|
+
db["8771"] = _param("Room setpoint 2", "19.0", unit="°C", readonly=1)
|
|
225
228
|
db["8750"] = _param(
|
|
226
229
|
"HC2 Room1 thermostat mode",
|
|
227
230
|
"1",
|
|
@@ -250,7 +253,7 @@ def build_parameter_db(
|
|
|
250
253
|
data_type=1,
|
|
251
254
|
readonly=1,
|
|
252
255
|
)
|
|
253
|
-
db["
|
|
256
|
+
db["8800"] = _param(
|
|
254
257
|
"Room temp 3 actual value", "17.5", unit="°C", readonly=1
|
|
255
258
|
)
|
|
256
259
|
db["8751"] = _param(
|
|
@@ -340,6 +343,7 @@ class FakeBSBLAN:
|
|
|
340
343
|
"1680",
|
|
341
344
|
"8820",
|
|
342
345
|
"8830",
|
|
346
|
+
"8831",
|
|
343
347
|
}
|
|
344
348
|
)
|
|
345
349
|
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
"url-parse": "1.5.10"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"prettier": "3.9.
|
|
15
|
+
"prettier": "3.9.10"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"node_modules/prettier": {
|
|
19
|
-
"version": "3.9.
|
|
20
|
-
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.
|
|
21
|
-
"integrity": "sha512-
|
|
19
|
+
"version": "3.9.10",
|
|
20
|
+
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.10.tgz",
|
|
21
|
+
"integrity": "sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==",
|
|
22
22
|
"dev": true,
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"bin": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "python-bsblan"
|
|
3
|
-
version = "6.1.
|
|
3
|
+
version = "6.1.11"
|
|
4
4
|
description = "Asynchronous Python client for BSBLAN API"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Willem-Jan van Rootselaar", email = "liudgervr@gmail.com"}
|
|
@@ -186,7 +186,7 @@ build-backend = "hatchling.build"
|
|
|
186
186
|
dev = [
|
|
187
187
|
"aresponses==3.0.0",
|
|
188
188
|
"bandit==1.9.4",
|
|
189
|
-
"black==26.10.
|
|
189
|
+
"black==26.10.1",
|
|
190
190
|
"blacken-docs==1.20.0",
|
|
191
191
|
"codespell==2.4.3",
|
|
192
192
|
"covdefaults==2.3.0",
|
|
@@ -197,16 +197,16 @@ dev = [
|
|
|
197
197
|
# hatch is required to support type hinting and proper packaging of the py.typed file.
|
|
198
198
|
"hatch>=1.14.1",
|
|
199
199
|
"isort==8.0.1",
|
|
200
|
-
"ty==0.0.
|
|
200
|
+
"ty==0.0.89",
|
|
201
201
|
"prek>=0.3.3",
|
|
202
202
|
"pre-commit-hooks==6.0.0",
|
|
203
|
-
"pylint==4.1.
|
|
203
|
+
"pylint==4.1.3",
|
|
204
204
|
"pytest>=8.3.5",
|
|
205
205
|
"pytest-asyncio==1.4.0",
|
|
206
206
|
"pytest-cov==7.1.0",
|
|
207
207
|
"pytest-xdist>=3.8.0",
|
|
208
|
-
"pyupgrade==3.
|
|
209
|
-
"ruff==0.
|
|
208
|
+
"pyupgrade==3.22.0",
|
|
209
|
+
"ruff==0.17.1",
|
|
210
210
|
"safety==3.8.1",
|
|
211
211
|
"vulture==2.16",
|
|
212
212
|
"yamllint==1.38.0",
|
|
@@ -35,7 +35,7 @@ class ThermostatWriter:
|
|
|
35
35
|
self._temperature = temperature
|
|
36
36
|
self._set_payload = set_payload
|
|
37
37
|
|
|
38
|
-
async def prepare_state( # pylint: disable=too-many-arguments
|
|
38
|
+
async def prepare_state( # noqa: PLR0913 # pylint: disable=too-many-arguments
|
|
39
39
|
self,
|
|
40
40
|
target_temperature: str | None,
|
|
41
41
|
hvac_mode: int | None,
|
|
@@ -43,6 +43,8 @@ class ThermostatWriter:
|
|
|
43
43
|
target_temperature_high: str | float | None = None,
|
|
44
44
|
*,
|
|
45
45
|
cooling_operating_mode: int | None = None,
|
|
46
|
+
temp_reduced_setpoint: str | float | None = None,
|
|
47
|
+
temporary_mode: int | None = None,
|
|
46
48
|
) -> dict[str, Any]:
|
|
47
49
|
"""Prepare the thermostat state for setting.
|
|
48
50
|
|
|
@@ -53,6 +55,9 @@ class ThermostatWriter:
|
|
|
53
55
|
target_temperature_high: The cooling comfort setpoint to set.
|
|
54
56
|
cooling_operating_mode: The cooling circuit operating mode to set
|
|
55
57
|
as raw integer.
|
|
58
|
+
temp_reduced_setpoint: The reduced (eco) room setpoint to set.
|
|
59
|
+
temporary_mode: The temporary operating mode override to set as
|
|
60
|
+
raw integer.
|
|
56
61
|
|
|
57
62
|
Returns:
|
|
58
63
|
dict[str, Any]: The prepared state for the thermostat.
|
|
@@ -69,10 +74,7 @@ class ThermostatWriter:
|
|
|
69
74
|
self._set_payload(param_ids["target_temperature"], target_temperature),
|
|
70
75
|
)
|
|
71
76
|
if target_temperature_high is not None:
|
|
72
|
-
param_id =
|
|
73
|
-
if param_id is None:
|
|
74
|
-
parameter_name = "target_temperature_high"
|
|
75
|
-
raise BSBLANInvalidParameterError(parameter_name)
|
|
77
|
+
param_id = self._param_id(param_ids, "target_temperature_high")
|
|
76
78
|
await self._temperature.validate_target_temperature_high(
|
|
77
79
|
target_temperature_high,
|
|
78
80
|
circuit,
|
|
@@ -89,16 +91,35 @@ class ThermostatWriter:
|
|
|
89
91
|
self._set_payload(param_ids["hvac_mode"], hvac_value),
|
|
90
92
|
)
|
|
91
93
|
if cooling_operating_mode is not None:
|
|
92
|
-
param_id =
|
|
93
|
-
if param_id is None:
|
|
94
|
-
parameter_name = "cooling_operating_mode"
|
|
95
|
-
raise BSBLANInvalidParameterError(parameter_name)
|
|
94
|
+
param_id = self._param_id(param_ids, "cooling_operating_mode")
|
|
96
95
|
self.validate_cooling_operating_mode(cooling_operating_mode)
|
|
97
96
|
state.update(
|
|
98
97
|
self._set_payload(param_id, str(cooling_operating_mode)),
|
|
99
98
|
)
|
|
99
|
+
if temp_reduced_setpoint is not None:
|
|
100
|
+
param_id = self._param_id(param_ids, "temp_reduced_setpoint")
|
|
101
|
+
await self._temperature.validate_target_temperature(
|
|
102
|
+
str(temp_reduced_setpoint),
|
|
103
|
+
circuit,
|
|
104
|
+
)
|
|
105
|
+
state.update(
|
|
106
|
+
self._set_payload(param_id, str(temp_reduced_setpoint)),
|
|
107
|
+
)
|
|
108
|
+
if temporary_mode is not None:
|
|
109
|
+
param_id = self._param_id(param_ids, "temporary_mode")
|
|
110
|
+
self.validate_temporary_mode(temporary_mode)
|
|
111
|
+
state.update(
|
|
112
|
+
self._set_payload(param_id, str(temporary_mode)),
|
|
113
|
+
)
|
|
100
114
|
return state
|
|
101
115
|
|
|
116
|
+
@staticmethod
|
|
117
|
+
def _param_id(param_ids: dict[str, str], parameter_name: str) -> str:
|
|
118
|
+
"""Return the parameter ID for a write, or raise if unsupported."""
|
|
119
|
+
if (param_id := param_ids.get(parameter_name)) is None:
|
|
120
|
+
raise BSBLANInvalidParameterError(parameter_name)
|
|
121
|
+
return param_id
|
|
122
|
+
|
|
102
123
|
def thermostat_params(self, circuit: int) -> dict[str, str]:
|
|
103
124
|
"""Return thermostat write parameters for the active bus type."""
|
|
104
125
|
if self._uses_pps_bus():
|
|
@@ -135,3 +156,17 @@ class ThermostatWriter:
|
|
|
135
156
|
"""
|
|
136
157
|
if cooling_operating_mode not in Validation.COOLING_OPERATING_MODES:
|
|
137
158
|
raise BSBLANInvalidParameterError(str(cooling_operating_mode))
|
|
159
|
+
|
|
160
|
+
def validate_temporary_mode(self, temporary_mode: int) -> None:
|
|
161
|
+
"""Validate the temporary operating mode override.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
temporary_mode (int): The mode to validate. Valid values are
|
|
165
|
+
0=No override, 1=Reduced, 2=Comfort.
|
|
166
|
+
|
|
167
|
+
Raises:
|
|
168
|
+
BSBLANInvalidParameterError: If the mode is invalid.
|
|
169
|
+
|
|
170
|
+
"""
|
|
171
|
+
if temporary_mode not in Validation.TEMPORARY_MODES:
|
|
172
|
+
raise BSBLANInvalidParameterError(str(temporary_mode))
|
|
@@ -17,7 +17,12 @@ import asyncio
|
|
|
17
17
|
import logging
|
|
18
18
|
from typing import TYPE_CHECKING, cast
|
|
19
19
|
|
|
20
|
-
from .constants import
|
|
20
|
+
from .constants import (
|
|
21
|
+
PPS_HEATING_PARAMS,
|
|
22
|
+
PPS_SENSOR_PARAMS,
|
|
23
|
+
PPS_STATIC_VALUES_PARAMS,
|
|
24
|
+
ErrorMsg,
|
|
25
|
+
)
|
|
21
26
|
from .exceptions import BSBLANError
|
|
22
27
|
from .utility import APIValidator
|
|
23
28
|
|
|
@@ -117,6 +122,7 @@ class SectionValidator:
|
|
|
117
122
|
|
|
118
123
|
api_data["heating"] = PPS_HEATING_PARAMS.copy()
|
|
119
124
|
api_data["staticValues"] = PPS_STATIC_VALUES_PARAMS.copy()
|
|
125
|
+
api_data["sensor"] = PPS_SENSOR_PARAMS.copy()
|
|
120
126
|
api_data["heating_circuit2"] = {}
|
|
121
127
|
api_data["staticValues_circuit2"] = {}
|
|
122
128
|
|
|
@@ -227,7 +233,7 @@ class SectionValidator:
|
|
|
227
233
|
|
|
228
234
|
This enables more granular lazy loading for hot water - instead of
|
|
229
235
|
validating all 29 hot water parameters at once, we only validate
|
|
230
|
-
the specific group needed (essential:
|
|
236
|
+
the specific group needed (essential: 6, config: 16, schedule: 8).
|
|
231
237
|
|
|
232
238
|
Uses a per-group lock to prevent concurrent validation of the same
|
|
233
239
|
group, which could cause duplicate network requests.
|
|
@@ -171,7 +171,20 @@ class BSBLAN:
|
|
|
171
171
|
if self.session is None:
|
|
172
172
|
self.session = aiohttp.ClientSession()
|
|
173
173
|
self._close_session = True
|
|
174
|
-
|
|
174
|
+
try:
|
|
175
|
+
await self.initialize()
|
|
176
|
+
except BaseException:
|
|
177
|
+
if self._close_session and self.session:
|
|
178
|
+
session = self.session
|
|
179
|
+
self.session = None
|
|
180
|
+
self._close_session = False
|
|
181
|
+
try:
|
|
182
|
+
await session.close()
|
|
183
|
+
except Exception:
|
|
184
|
+
logger.exception(
|
|
185
|
+
"Failed to close session after initialization failure"
|
|
186
|
+
)
|
|
187
|
+
raise
|
|
175
188
|
return self
|
|
176
189
|
|
|
177
190
|
async def __aexit__(self, *args: object) -> None:
|
|
@@ -332,7 +345,7 @@ class BSBLAN:
|
|
|
332
345
|
|
|
333
346
|
This enables more granular lazy loading for hot water - instead of
|
|
334
347
|
validating all 29 hot water parameters at once, we only validate
|
|
335
|
-
the specific group needed (essential:
|
|
348
|
+
the specific group needed (essential: 6, config: 16, schedule: 8).
|
|
336
349
|
|
|
337
350
|
Uses a per-group lock to prevent concurrent validation of the same
|
|
338
351
|
group, which could cause duplicate network requests.
|
|
@@ -720,7 +733,7 @@ class BSBLAN:
|
|
|
720
733
|
include: Optional list of parameter names to fetch. If None,
|
|
721
734
|
fetches all state parameters. Valid names include:
|
|
722
735
|
hvac_mode, target_temperature, hvac_action,
|
|
723
|
-
hvac_mode_changeover, current_temperature,
|
|
736
|
+
hvac_mode_changeover, current_temperature, current_setpoint,
|
|
724
737
|
room1_temp_setpoint_boost.
|
|
725
738
|
circuit: The heating circuit number (1 or 2). Defaults to 1.
|
|
726
739
|
Circuit 2 uses separate parameter IDs but returns the
|
|
@@ -782,15 +795,17 @@ class BSBLAN:
|
|
|
782
795
|
Args:
|
|
783
796
|
include: Optional list of parameter names to fetch. If None,
|
|
784
797
|
fetches all static parameters. Valid names include:
|
|
785
|
-
|
|
798
|
+
temp_reduced_setpoint, heating_protective_setpoint,
|
|
799
|
+
comfort_setpoint_max, cooling_comfort_setpoint_min,
|
|
800
|
+
cooling_reduced_setpoint, and min_temp, max_temp on PPS.
|
|
786
801
|
circuit: The heating circuit number (1 or 2). Defaults to 1.
|
|
787
802
|
|
|
788
803
|
Returns:
|
|
789
804
|
StaticState: The static information from the BSBLAN device.
|
|
790
805
|
|
|
791
806
|
Example:
|
|
792
|
-
# Fetch only
|
|
793
|
-
static = await client.static_values(include=["
|
|
807
|
+
# Fetch only the reduced setpoint
|
|
808
|
+
static = await client.static_values(include=["temp_reduced_setpoint"])
|
|
794
809
|
|
|
795
810
|
# Fetch static values for heating circuit 2
|
|
796
811
|
static_hc2 = await client.static_values(circuit=2)
|
|
@@ -866,7 +881,7 @@ class BSBLAN:
|
|
|
866
881
|
)
|
|
867
882
|
logger.debug("Response for setting time: %s", response)
|
|
868
883
|
|
|
869
|
-
async def thermostat( # pylint: disable=too-many-arguments
|
|
884
|
+
async def thermostat( # noqa: PLR0913 # pylint: disable=too-many-arguments
|
|
870
885
|
self,
|
|
871
886
|
target_temperature: str | None = None,
|
|
872
887
|
hvac_mode: int | None = None,
|
|
@@ -874,6 +889,8 @@ class BSBLAN:
|
|
|
874
889
|
target_temperature_high: str | float | None = None,
|
|
875
890
|
*,
|
|
876
891
|
cooling_operating_mode: int | None = None,
|
|
892
|
+
temp_reduced_setpoint: str | float | None = None,
|
|
893
|
+
temporary_mode: int | None = None,
|
|
877
894
|
) -> None:
|
|
878
895
|
"""Change the state of the thermostat through BSB-Lan.
|
|
879
896
|
|
|
@@ -888,6 +905,12 @@ class BSBLAN:
|
|
|
888
905
|
cooling_operating_mode: The cooling circuit operating mode to set
|
|
889
906
|
as raw integer value: 0=Protection, 1=Automatic, 2=Reduced,
|
|
890
907
|
3=Comfort. Not supported on PPS devices.
|
|
908
|
+
temp_reduced_setpoint: The reduced (eco) room setpoint to set.
|
|
909
|
+
Not supported on PPS devices.
|
|
910
|
+
temporary_mode: Temporary operating mode override, active until
|
|
911
|
+
the next time program switch point. Raw integer value:
|
|
912
|
+
0=No override, 1=Reduced, 2=Comfort. Only has an effect in
|
|
913
|
+
automatic mode. Not supported on PPS devices.
|
|
891
914
|
|
|
892
915
|
Example:
|
|
893
916
|
# Set HC1 temperature
|
|
@@ -899,6 +922,12 @@ class BSBLAN:
|
|
|
899
922
|
# Set HC1 cooling operating mode
|
|
900
923
|
await client.thermostat(cooling_operating_mode=1)
|
|
901
924
|
|
|
925
|
+
# Set HC1 reduced setpoint
|
|
926
|
+
await client.thermostat(temp_reduced_setpoint="18.0")
|
|
927
|
+
|
|
928
|
+
# Switch HC1 to comfort until the next time program switch point
|
|
929
|
+
await client.thermostat(temporary_mode=2)
|
|
930
|
+
|
|
902
931
|
# Set HC2 mode
|
|
903
932
|
await client.thermostat(hvac_mode=1, circuit=2)
|
|
904
933
|
|
|
@@ -906,13 +935,14 @@ class BSBLAN:
|
|
|
906
935
|
self._validate_circuit(circuit)
|
|
907
936
|
if self._uses_pps_bus:
|
|
908
937
|
self._validate_bus_write_supported()
|
|
909
|
-
await self._initialize_temperature_range(circuit)
|
|
910
938
|
|
|
911
939
|
self._validate_single_parameter(
|
|
912
940
|
target_temperature,
|
|
913
941
|
hvac_mode,
|
|
914
942
|
target_temperature_high,
|
|
915
943
|
cooling_operating_mode,
|
|
944
|
+
temp_reduced_setpoint,
|
|
945
|
+
temporary_mode,
|
|
916
946
|
error_msg=ErrorMsg.MULTI_PARAMETER,
|
|
917
947
|
)
|
|
918
948
|
|
|
@@ -922,6 +952,8 @@ class BSBLAN:
|
|
|
922
952
|
circuit,
|
|
923
953
|
target_temperature_high,
|
|
924
954
|
cooling_operating_mode=cooling_operating_mode,
|
|
955
|
+
temp_reduced_setpoint=temp_reduced_setpoint,
|
|
956
|
+
temporary_mode=temporary_mode,
|
|
925
957
|
)
|
|
926
958
|
await self._set_device_state(state)
|
|
927
959
|
|
|
@@ -1010,14 +1042,15 @@ class BSBLAN:
|
|
|
1010
1042
|
that are typically checked frequently for monitoring purposes.
|
|
1011
1043
|
This reduces API calls and improves performance for regular polling.
|
|
1012
1044
|
|
|
1013
|
-
Uses granular lazy loading - only validates the
|
|
1045
|
+
Uses granular lazy loading - only validates the 6 essential params,
|
|
1014
1046
|
not all 29 hot water parameters.
|
|
1015
1047
|
|
|
1016
1048
|
Args:
|
|
1017
1049
|
include: Optional list of parameter names to fetch. If None,
|
|
1018
1050
|
fetches all essential hot water parameters. Valid names include:
|
|
1019
1051
|
operating_mode, nominal_setpoint, release,
|
|
1020
|
-
dhw_actual_value_top_temperature,
|
|
1052
|
+
dhw_actual_value_top_temperature, dhw_current_setpoint,
|
|
1053
|
+
state_dhw_pump.
|
|
1021
1054
|
|
|
1022
1055
|
Returns:
|
|
1023
1056
|
HotWaterState: Essential hot water state information.
|
|
@@ -45,6 +45,7 @@ BASE_HEATING_PARAMS: Final[dict[str, str]] = {
|
|
|
45
45
|
# -------
|
|
46
46
|
"8000": "hvac_action",
|
|
47
47
|
"8740": "current_temperature",
|
|
48
|
+
"8741": "current_setpoint",
|
|
48
49
|
"770": "room1_temp_setpoint_boost",
|
|
49
50
|
}
|
|
50
51
|
|
|
@@ -90,6 +91,7 @@ BASE_HOT_WATER_PARAMS: Final[dict[str, str]] = {
|
|
|
90
91
|
"1680": "operating_mode_changeover",
|
|
91
92
|
# -------
|
|
92
93
|
"8830": "dhw_actual_value_top_temperature",
|
|
94
|
+
"8831": "dhw_current_setpoint",
|
|
93
95
|
"8820": "state_dhw_pump",
|
|
94
96
|
# -------
|
|
95
97
|
"561": "dhw_time_program_monday",
|
|
@@ -113,6 +115,7 @@ BASE_HEATING_CIRCUIT2_PARAMS: Final[dict[str, str]] = {
|
|
|
113
115
|
# -------
|
|
114
116
|
"8001": "hvac_action",
|
|
115
117
|
"8770": "current_temperature",
|
|
118
|
+
"8771": "current_setpoint",
|
|
116
119
|
"1070": "room1_temp_setpoint_boost",
|
|
117
120
|
}
|
|
118
121
|
|
|
@@ -156,6 +159,11 @@ PPS_STATIC_VALUES_PARAMS: Final[dict[str, str]] = {
|
|
|
156
159
|
"15007": "max_temp",
|
|
157
160
|
}
|
|
158
161
|
|
|
162
|
+
PPS_SENSOR_PARAMS: Final[dict[str, str]] = {
|
|
163
|
+
"15030": "outside_temperature",
|
|
164
|
+
"15008": "current_temperature",
|
|
165
|
+
}
|
|
166
|
+
|
|
159
167
|
|
|
160
168
|
# Circuit configuration
|
|
161
169
|
class CircuitConfig:
|
|
@@ -176,12 +184,16 @@ class CircuitConfig:
|
|
|
176
184
|
"target_temperature_high": "902",
|
|
177
185
|
"hvac_mode": "700",
|
|
178
186
|
"cooling_operating_mode": "901",
|
|
187
|
+
"temp_reduced_setpoint": "712",
|
|
188
|
+
"temporary_mode": "10110",
|
|
179
189
|
},
|
|
180
190
|
2: {
|
|
181
191
|
"target_temperature": "1010",
|
|
182
192
|
"target_temperature_high": "1202",
|
|
183
193
|
"hvac_mode": "1000",
|
|
184
194
|
"cooling_operating_mode": "1201",
|
|
195
|
+
"temp_reduced_setpoint": "1012",
|
|
196
|
+
"temporary_mode": "10111",
|
|
185
197
|
},
|
|
186
198
|
}
|
|
187
199
|
PROBE_PARAMS: Final[dict[int, str]] = {
|
|
@@ -247,6 +259,9 @@ class Validation:
|
|
|
247
259
|
# 901/1201 cooling circuit operating mode (ENUM, verified on device):
|
|
248
260
|
# 0=Protection, 1=Automatic, 2=Reduced, 3=Comfort
|
|
249
261
|
COOLING_OPERATING_MODES: Final[set[int]] = {0, 1, 2, 3}
|
|
262
|
+
# 10110/10111 temporary operating mode (write-only, verified on device):
|
|
263
|
+
# 0=No override, 1=Reduced, 2=Comfort, until the next time program switch
|
|
264
|
+
TEMPORARY_MODES: Final[set[int]] = {0, 1, 2}
|
|
250
265
|
PPS_HVAC_MODES: Final[set[int]] = {0, 1, 3}
|
|
251
266
|
PPS_HVAC_MODE_TO_BSBLAN: Final[dict[int, str]] = {
|
|
252
267
|
0: "2", # off
|
|
@@ -410,19 +425,20 @@ _STATUS_TO_CATEGORY: dict[HeatingCircuitStatus, HVACActionCategory] = {
|
|
|
410
425
|
# Off states
|
|
411
426
|
HeatingCircuitStatus.FAULT_ERROR: HVACActionCategory.OFF,
|
|
412
427
|
HeatingCircuitStatus.STANDBY_1: HVACActionCategory.OFF,
|
|
413
|
-
HeatingCircuitStatus.OFF: HVACActionCategory.OFF,
|
|
414
428
|
HeatingCircuitStatus.STANDBY_2: HVACActionCategory.OFF,
|
|
415
429
|
HeatingCircuitStatus.COOLING_OFF: HVACActionCategory.OFF,
|
|
416
|
-
HeatingCircuitStatus.COOLING_LOCKED: HVACActionCategory.OFF,
|
|
417
430
|
HeatingCircuitStatus.HEATING_GENERATOR_OFF: HVACActionCategory.OFF,
|
|
418
431
|
HeatingCircuitStatus.HEATING_OFF: HVACActionCategory.OFF,
|
|
419
|
-
|
|
420
|
-
HeatingCircuitStatus.
|
|
421
|
-
HeatingCircuitStatus.
|
|
422
|
-
HeatingCircuitStatus.
|
|
423
|
-
HeatingCircuitStatus.
|
|
424
|
-
HeatingCircuitStatus.
|
|
425
|
-
HeatingCircuitStatus.
|
|
432
|
+
# Idle states: the circuit runs but currently needs no heat
|
|
433
|
+
HeatingCircuitStatus.OFF: HVACActionCategory.IDLE, # summer operation
|
|
434
|
+
HeatingCircuitStatus.COOLING_LOCKED: HVACActionCategory.IDLE,
|
|
435
|
+
HeatingCircuitStatus.LOCKED_HEATING_MODE: HVACActionCategory.IDLE,
|
|
436
|
+
HeatingCircuitStatus.LOCKED_GENERATOR: HVACActionCategory.IDLE,
|
|
437
|
+
HeatingCircuitStatus.LOCKED_BUFFER: HVACActionCategory.IDLE,
|
|
438
|
+
HeatingCircuitStatus.DAY_ECO: HVACActionCategory.IDLE,
|
|
439
|
+
HeatingCircuitStatus.SETBACK_REDUCED: HVACActionCategory.IDLE,
|
|
440
|
+
HeatingCircuitStatus.SETBACK_FROST_PROTECTION: HVACActionCategory.IDLE,
|
|
441
|
+
HeatingCircuitStatus.ROOM_TEMPERATURE_LIMIT: HVACActionCategory.IDLE,
|
|
426
442
|
# Heating states
|
|
427
443
|
HeatingCircuitStatus.MANUAL_CONTROL: HVACActionCategory.HEATING,
|
|
428
444
|
HeatingCircuitStatus.OVERRUN: HVACActionCategory.HEATING,
|
|
@@ -637,6 +653,7 @@ class HotWaterParams:
|
|
|
637
653
|
"nominal_setpoint",
|
|
638
654
|
"release",
|
|
639
655
|
"dhw_actual_value_top_temperature",
|
|
656
|
+
"dhw_current_setpoint",
|
|
640
657
|
"state_dhw_pump",
|
|
641
658
|
}
|
|
642
659
|
}
|
|
@@ -481,6 +481,8 @@ class State(BaseModel):
|
|
|
481
481
|
# (0=Protection, 1=Automatic, 2=Reduced, 3=Comfort)
|
|
482
482
|
cooling_operating_mode: EntityInfo[int] | None = None
|
|
483
483
|
current_temperature: EntityInfo[float] | None = None
|
|
484
|
+
# 8741/8771: room setpoint currently applied by the controller
|
|
485
|
+
current_setpoint: EntityInfo[float] | None = None
|
|
484
486
|
room1_temp_setpoint_boost: EntityInfo[float] | None = None
|
|
485
487
|
|
|
486
488
|
|
|
@@ -525,6 +527,7 @@ class HotWaterState(BaseModel):
|
|
|
525
527
|
nominal_setpoint: EntityInfo[float] | None = None
|
|
526
528
|
release: EntityInfo[int] | None = None
|
|
527
529
|
dhw_actual_value_top_temperature: EntityInfo[float] | None = None
|
|
530
|
+
dhw_current_setpoint: EntityInfo[float] | None = None
|
|
528
531
|
state_dhw_pump: EntityInfo[int] | None = None
|
|
529
532
|
|
|
530
533
|
|
|
@@ -249,6 +249,19 @@
|
|
|
249
249
|
"readwrite": 1,
|
|
250
250
|
"unit": "°C"
|
|
251
251
|
},
|
|
252
|
+
"8831": {
|
|
253
|
+
"name": "DHW temperature setpoint",
|
|
254
|
+
"dataType_name": "TEMP",
|
|
255
|
+
"dataType_family": "VALS",
|
|
256
|
+
"error": 0,
|
|
257
|
+
"value": "45.0",
|
|
258
|
+
"desc": "",
|
|
259
|
+
"precision": 0.1,
|
|
260
|
+
"dataType": 0,
|
|
261
|
+
"readonly": 1,
|
|
262
|
+
"readwrite": 1,
|
|
263
|
+
"unit": "°C"
|
|
264
|
+
},
|
|
252
265
|
"8820": {
|
|
253
266
|
"name": "State DHW pump",
|
|
254
267
|
"dataType_name": "ONOFF",
|