python-bsblan 6.1.10__tar.gz → 6.2.0__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.10 → python_bsblan-6.2.0}/.github/workflows/codeql.yaml +2 -2
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/release.yaml +1 -1
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/scorecard.yml +2 -2
- python_bsblan-6.2.0/.nvmrc +1 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/PKG-INFO +1 -1
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/docs/getting-started.md +38 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/examples/control.py +3 -5
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/examples/fake_bsblan_server.py +6 -2
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/package-lock.json +4 -4
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/package.json +1 -1
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/pyproject.toml +5 -5
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/_thermostat.py +44 -9
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/_validation.py +1 -1
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/bsblan.py +58 -32
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/constants.py +22 -9
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/models.py +3 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/utility.py +0 -60
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/hot_water_state.json +13 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/state.json +13 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/state_circuit2.json +13 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_circuit.py +35 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_constants.py +9 -3
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_context_manager.py +76 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_hotwater_state.py +6 -2
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_pps.py +53 -2
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_state.py +5 -1
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_temperature_unit.py +14 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_thermostat.py +131 -1
- python_bsblan-6.2.0/tests/test_time.py +112 -0
- python_bsblan-6.2.0/tests/test_utility.py +173 -0
- python_bsblan-6.1.10/.nvmrc +0 -1
- python_bsblan-6.1.10/tests/test_time.py +0 -182
- python_bsblan-6.1.10/tests/test_utility.py +0 -398
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.editorconfig +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.gitattributes +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/CODE_OF_CONDUCT.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/CONTRIBUTING.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/ISSUE_TEMPLATE/PULL_REQUEST_TEMPLATE.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/SECURITY.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/agents/pr-review.agent.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/agents/security-reviewer.agent.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/agents/tdd-refactor.agent.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/copilot-instructions.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/labels.yml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/prompts/add-parameter.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/prompts/code-review.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/prompts/opsx-apply.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/prompts/opsx-archive.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/prompts/opsx-explore.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/prompts/opsx-propose.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/prompts/opsx-sync.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/release-drafter.yml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/renovate.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/skills/bsblan-parameters/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/skills/bsblan-testing/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/skills/feature-doc-updates/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/skills/openspec-apply-change/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/skills/openspec-archive-change/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/skills/openspec-explore/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/skills/openspec-propose/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/skills/openspec-sync-specs/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/auto-approve-renovate.yml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/dependency-review.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/docs.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/labels.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/linting.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/lock.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/pr-labels.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/release-drafter.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/stale.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/tests.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/workflows/typing.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.github/zizmor.yml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.gitignore +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.pre-commit-config.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.prettierignore +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/.yamllint +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/AGENTS.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/CLAUDE.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/LICENSE.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/Makefile +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/README.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/docs/api/client.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/docs/api/constants.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/docs/api/exceptions.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/docs/api/models.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/docs/index.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/examples/discovery.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/examples/fetch_param.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/examples/profile_init.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/examples/ruff.toml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/examples/speed_test.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/mkdocs.yml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/.openspec.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/design.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/proposal.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/specs/cooling-operating-mode/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/tasks.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/.openspec.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/design.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/proposal.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/specs/circuit-discovery/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/tasks.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/.openspec.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/design.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/proposal.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/specs/client-retry-policy/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/tasks.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/.openspec.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/design.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/proposal.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/specs/error-classification/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/tasks.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/.openspec.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/design.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/proposal.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/specs/lazy-validation-completeness/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/tasks.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/config.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/specs/circuit-discovery/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/specs/client-retry-policy/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/specs/cooling-operating-mode/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/specs/error-classification/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/openspec/specs/lazy-validation-completeness/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/sonar-project.properties +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/__init__.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/_hot_water.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/_parameters.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/_schedules.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/_temperature.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/_transport.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/_version.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/exceptions.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/src/bsblan/py.typed +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/__init__.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/conftest.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/device.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/dict_version.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/info.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/password.txt +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/pps_device.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/pps_state.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/pps_static_values.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/sensor.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/static_state.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/static_state_circuit2.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/thermostat_hvac.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/thermostat_temp.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/fixtures/time.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/ruff.toml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_api_initialization.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_api_validation.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_auth.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_backoff_retry.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_bsblan.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_bsblan_edge_cases.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_configuration.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_device.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_dhw_time_switch.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_entity_info.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_entity_info_ha.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_heating_schedule.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_hot_water_additional.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_include_parameter.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_info.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_initialization.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_read_parameters.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_reset_validation.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_response_encoding.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_schedule_models.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_sensor.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_set_heating_schedule.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_set_hot_water_schedule.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_set_hotwater.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_static_state.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_temperature_validation.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_utility_additional.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/tests/test_utility_edge_cases.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.2.0}/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@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
|
|
31
31
|
- name: 🚀 Perform CodeQL Analysis
|
|
32
|
-
uses: github/codeql-action/analyze@
|
|
32
|
+
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
|
|
@@ -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@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
|
|
58
58
|
with:
|
|
59
59
|
verbose: true
|
|
60
60
|
print-hash: true
|
|
@@ -40,7 +40,7 @@ jobs:
|
|
|
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@fa7117db83c52d787ae5a89f350999701505d78e # v2.5.0
|
|
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@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
|
|
70
70
|
with:
|
|
71
71
|
sarif_file: results.sarif
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24.22.1
|
|
@@ -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
|
|
@@ -409,11 +409,9 @@ async def main() -> None:
|
|
|
409
409
|
# Example: Set device time
|
|
410
410
|
if bsblan.supports_time_sync:
|
|
411
411
|
print("\nSetting device time to current system time")
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
print(f"Current system time: {formatted_time}")
|
|
416
|
-
await bsblan.set_time(formatted_time)
|
|
412
|
+
current_time = datetime.now() # noqa: DTZ005
|
|
413
|
+
print(f"Current system time: {current_time:%d.%m.%Y %H:%M:%S}")
|
|
414
|
+
await bsblan.set_time(current_time)
|
|
417
415
|
else:
|
|
418
416
|
print("\nSkipping device time sync for this bus type")
|
|
419
417
|
|
|
@@ -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.
|
|
3
|
+
version = "6.2.0"
|
|
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.90",
|
|
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
208
|
"pyupgrade==3.22.0",
|
|
209
|
-
"ruff==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))
|
|
@@ -233,7 +233,7 @@ class SectionValidator:
|
|
|
233
233
|
|
|
234
234
|
This enables more granular lazy loading for hot water - instead of
|
|
235
235
|
validating all 29 hot water parameters at once, we only validate
|
|
236
|
-
the specific group needed (essential:
|
|
236
|
+
the specific group needed (essential: 6, config: 16, schedule: 8).
|
|
237
237
|
|
|
238
238
|
Uses a per-group lock to prevent concurrent validation of the same
|
|
239
239
|
group, which could cause duplicate network requests.
|
|
@@ -51,9 +51,10 @@ from .models import (
|
|
|
51
51
|
State,
|
|
52
52
|
StaticState,
|
|
53
53
|
)
|
|
54
|
-
from .utility import is_param_value_active
|
|
54
|
+
from .utility import is_param_value_active
|
|
55
55
|
|
|
56
56
|
if TYPE_CHECKING:
|
|
57
|
+
from datetime import datetime
|
|
57
58
|
from typing import Self
|
|
58
59
|
|
|
59
60
|
from aiohttp.client import ClientSession
|
|
@@ -171,7 +172,20 @@ class BSBLAN:
|
|
|
171
172
|
if self.session is None:
|
|
172
173
|
self.session = aiohttp.ClientSession()
|
|
173
174
|
self._close_session = True
|
|
174
|
-
|
|
175
|
+
try:
|
|
176
|
+
await self.initialize()
|
|
177
|
+
except BaseException:
|
|
178
|
+
if self._close_session and self.session:
|
|
179
|
+
session = self.session
|
|
180
|
+
self.session = None
|
|
181
|
+
self._close_session = False
|
|
182
|
+
try:
|
|
183
|
+
await session.close()
|
|
184
|
+
except Exception:
|
|
185
|
+
logger.exception(
|
|
186
|
+
"Failed to close session after initialization failure"
|
|
187
|
+
)
|
|
188
|
+
raise
|
|
175
189
|
return self
|
|
176
190
|
|
|
177
191
|
async def __aexit__(self, *args: object) -> None:
|
|
@@ -332,7 +346,7 @@ class BSBLAN:
|
|
|
332
346
|
|
|
333
347
|
This enables more granular lazy loading for hot water - instead of
|
|
334
348
|
validating all 29 hot water parameters at once, we only validate
|
|
335
|
-
the specific group needed (essential:
|
|
349
|
+
the specific group needed (essential: 6, config: 16, schedule: 8).
|
|
336
350
|
|
|
337
351
|
Uses a per-group lock to prevent concurrent validation of the same
|
|
338
352
|
group, which could cause duplicate network requests.
|
|
@@ -720,7 +734,7 @@ class BSBLAN:
|
|
|
720
734
|
include: Optional list of parameter names to fetch. If None,
|
|
721
735
|
fetches all state parameters. Valid names include:
|
|
722
736
|
hvac_mode, target_temperature, hvac_action,
|
|
723
|
-
hvac_mode_changeover, current_temperature,
|
|
737
|
+
hvac_mode_changeover, current_temperature, current_setpoint,
|
|
724
738
|
room1_temp_setpoint_boost.
|
|
725
739
|
circuit: The heating circuit number (1 or 2). Defaults to 1.
|
|
726
740
|
Circuit 2 uses separate parameter IDs but returns the
|
|
@@ -782,15 +796,17 @@ class BSBLAN:
|
|
|
782
796
|
Args:
|
|
783
797
|
include: Optional list of parameter names to fetch. If None,
|
|
784
798
|
fetches all static parameters. Valid names include:
|
|
785
|
-
|
|
799
|
+
temp_reduced_setpoint, heating_protective_setpoint,
|
|
800
|
+
comfort_setpoint_max, cooling_comfort_setpoint_min,
|
|
801
|
+
cooling_reduced_setpoint, and min_temp, max_temp on PPS.
|
|
786
802
|
circuit: The heating circuit number (1 or 2). Defaults to 1.
|
|
787
803
|
|
|
788
804
|
Returns:
|
|
789
805
|
StaticState: The static information from the BSBLAN device.
|
|
790
806
|
|
|
791
807
|
Example:
|
|
792
|
-
# Fetch only
|
|
793
|
-
static = await client.static_values(include=["
|
|
808
|
+
# Fetch only the reduced setpoint
|
|
809
|
+
static = await client.static_values(include=["temp_reduced_setpoint"])
|
|
794
810
|
|
|
795
811
|
# Fetch static values for heating circuit 2
|
|
796
812
|
static_hc2 = await client.static_values(circuit=2)
|
|
@@ -847,26 +863,33 @@ class BSBLAN:
|
|
|
847
863
|
time_data = {"time": data["0"]}
|
|
848
864
|
return DeviceTime.model_validate(time_data)
|
|
849
865
|
|
|
850
|
-
async def set_time(self,
|
|
866
|
+
async def set_time(self, value: datetime) -> None:
|
|
851
867
|
"""Set the time on the BSB-LAN device.
|
|
852
868
|
|
|
853
869
|
Args:
|
|
854
|
-
|
|
855
|
-
|
|
870
|
+
value (datetime): The local time of the heating system. Its date and
|
|
871
|
+
time are written as they are, without timezone conversion.
|
|
856
872
|
|
|
857
873
|
Raises:
|
|
858
|
-
BSBLANInvalidParameterError: If the
|
|
874
|
+
BSBLANInvalidParameterError: If the year is out of range.
|
|
875
|
+
BSBLANError: If the device does not accept the new time.
|
|
859
876
|
|
|
860
877
|
"""
|
|
861
878
|
await self._ensure_device_metadata()
|
|
862
879
|
self._validate_time_sync_supported()
|
|
863
|
-
|
|
880
|
+
if not Validation.MIN_YEAR <= value.year <= Validation.MAX_YEAR:
|
|
881
|
+
msg = f"Invalid year: {value.year}"
|
|
882
|
+
raise BSBLANInvalidParameterError(msg)
|
|
883
|
+
# BSB-LAN 4.1 rejects a space between the date and the time
|
|
864
884
|
response = await self._request(
|
|
865
|
-
base_path="/JS",
|
|
885
|
+
base_path="/JS",
|
|
886
|
+
data=self._set_payload("0", value.strftime("%d.%m.%Y_%H:%M:%S")),
|
|
866
887
|
)
|
|
867
888
|
logger.debug("Response for setting time: %s", response)
|
|
889
|
+
if response.get("0", {}).get("status") != 1:
|
|
890
|
+
raise BSBLANError(ErrorMsg.TIME_SYNC_FAILED)
|
|
868
891
|
|
|
869
|
-
async def thermostat( # pylint: disable=too-many-arguments
|
|
892
|
+
async def thermostat( # noqa: PLR0913 # pylint: disable=too-many-arguments
|
|
870
893
|
self,
|
|
871
894
|
target_temperature: str | None = None,
|
|
872
895
|
hvac_mode: int | None = None,
|
|
@@ -874,6 +897,8 @@ class BSBLAN:
|
|
|
874
897
|
target_temperature_high: str | float | None = None,
|
|
875
898
|
*,
|
|
876
899
|
cooling_operating_mode: int | None = None,
|
|
900
|
+
temp_reduced_setpoint: str | float | None = None,
|
|
901
|
+
temporary_mode: int | None = None,
|
|
877
902
|
) -> None:
|
|
878
903
|
"""Change the state of the thermostat through BSB-Lan.
|
|
879
904
|
|
|
@@ -888,6 +913,12 @@ class BSBLAN:
|
|
|
888
913
|
cooling_operating_mode: The cooling circuit operating mode to set
|
|
889
914
|
as raw integer value: 0=Protection, 1=Automatic, 2=Reduced,
|
|
890
915
|
3=Comfort. Not supported on PPS devices.
|
|
916
|
+
temp_reduced_setpoint: The reduced (eco) room setpoint to set.
|
|
917
|
+
Not supported on PPS devices.
|
|
918
|
+
temporary_mode: Temporary operating mode override, active until
|
|
919
|
+
the next time program switch point. Raw integer value:
|
|
920
|
+
0=No override, 1=Reduced, 2=Comfort. Only has an effect in
|
|
921
|
+
automatic mode. Not supported on PPS devices.
|
|
891
922
|
|
|
892
923
|
Example:
|
|
893
924
|
# Set HC1 temperature
|
|
@@ -899,6 +930,12 @@ class BSBLAN:
|
|
|
899
930
|
# Set HC1 cooling operating mode
|
|
900
931
|
await client.thermostat(cooling_operating_mode=1)
|
|
901
932
|
|
|
933
|
+
# Set HC1 reduced setpoint
|
|
934
|
+
await client.thermostat(temp_reduced_setpoint="18.0")
|
|
935
|
+
|
|
936
|
+
# Switch HC1 to comfort until the next time program switch point
|
|
937
|
+
await client.thermostat(temporary_mode=2)
|
|
938
|
+
|
|
902
939
|
# Set HC2 mode
|
|
903
940
|
await client.thermostat(hvac_mode=1, circuit=2)
|
|
904
941
|
|
|
@@ -906,13 +943,14 @@ class BSBLAN:
|
|
|
906
943
|
self._validate_circuit(circuit)
|
|
907
944
|
if self._uses_pps_bus:
|
|
908
945
|
self._validate_bus_write_supported()
|
|
909
|
-
await self._initialize_temperature_range(circuit)
|
|
910
946
|
|
|
911
947
|
self._validate_single_parameter(
|
|
912
948
|
target_temperature,
|
|
913
949
|
hvac_mode,
|
|
914
950
|
target_temperature_high,
|
|
915
951
|
cooling_operating_mode,
|
|
952
|
+
temp_reduced_setpoint,
|
|
953
|
+
temporary_mode,
|
|
916
954
|
error_msg=ErrorMsg.MULTI_PARAMETER,
|
|
917
955
|
)
|
|
918
956
|
|
|
@@ -922,6 +960,8 @@ class BSBLAN:
|
|
|
922
960
|
circuit,
|
|
923
961
|
target_temperature_high,
|
|
924
962
|
cooling_operating_mode=cooling_operating_mode,
|
|
963
|
+
temp_reduced_setpoint=temp_reduced_setpoint,
|
|
964
|
+
temporary_mode=temporary_mode,
|
|
925
965
|
)
|
|
926
966
|
await self._set_device_state(state)
|
|
927
967
|
|
|
@@ -960,21 +1000,6 @@ class BSBLAN:
|
|
|
960
1000
|
circuit,
|
|
961
1001
|
)
|
|
962
1002
|
|
|
963
|
-
def _validate_time_format(self, time_value: str) -> None:
|
|
964
|
-
"""Validate the time format.
|
|
965
|
-
|
|
966
|
-
Args:
|
|
967
|
-
time_value (str): The time value to validate.
|
|
968
|
-
|
|
969
|
-
Raises:
|
|
970
|
-
BSBLANInvalidParameterError: If the time format is invalid.
|
|
971
|
-
|
|
972
|
-
"""
|
|
973
|
-
try:
|
|
974
|
-
validate_time_format(time_value, Validation.MIN_YEAR, Validation.MAX_YEAR)
|
|
975
|
-
except ValueError as err:
|
|
976
|
-
raise BSBLANInvalidParameterError(str(err)) from err
|
|
977
|
-
|
|
978
1003
|
def _set_payload(
|
|
979
1004
|
self, parameter: str, value: str, type_: str = "1"
|
|
980
1005
|
) -> dict[str, Any]:
|
|
@@ -1010,14 +1035,15 @@ class BSBLAN:
|
|
|
1010
1035
|
that are typically checked frequently for monitoring purposes.
|
|
1011
1036
|
This reduces API calls and improves performance for regular polling.
|
|
1012
1037
|
|
|
1013
|
-
Uses granular lazy loading - only validates the
|
|
1038
|
+
Uses granular lazy loading - only validates the 6 essential params,
|
|
1014
1039
|
not all 29 hot water parameters.
|
|
1015
1040
|
|
|
1016
1041
|
Args:
|
|
1017
1042
|
include: Optional list of parameter names to fetch. If None,
|
|
1018
1043
|
fetches all essential hot water parameters. Valid names include:
|
|
1019
1044
|
operating_mode, nominal_setpoint, release,
|
|
1020
|
-
dhw_actual_value_top_temperature,
|
|
1045
|
+
dhw_actual_value_top_temperature, dhw_current_setpoint,
|
|
1046
|
+
state_dhw_pump.
|
|
1021
1047
|
|
|
1022
1048
|
Returns:
|
|
1023
1049
|
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
|
|
|
@@ -181,12 +184,16 @@ class CircuitConfig:
|
|
|
181
184
|
"target_temperature_high": "902",
|
|
182
185
|
"hvac_mode": "700",
|
|
183
186
|
"cooling_operating_mode": "901",
|
|
187
|
+
"temp_reduced_setpoint": "712",
|
|
188
|
+
"temporary_mode": "10110",
|
|
184
189
|
},
|
|
185
190
|
2: {
|
|
186
191
|
"target_temperature": "1010",
|
|
187
192
|
"target_temperature_high": "1202",
|
|
188
193
|
"hvac_mode": "1000",
|
|
189
194
|
"cooling_operating_mode": "1201",
|
|
195
|
+
"temp_reduced_setpoint": "1012",
|
|
196
|
+
"temporary_mode": "10111",
|
|
190
197
|
},
|
|
191
198
|
}
|
|
192
199
|
PROBE_PARAMS: Final[dict[int, str]] = {
|
|
@@ -252,6 +259,9 @@ class Validation:
|
|
|
252
259
|
# 901/1201 cooling circuit operating mode (ENUM, verified on device):
|
|
253
260
|
# 0=Protection, 1=Automatic, 2=Reduced, 3=Comfort
|
|
254
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}
|
|
255
265
|
PPS_HVAC_MODES: Final[set[int]] = {0, 1, 3}
|
|
256
266
|
PPS_HVAC_MODE_TO_BSBLAN: Final[dict[int, str]] = {
|
|
257
267
|
0: "2", # off
|
|
@@ -415,19 +425,20 @@ _STATUS_TO_CATEGORY: dict[HeatingCircuitStatus, HVACActionCategory] = {
|
|
|
415
425
|
# Off states
|
|
416
426
|
HeatingCircuitStatus.FAULT_ERROR: HVACActionCategory.OFF,
|
|
417
427
|
HeatingCircuitStatus.STANDBY_1: HVACActionCategory.OFF,
|
|
418
|
-
HeatingCircuitStatus.OFF: HVACActionCategory.OFF,
|
|
419
428
|
HeatingCircuitStatus.STANDBY_2: HVACActionCategory.OFF,
|
|
420
429
|
HeatingCircuitStatus.COOLING_OFF: HVACActionCategory.OFF,
|
|
421
|
-
HeatingCircuitStatus.COOLING_LOCKED: HVACActionCategory.OFF,
|
|
422
430
|
HeatingCircuitStatus.HEATING_GENERATOR_OFF: HVACActionCategory.OFF,
|
|
423
431
|
HeatingCircuitStatus.HEATING_OFF: HVACActionCategory.OFF,
|
|
424
|
-
|
|
425
|
-
HeatingCircuitStatus.
|
|
426
|
-
HeatingCircuitStatus.
|
|
427
|
-
HeatingCircuitStatus.
|
|
428
|
-
HeatingCircuitStatus.
|
|
429
|
-
HeatingCircuitStatus.
|
|
430
|
-
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,
|
|
431
442
|
# Heating states
|
|
432
443
|
HeatingCircuitStatus.MANUAL_CONTROL: HVACActionCategory.HEATING,
|
|
433
444
|
HeatingCircuitStatus.OVERRUN: HVACActionCategory.HEATING,
|
|
@@ -550,6 +561,7 @@ class ErrorMsg:
|
|
|
550
561
|
)
|
|
551
562
|
NO_HEATING_SCHEDULE_PARAMS = "No heating schedule parameters available"
|
|
552
563
|
TIME_SYNC_NOT_SUPPORTED = "Time synchronization is not supported by this device"
|
|
564
|
+
TIME_SYNC_FAILED = "The device did not accept the new time"
|
|
553
565
|
BUS_WRITE_NOT_SUPPORTED = "Writing parameters is not supported by this device"
|
|
554
566
|
|
|
555
567
|
|
|
@@ -642,6 +654,7 @@ class HotWaterParams:
|
|
|
642
654
|
"nominal_setpoint",
|
|
643
655
|
"release",
|
|
644
656
|
"dhw_actual_value_top_temperature",
|
|
657
|
+
"dhw_current_setpoint",
|
|
645
658
|
"state_dhw_pump",
|
|
646
659
|
}
|
|
647
660
|
}
|
|
@@ -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
|
|