python-bsblan 6.1.10__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.10 → python_bsblan-6.1.11}/.github/workflows/codeql.yaml +2 -2
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/scorecard.yml +1 -1
- python_bsblan-6.1.11/.nvmrc +1 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/PKG-INFO +1 -1
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/docs/getting-started.md +38 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/examples/fake_bsblan_server.py +6 -2
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/package-lock.json +4 -4
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/package.json +1 -1
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/pyproject.toml +5 -5
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/_thermostat.py +44 -9
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/_validation.py +1 -1
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/bsblan.py +43 -10
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/constants.py +21 -9
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/models.py +3 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/hot_water_state.json +13 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/state.json +13 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/state_circuit2.json +13 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_circuit.py +35 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_constants.py +9 -3
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_context_manager.py +76 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_hotwater_state.py +6 -2
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_pps.py +50 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_state.py +5 -1
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_temperature_unit.py +14 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_thermostat.py +131 -1
- python_bsblan-6.1.10/.nvmrc +0 -1
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.editorconfig +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.gitattributes +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/CODE_OF_CONDUCT.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/CONTRIBUTING.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/ISSUE_TEMPLATE/PULL_REQUEST_TEMPLATE.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/SECURITY.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/agents/pr-review.agent.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/agents/security-reviewer.agent.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/agents/tdd-refactor.agent.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/copilot-instructions.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/labels.yml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/prompts/add-parameter.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/prompts/code-review.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/prompts/opsx-apply.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/prompts/opsx-archive.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/prompts/opsx-explore.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/prompts/opsx-propose.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/prompts/opsx-sync.prompt.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/release-drafter.yml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/renovate.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/skills/bsblan-parameters/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/skills/bsblan-testing/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/skills/feature-doc-updates/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/skills/openspec-apply-change/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/skills/openspec-archive-change/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/skills/openspec-explore/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/skills/openspec-propose/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/skills/openspec-sync-specs/SKILL.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/auto-approve-renovate.yml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/dependency-review.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/docs.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/labels.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/linting.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/lock.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/pr-labels.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/release-drafter.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/release.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/stale.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/tests.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/workflows/typing.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.github/zizmor.yml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.gitignore +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.pre-commit-config.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.prettierignore +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/.yamllint +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/AGENTS.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/CLAUDE.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/LICENSE.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/Makefile +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/README.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/docs/api/client.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/docs/api/constants.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/docs/api/exceptions.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/docs/api/models.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/docs/index.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/examples/control.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/examples/discovery.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/examples/fetch_param.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/examples/profile_init.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/examples/ruff.toml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/examples/speed_test.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/mkdocs.yml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/.openspec.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/design.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/proposal.md +0 -0
- {python_bsblan-6.1.10 → 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.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-03-add-cooling-operating-mode/tasks.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/.openspec.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/design.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/proposal.md +0 -0
- {python_bsblan-6.1.10 → 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.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-harden-circuit-discovery-consistency/tasks.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/.openspec.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/design.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/proposal.md +0 -0
- {python_bsblan-6.1.10 → 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.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-04-improve-client-readability-domain-slices/tasks.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/.openspec.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/design.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/proposal.md +0 -0
- {python_bsblan-6.1.10 → 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.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-12-distinguish-schedule-error-types/tasks.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/.openspec.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/design.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/proposal.md +0 -0
- {python_bsblan-6.1.10 → 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.10 → python_bsblan-6.1.11}/openspec/changes/archive/2026-07-16-fix-partial-validation-state/tasks.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/config.yaml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/specs/circuit-discovery/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/specs/client-retry-policy/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/specs/cooling-operating-mode/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/specs/error-classification/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/openspec/specs/lazy-validation-completeness/spec.md +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/sonar-project.properties +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/__init__.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/_hot_water.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/_parameters.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/_schedules.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/_temperature.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/_transport.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/_version.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/exceptions.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/py.typed +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/src/bsblan/utility.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/__init__.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/conftest.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/device.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/dict_version.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/info.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/password.txt +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/pps_device.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/pps_state.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/pps_static_values.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/sensor.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/static_state.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/static_state_circuit2.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/thermostat_hvac.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/thermostat_temp.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/fixtures/time.json +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/ruff.toml +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_api_initialization.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_api_validation.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_auth.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_backoff_retry.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_bsblan.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_bsblan_edge_cases.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_configuration.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_device.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_dhw_time_switch.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_entity_info.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_entity_info_ha.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_heating_schedule.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_hot_water_additional.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_include_parameter.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_info.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_initialization.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_read_parameters.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_reset_validation.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_response_encoding.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_schedule_models.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_sensor.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_set_heating_schedule.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_set_hot_water_schedule.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_set_hotwater.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_static_state.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_temperature_validation.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_time.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_utility.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_utility_additional.py +0 -0
- {python_bsblan-6.1.10 → python_bsblan-6.1.11}/tests/test_utility_edge_cases.py +0 -0
- {python_bsblan-6.1.10 → 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
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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
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.
|
|
@@ -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
|
|
|
@@ -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,
|
|
@@ -642,6 +653,7 @@ class HotWaterParams:
|
|
|
642
653
|
"nominal_setpoint",
|
|
643
654
|
"release",
|
|
644
655
|
"dhw_actual_value_top_temperature",
|
|
656
|
+
"dhw_current_setpoint",
|
|
645
657
|
"state_dhw_pump",
|
|
646
658
|
}
|
|
647
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",
|
|
@@ -86,6 +86,19 @@
|
|
|
86
86
|
"readwrite": 1,
|
|
87
87
|
"unit": "°C"
|
|
88
88
|
},
|
|
89
|
+
"8741": {
|
|
90
|
+
"name": "Room temperature setpoint 1",
|
|
91
|
+
"dataType_name": "TEMP",
|
|
92
|
+
"dataType_family": "VALS",
|
|
93
|
+
"error": 0,
|
|
94
|
+
"value": "18.0",
|
|
95
|
+
"desc": "",
|
|
96
|
+
"precision": 0.1,
|
|
97
|
+
"dataType": 0,
|
|
98
|
+
"readonly": 1,
|
|
99
|
+
"readwrite": 1,
|
|
100
|
+
"unit": "°C"
|
|
101
|
+
},
|
|
89
102
|
"770": {
|
|
90
103
|
"name": "Room temp setpoint boost (boost heating)",
|
|
91
104
|
"dataType_name": "TEMP",
|
|
@@ -86,6 +86,19 @@
|
|
|
86
86
|
"readwrite": 1,
|
|
87
87
|
"unit": "°C"
|
|
88
88
|
},
|
|
89
|
+
"8771": {
|
|
90
|
+
"name": "Room temperature setpoint 2",
|
|
91
|
+
"dataType_name": "TEMP",
|
|
92
|
+
"dataType_family": "VALS",
|
|
93
|
+
"error": 0,
|
|
94
|
+
"value": "19.0",
|
|
95
|
+
"desc": "",
|
|
96
|
+
"precision": 0.1,
|
|
97
|
+
"dataType": 0,
|
|
98
|
+
"readonly": 1,
|
|
99
|
+
"readwrite": 1,
|
|
100
|
+
"unit": "°C"
|
|
101
|
+
},
|
|
89
102
|
"1070": {
|
|
90
103
|
"name": "Room temp setpoint boost (boost heating)",
|
|
91
104
|
"dataType_name": "TEMP",
|
|
@@ -149,6 +149,8 @@ async def test_state_circuit2(monkeypatch: Any) -> None:
|
|
|
149
149
|
assert state.hvac_mode.desc == "Automatic"
|
|
150
150
|
assert state.current_temperature is not None
|
|
151
151
|
assert state.current_temperature.value == 18.5
|
|
152
|
+
assert state.current_setpoint is not None
|
|
153
|
+
assert state.current_setpoint.value == 19.0
|
|
152
154
|
assert state.target_temperature_high is not None
|
|
153
155
|
assert state.target_temperature_high.value == 24.0
|
|
154
156
|
assert state.cooling_operating_mode is not None
|
|
@@ -491,6 +493,39 @@ async def test_thermostat_circuit2_cooling_operating_mode(
|
|
|
491
493
|
)
|
|
492
494
|
|
|
493
495
|
|
|
496
|
+
@pytest.mark.asyncio
|
|
497
|
+
@pytest.mark.parametrize(
|
|
498
|
+
("kwargs", "parameter", "value"),
|
|
499
|
+
[
|
|
500
|
+
({"temp_reduced_setpoint": "17"}, "1012", "17"),
|
|
501
|
+
({"temporary_mode": 2}, "10111", "2"),
|
|
502
|
+
],
|
|
503
|
+
)
|
|
504
|
+
async def test_thermostat_circuit2_reduced_and_temporary_mode(
|
|
505
|
+
mock_bsblan_circuit: BSBLAN,
|
|
506
|
+
aresponses: ResponsesMockServer,
|
|
507
|
+
kwargs: dict[str, Any],
|
|
508
|
+
parameter: str,
|
|
509
|
+
value: str,
|
|
510
|
+
) -> None:
|
|
511
|
+
"""Test setting the reduced setpoint and temporary mode on circuit 2."""
|
|
512
|
+
mock_bsblan_circuit._temperature._circuit_temp_ranges[2] = {
|
|
513
|
+
"min": 16.0,
|
|
514
|
+
"max": 28.0,
|
|
515
|
+
}
|
|
516
|
+
mock_bsblan_circuit._temperature._circuit_temp_initialized.add(2)
|
|
517
|
+
|
|
518
|
+
aresponses.add(
|
|
519
|
+
"example.com",
|
|
520
|
+
"/JS",
|
|
521
|
+
"POST",
|
|
522
|
+
create_response_handler(
|
|
523
|
+
{"Parameter": parameter, "Value": value, "Type": "1"},
|
|
524
|
+
),
|
|
525
|
+
)
|
|
526
|
+
await mock_bsblan_circuit.thermostat(**kwargs, circuit=2)
|
|
527
|
+
|
|
528
|
+
|
|
494
529
|
# --- Temperature range initialization tests ---
|
|
495
530
|
|
|
496
531
|
|