python-bsblan 6.0.1__tar.gz → 6.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. python_bsblan-6.1.1/.github/agents/security-reviewer.agent.md +32 -0
  2. python_bsblan-6.1.1/.github/copilot-instructions.md +7 -0
  3. python_bsblan-6.1.1/.github/hooks/run-validation-after-edits.json +11 -0
  4. python_bsblan-6.1.1/.github/hooks/run_validation_after_edits.sh +72 -0
  5. python_bsblan-6.1.1/.github/prompts/code-review.prompt.md +65 -0
  6. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/skills/bsblan-testing/SKILL.md +47 -1
  7. python_bsblan-6.1.1/.github/skills/feature-doc-updates/SKILL.md +75 -0
  8. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/lock.yaml +1 -1
  9. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/release-drafter.yaml +1 -1
  10. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/stale.yaml +1 -1
  11. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/tests.yaml +10 -2
  12. python_bsblan-6.1.1/.nvmrc +1 -0
  13. python_bsblan-6.1.1/AGENTS.md +98 -0
  14. python_bsblan-6.1.1/CLAUDE.md +1 -0
  15. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/PKG-INFO +1 -1
  16. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/docs/getting-started.md +37 -0
  17. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/docs/index.md +2 -1
  18. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/examples/control.py +97 -95
  19. python_bsblan-6.1.1/examples/fetch_param.py +165 -0
  20. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/pyproject.toml +3 -3
  21. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/src/bsblan/bsblan.py +134 -18
  22. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/src/bsblan/constants.py +50 -65
  23. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/src/bsblan/models.py +11 -0
  24. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/dict_version.json +3 -2
  25. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/state.json +13 -0
  26. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/state_circuit2.json +13 -0
  27. python_bsblan-6.1.1/tests/fixtures/static_state.json +37 -0
  28. python_bsblan-6.1.1/tests/fixtures/static_state_circuit2.json +37 -0
  29. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_api_initialization.py +5 -5
  30. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_api_validation.py +5 -5
  31. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_circuit.py +99 -12
  32. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_constants.py +56 -51
  33. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_include_parameter.py +8 -5
  34. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_initialization.py +5 -7
  35. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_pps.py +18 -0
  36. python_bsblan-6.1.1/tests/test_state.py +158 -0
  37. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_static_state.py +11 -4
  38. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_temperature_unit.py +6 -0
  39. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_temperature_validation.py +68 -8
  40. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_thermostat.py +41 -1
  41. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_utility.py +2 -1
  42. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_utility_additional.py +3 -2
  43. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_version_errors.py +25 -10
  44. python_bsblan-6.0.1/.github/copilot-instructions.md +0 -277
  45. python_bsblan-6.0.1/.github/prompts/code-review.prompt.md +0 -40
  46. python_bsblan-6.0.1/.nvmrc +0 -1
  47. python_bsblan-6.0.1/AGENTS.md +0 -1
  48. python_bsblan-6.0.1/CLAUDE.md +0 -1
  49. python_bsblan-6.0.1/examples/fetch_param.py +0 -67
  50. python_bsblan-6.0.1/tests/fixtures/static_state.json +0 -16
  51. python_bsblan-6.0.1/tests/fixtures/static_state_circuit2.json +0 -16
  52. python_bsblan-6.0.1/tests/test_state.py +0 -85
  53. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.editorconfig +0 -0
  54. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.gitattributes +0 -0
  55. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/CODE_OF_CONDUCT.md +0 -0
  56. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/CONTRIBUTING.md +0 -0
  57. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/ISSUE_TEMPLATE/PULL_REQUEST_TEMPLATE.md +0 -0
  58. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  59. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  60. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/SECURITY.md +0 -0
  61. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/labels.yml +0 -0
  62. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/prompts/add-parameter.prompt.md +0 -0
  63. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/release-drafter.yml +0 -0
  64. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/renovate.json +0 -0
  65. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/skills/bsblan-parameters/SKILL.md +0 -0
  66. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/auto-approve-renovate.yml +0 -0
  67. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/codeql.yaml +0 -0
  68. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/dependency-review.yaml +0 -0
  69. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/docs.yaml +0 -0
  70. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/labels.yaml +0 -0
  71. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/linting.yaml +0 -0
  72. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/pr-labels.yaml +0 -0
  73. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/release.yaml +0 -0
  74. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/scorecard.yml +0 -0
  75. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/workflows/typing.yaml +0 -0
  76. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.github/zizmor.yml +0 -0
  77. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.gitignore +0 -0
  78. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.pre-commit-config.yaml +0 -0
  79. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.prettierignore +0 -0
  80. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/.yamllint +0 -0
  81. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/LICENSE.md +0 -0
  82. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/Makefile +0 -0
  83. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/README.md +0 -0
  84. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/docs/api/client.md +0 -0
  85. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/docs/api/constants.md +0 -0
  86. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/docs/api/exceptions.md +0 -0
  87. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/docs/api/models.md +0 -0
  88. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/examples/discovery.py +0 -0
  89. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/examples/profile_init.py +0 -0
  90. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/examples/ruff.toml +0 -0
  91. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/examples/speed_test.py +0 -0
  92. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/mkdocs.yml +0 -0
  93. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/package-lock.json +0 -0
  94. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/package.json +0 -0
  95. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/sonar-project.properties +0 -0
  96. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/src/bsblan/__init__.py +0 -0
  97. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/src/bsblan/exceptions.py +0 -0
  98. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/src/bsblan/py.typed +0 -0
  99. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/src/bsblan/utility.py +0 -0
  100. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/__init__.py +0 -0
  101. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/conftest.py +0 -0
  102. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/device.json +0 -0
  103. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/hot_water_state.json +0 -0
  104. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/info.json +0 -0
  105. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/password.txt +0 -0
  106. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/pps_device.json +0 -0
  107. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/pps_state.json +0 -0
  108. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/pps_static_values.json +0 -0
  109. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/sensor.json +0 -0
  110. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/thermostat_hvac.json +0 -0
  111. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/thermostat_temp.json +0 -0
  112. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/fixtures/time.json +0 -0
  113. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/ruff.toml +0 -0
  114. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_auth.py +0 -0
  115. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_backoff_retry.py +0 -0
  116. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_bsblan.py +0 -0
  117. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_bsblan_edge_cases.py +0 -0
  118. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_configuration.py +0 -0
  119. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_context_manager.py +0 -0
  120. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_device.py +0 -0
  121. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_dhw_time_switch.py +0 -0
  122. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_entity_info.py +0 -0
  123. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_entity_info_ha.py +0 -0
  124. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_heating_schedule.py +0 -0
  125. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_hot_water_additional.py +0 -0
  126. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_hotwater_state.py +0 -0
  127. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_info.py +0 -0
  128. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_read_parameters.py +0 -0
  129. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_reset_validation.py +0 -0
  130. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_schedule_models.py +0 -0
  131. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_sensor.py +0 -0
  132. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_set_heating_schedule.py +0 -0
  133. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_set_hot_water_schedule.py +0 -0
  134. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_set_hotwater.py +0 -0
  135. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_time.py +0 -0
  136. {python_bsblan-6.0.1 → python_bsblan-6.1.1}/tests/test_utility_edge_cases.py +0 -0
@@ -0,0 +1,32 @@
1
+ ---
2
+ description: "Use when you need a security review, threat modeling, vulnerability triage, secrets exposure checks, auth/session risk analysis, input validation review, dependency risk review, or secure coding feedback."
3
+ name: "Security Reviewer"
4
+ tools: [read, search]
5
+ argument-hint: "Provide scope (files, PR, or feature), threat context, and any known attack surface."
6
+ ---
7
+ You are a specialist security code reviewer. Your job is to find exploitable risks and provide precise, actionable remediation guidance.
8
+
9
+ ## Constraints
10
+ - DO NOT refactor for style or performance unless it directly affects security.
11
+ - DO NOT propose broad rewrites when a targeted fix is sufficient.
12
+ - DO NOT claim an issue without explaining exploitability, impact, and preconditions.
13
+ - ONLY report findings that are security-relevant or materially increase security risk.
14
+
15
+ ## Approach
16
+ 1. Map attack surface first: inputs, trust boundaries, secrets, authn/authz, network calls, file/system access, and third-party dependencies.
17
+ 2. Prioritize exploitability over code smell; assess realistic attacker paths and blast radius.
18
+ 3. Verify mitigations already present to avoid false positives.
19
+ 4. Provide concrete fixes with smallest safe change and validation steps.
20
+
21
+ ## Output Format
22
+ 1. Findings (ordered by severity: critical, high, medium, low)
23
+ - Title
24
+ - Severity
25
+ - Location (file and line)
26
+ - Why this is vulnerable
27
+ - Exploit scenario
28
+ - Recommended fix
29
+ 2. Open questions or assumptions
30
+ 3. Residual risk and security test gaps
31
+
32
+ If no findings are discovered, explicitly state that and list residual risks or testing gaps.
@@ -0,0 +1,7 @@
1
+ # Canonical Instructions
2
+
3
+ The canonical instruction file for this repository is:
4
+
5
+ - `AGENTS.md`
6
+
7
+ Keep this file as a lightweight compatibility pointer to avoid duplicated guidance.
@@ -0,0 +1,11 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "type": "command",
6
+ "command": "./.github/hooks/run_validation_after_edits.sh",
7
+ "timeout": 1800
8
+ }
9
+ ]
10
+ }
11
+ }
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ payload="$(cat)"
5
+
6
+ should_run="$(HOOK_PAYLOAD="$payload" python3 - <<'PY'
7
+ import json
8
+ import os
9
+ import sys
10
+
11
+ edit_tool_tokens = {
12
+ "edit",
13
+ "write",
14
+ "multi_edit",
15
+ "multiedit",
16
+ "apply_patch",
17
+ "create_file",
18
+ "edit_notebook_file",
19
+ "create_new_jupyter_notebook",
20
+ "mcp_github_create_or_update_file",
21
+ "mcp_io_github_git_create_or_update_file",
22
+ }
23
+
24
+ def walk(value):
25
+ if isinstance(value, dict):
26
+ for key, item in value.items():
27
+ yield str(key)
28
+ yield from walk(item)
29
+ elif isinstance(value, list):
30
+ for item in value:
31
+ yield from walk(item)
32
+ elif isinstance(value, str):
33
+ yield value
34
+
35
+ raw = os.environ.get("HOOK_PAYLOAD", "").strip()
36
+ if not raw:
37
+ print("skip")
38
+ raise SystemExit(0)
39
+
40
+ try:
41
+ data = json.loads(raw)
42
+ except json.JSONDecodeError:
43
+ print("skip")
44
+ raise SystemExit(0)
45
+
46
+ haystack = "\n".join(s.lower() for s in walk(data))
47
+ if any(token in haystack for token in edit_tool_tokens):
48
+ print("run")
49
+ else:
50
+ print("skip")
51
+ PY
52
+ )"
53
+
54
+ if [[ "$should_run" != "run" ]]; then
55
+ exit 0
56
+ fi
57
+
58
+ echo "[hook] File edit detected. Running validation commands..."
59
+
60
+ echo "[hook] Running tests"
61
+ if ! uv run pytest --no-cov; then
62
+ echo "[hook] Tests failed"
63
+ exit 2
64
+ fi
65
+
66
+ echo "[hook] Running prek"
67
+ if ! uv run prek run --all-files; then
68
+ echo "[hook] Prek failed"
69
+ exit 2
70
+ fi
71
+
72
+ echo "[hook] Validation passed"
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: Code Review
3
+ agent: agent
4
+ description: Review python-bsblan changes with findings first and repo quality gates
5
+ argument-hint: "Scope to review (staged changes, PR #, or file paths)"
6
+ ---
7
+
8
+ # Code Review
9
+
10
+ Review the requested scope for regressions, correctness issues, and missing
11
+ tests.
12
+
13
+ Prioritize findings over summaries and order findings by severity.
14
+ Use this output format:
15
+
16
+ 1. Findings
17
+ - Severity (`high`, `medium`, `low`), location, and impact.
18
+ - Include precise file references and actionable fixes.
19
+
20
+ 2. Open Questions / Assumptions
21
+ - Unknowns that block confidence in the review.
22
+
23
+ 3. Change Summary
24
+ - Brief recap only after findings.
25
+
26
+ Reference these standards while reviewing:
27
+ - [CONTRIBUTING](../CONTRIBUTING.md)
28
+ - [Copilot instructions](../copilot-instructions.md)
29
+
30
+ ## Review Checks
31
+
32
+ ### Code Quality
33
+ - [ ] Type hints on all functions
34
+ - [ ] Docstrings on public methods
35
+ - [ ] Line length under 88 characters
36
+ - [ ] Consistent parameter naming (snake_case)
37
+
38
+ ### Testing
39
+ - [ ] Tests added for new functionality
40
+ - [ ] Total coverage remains 95%+
41
+ - [ ] Patch coverage for changed code is 100%
42
+
43
+ ### Patterns
44
+ - [ ] Response models use `pydantic` `BaseModel`
45
+ - [ ] Set-parameter models use `@dataclass`
46
+ - [ ] Uses `aiohttp` for async HTTP
47
+ - [ ] Follows existing parameter naming conventions
48
+ - [ ] Error handling uses custom exceptions (`BSBLANError`,
49
+ `BSBLANConnectionError`)
50
+
51
+ ### Prek
52
+ - [ ] Ruff passes (linting + formatting)
53
+ - [ ] ty passes (type checking)
54
+ - [ ] Pylint passes (code analysis)
55
+ - [ ] Pytest passes (tests)
56
+
57
+ ## Validation Commands
58
+
59
+ ```bash
60
+ uv run prek run --all-files
61
+ uv run pytest --cov=src/bsblan --cov-report=term-missing
62
+ ```
63
+
64
+ If no findings are discovered, explicitly say so and list any residual testing
65
+ gaps.
@@ -39,7 +39,16 @@ Test fixtures (JSON responses) are in `tests/fixtures/`. Common fixtures:
39
39
  - `hot_water_state.json` - Hot water state
40
40
  - `sensor.json` - Sensor readings
41
41
 
42
- Load fixtures using the `load_fixture` helper from `conftest.py`.
42
+ Load fixtures using `load_fixture(filename: str) -> str` from `tests/__init__.py`.
43
+
44
+ ```python
45
+ import json
46
+
47
+ from tests import load_fixture
48
+
49
+ raw = load_fixture("device.json")
50
+ data = json.loads(raw) # parsed dict[str, Any]
51
+ ```
43
52
 
44
53
  ## Coverage Requirements
45
54
 
@@ -84,6 +93,8 @@ CI enforces:
84
93
  - Patch coverage = 100% (Codecov checks new/modified lines)
85
94
 
86
95
  If CI fails with coverage issues, check the Codecov report in the PR for uncovered lines.
96
+ If a line is genuinely untestable (for example defensive guards), mark it with
97
+ `# pragma: no cover` and justify that choice in the PR description.
87
98
 
88
99
  ## Running Tests
89
100
 
@@ -126,6 +137,41 @@ mock_bsblan._request.assert_awaited_with(
126
137
  )
127
138
  ```
128
139
 
140
+ ### Fixture Setup and Registration
141
+
142
+ Define shared fixtures in `tests/conftest.py` so pytest auto-discovers them.
143
+ Use the `mock_bsblan` pattern for naming and setup:
144
+
145
+ ```python
146
+ @pytest.fixture
147
+ async def mock_bsblan(
148
+ aresponses: ResponsesMockServer,
149
+ monkeypatch: Any,
150
+ ) -> AsyncGenerator[BSBLAN, Any]:
151
+ ...
152
+ ```
153
+
154
+ Add new JSON payloads in `tests/fixtures/` with descriptive, snake_case names
155
+ that match the behavior under test.
156
+
157
+ ### Mocking HTTP Response Handling
158
+
159
+ Use `monkeypatch` with `AsyncMock` to return fixture payloads when testing
160
+ response parsing logic (not only outgoing request arguments):
161
+
162
+ ```python
163
+ import json
164
+ from unittest.mock import AsyncMock
165
+
166
+ from tests import load_fixture
167
+
168
+ request_mock = AsyncMock(return_value=json.loads(load_fixture("state.json")))
169
+ monkeypatch.setattr(bsblan, "_request", request_mock)
170
+
171
+ state = await bsblan.state()
172
+ assert state.current_temperature is not None
173
+ ```
174
+
129
175
  ## Testing Lazy Loading
130
176
 
131
177
  When testing hot water methods, mark param groups as validated to skip network calls:
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: feature-doc-updates
3
+ description: Ensure README and documentation are updated when adding features in python-bsblan. Use when implementing new behavior, parameters, API surface changes, or user-visible capabilities.
4
+ argument-hint: What feature was added and which files changed?
5
+ ---
6
+
7
+ # Feature Documentation Updates
8
+
9
+ Use this skill after implementing a feature so user-facing documentation stays accurate.
10
+
11
+ ## When To Use
12
+
13
+ Use this workflow when a change includes one or more of the following:
14
+ - New public method, model field, parameter, or behavior.
15
+ - Changed defaults, constraints, validation, or supported API versions.
16
+ - New examples, setup steps, or migration considerations.
17
+ - Any change that would alter how users call or understand the library.
18
+
19
+ ## Inputs
20
+
21
+ Collect these inputs before writing docs:
22
+ - Feature summary in one sentence.
23
+ - Files changed in src and tests.
24
+ - Any new parameter IDs and names.
25
+ - Breaking changes or renamed fields.
26
+ - Example usage snippet (if applicable).
27
+
28
+ ## Procedure
29
+
30
+ 1. Classify feature impact.
31
+ - User-facing: update README and docs pages.
32
+ - Internal-only refactor with identical behavior: docs update is optional; add a short rationale in PR notes.
33
+
34
+ 2. Determine documentation targets.
35
+ - Update README when install, quick start, supported behavior, or public API usage changes.
36
+ - Update docs under docs/ when API, constants, models, or behavior explanations changed.
37
+ - Update examples/ when new behavior benefits from a runnable example.
38
+
39
+ 3. Apply documentation updates.
40
+ - README: adjust feature lists, capability notes, usage snippets, and compatibility statements.
41
+ - docs/: update the relevant page in docs/api or docs/getting-started to match the implementation.
42
+ - Keep terms, parameter names, and types identical to source code.
43
+
44
+ 4. Validate consistency with code.
45
+ - Confirm names in docs match constants and model fields exactly.
46
+ - Confirm examples call real methods and use valid arguments.
47
+ - Confirm version notes align with constants version-gating logic.
48
+
49
+ 5. Check deprecations and renames.
50
+ - If a public field/parameter is renamed, document migration guidance.
51
+ - Mention deprecation behavior and replacement names in docs where users will see it.
52
+
53
+ 6. Run quality checks.
54
+ - Run project checks: uv run prek run --all-files
55
+ - If API/docs behavior changed, run tests to verify examples and described behavior are still valid.
56
+
57
+ 7. Final completion check.
58
+ - README updated if user-visible behavior changed.
59
+ - Relevant docs pages updated if API or behavior changed.
60
+ - Any required examples updated.
61
+ - PR description includes a short Docs Updated section listing touched doc files.
62
+
63
+ ## Decision Rules
64
+
65
+ - Update README is required when feature discovery or onboarding changes.
66
+ - Update docs pages is required when API shape or semantics change.
67
+ - If neither changed, explicitly state why docs were not updated.
68
+
69
+ ## Output Format
70
+
71
+ When using this skill, produce:
72
+ 1. Documentation Impact Summary.
73
+ 2. Files updated (README, docs pages, examples).
74
+ 3. Any follow-up docs work still needed.
75
+ 4. Validation status for checks/tests.
@@ -18,7 +18,7 @@ jobs:
18
18
  pull-requests: write
19
19
  steps:
20
20
  # yamllint disable-line rule:line-length
21
- - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0
21
+ - uses: dessant/lock-threads@89ae32b08ed1a541efecbab17912962a5e38981c # v6.0.2
22
22
  with:
23
23
  github-token: ${{ github.token }}
24
24
  issue-inactive-days: "30"
@@ -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@c2e2804cc59f45f57076a99af580d0fedb697927 # v7.3.0
39
+ uses: release-drafter/release-drafter@693d20e7c1ce1a81d3a41962f85914253b518449 # v7.3.1
40
40
  with:
41
41
  prerelease: ${{ github.event.inputs.prerelease == 'true' }}
42
42
  prerelease-identifier: ${{ github.event.inputs.prerelease_identifier }}
@@ -18,7 +18,7 @@ jobs:
18
18
  pull-requests: write
19
19
  steps:
20
20
  - name: 🚀 Run stale
21
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
21
+ uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
22
22
  with:
23
23
  repo-token: ${{ secrets.GITHUB_TOKEN }}
24
24
  days-before-stale: 30
@@ -86,14 +86,22 @@ jobs:
86
86
  if: env.HAS_CODECOV_TOKEN == 'true'
87
87
  uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
88
88
  with:
89
+ fail_ci_if_error: true
90
+ files: coverage.xml
89
91
  token: ${{ secrets.CODECOV_TOKEN }}
92
+ - name: 🚀 Upload coverage report (tokenless)
93
+ if: env.HAS_CODECOV_TOKEN != 'true' && github.event_name == 'pull_request'
94
+ uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
95
+ with:
96
+ fail_ci_if_error: false
97
+ files: coverage.xml
90
98
  - name: ℹ️ Skip Codecov upload (missing token)
91
- if: env.HAS_CODECOV_TOKEN != 'true'
99
+ if: env.HAS_CODECOV_TOKEN != 'true' && github.event_name != 'pull_request'
92
100
  run: echo "CODECOV_TOKEN is not set; skipping Codecov upload."
93
101
  - name: SonarQube Cloud Scan
94
102
  if: env.HAS_SONAR_TOKEN == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork)
95
103
  # yamllint disable-line rule:line-length
96
- uses: SonarSource/sonarqube-scan-action@59db25f34e16620e48ab4bb9e4a5dce155cb5432 # v8.0
104
+ uses: SonarSource/sonarqube-scan-action@7006c4492b2e0ee0f816d36501671557c97f5995 # v8.1
97
105
  env:
98
106
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
99
107
  SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
@@ -0,0 +1 @@
1
+ 24.16.0
@@ -0,0 +1,98 @@
1
+ # Agent Instructions for python-bsblan
2
+
3
+ This file is the canonical AI agent guide for this repository.
4
+
5
+ ## Scope
6
+
7
+ - Applies to the whole workspace.
8
+ - Prefer links to project docs instead of duplicating large policy text.
9
+
10
+ ## Environment
11
+
12
+ - Python 3.12+
13
+ - uv for Python dependency management
14
+ - Node.js (used by formatting hooks)
15
+
16
+ Setup commands:
17
+
18
+ ```bash
19
+ npm install
20
+ make setup
21
+ ```
22
+
23
+ ## Required Validation
24
+
25
+ Before finishing changes, run:
26
+
27
+ ```bash
28
+ uv run prek run --all-files
29
+ ```
30
+
31
+ Useful test commands:
32
+
33
+ ```bash
34
+ uv run pytest
35
+ uv run pytest --cov=src/bsblan --cov-report=term-missing
36
+ uv run pytest --no-cov tests/test_file.py -k test_name
37
+ ```
38
+
39
+ Quality gate expectations:
40
+
41
+ - Keep total coverage at 95%+.
42
+ - Keep patch coverage at 100% for modified lines.
43
+
44
+ ## Code Map
45
+
46
+ - `src/bsblan/bsblan.py`: main async client (`BSBLAN`), request handling, lazy loading
47
+ - `src/bsblan/constants.py`: parameter IDs and name mappings
48
+ - `src/bsblan/models.py`: response models (`pydantic`) and set-param models (`dataclass`)
49
+ - `src/bsblan/utility.py`: helpers and API validation
50
+ - `src/bsblan/exceptions.py`: library exceptions
51
+ - `tests/conftest.py`: shared pytest fixtures (`mock_bsblan`)
52
+
53
+ ## Project Conventions
54
+
55
+ - Use type hints on all functions.
56
+ - Keep line length <= 88 chars (Ruff).
57
+ - Response/state models use `pydantic.BaseModel`.
58
+ - Set-parameter payload models use `@dataclass`.
59
+ - Parameter names use `snake_case`.
60
+ - For set operations, send one `/JS` request per populated parameter.
61
+ - Treat API v3 as the supported baseline (do not reintroduce v1-only behavior).
62
+
63
+ Naming patterns to preserve:
64
+
65
+ - `legionella_function_*`
66
+ - `dhw_*`
67
+
68
+ ## Parameter Change Workflow
69
+
70
+ When adding or renaming parameters:
71
+
72
+ 1. Query real-device raw data with `examples/fetch_param.py`.
73
+ 2. Update mappings in `src/bsblan/constants.py`.
74
+ 3. Update response models in `src/bsblan/models.py`.
75
+ 4. If settable, update set dataclass and client setter logic.
76
+ 5. Add/adjust tests in `tests/`.
77
+ 6. Run full validation (`uv run prek run --all-files`).
78
+
79
+ If raw responses are incomplete or `data_type` is unknown, prefer
80
+ `EntityInfo[str] | None` and avoid guessing numeric types.
81
+
82
+ ## Common Pitfalls
83
+
84
+ - Partial pytest runs can fail coverage gates; use `--no-cov` for focused checks.
85
+ - Missing Node.js dependencies can break formatting hooks.
86
+ - Do not confuse comfort/protective/cooling bounds when selecting IDs.
87
+
88
+ ## Source-of-Truth Docs
89
+
90
+ - Contribution and development process: `.github/CONTRIBUTING.md`
91
+ - User-facing usage and setup: `README.md`
92
+ - Developer docs index: `docs/index.md`
93
+ - Getting started examples: `docs/getting-started.md`
94
+ - API reference: `docs/api/client.md`
95
+
96
+ ## Legacy Compatibility
97
+
98
+ - `.github/copilot-instructions.md` and `CLAUDE.md` should reference this file.
@@ -0,0 +1 @@
1
+ AGENTS.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-bsblan
3
- Version: 6.0.1
3
+ Version: 6.1.1
4
4
  Summary: Asynchronous Python client for BSBLAN API
5
5
  Project-URL: Homepage, https://github.com/liudger/python-bsblan
6
6
  Project-URL: Repository, https://github.com/liudger/python-bsblan
@@ -63,6 +63,43 @@ async def main() -> None:
63
63
  asyncio.run(main())
64
64
  ```
65
65
 
66
+ ## Temperature bounds
67
+
68
+ For heating comfort setpoint writes (`target_temperature`), `min_temp` maps to
69
+ the reduced setpoint (`712` for circuit 1, `1012` for circuit 2). The protective
70
+ setpoints (`714` and `1014`) are exposed separately and are not used as the
71
+ comfort setpoint lower bound. The upper heating bound is `716` for circuit 1 and
72
+ `1016` for circuit 2 when the device exposes those parameters.
73
+
74
+ ## Cooling setpoint support
75
+
76
+ Some BSB/LPB controllers expose a cooling comfort setpoint for each heating
77
+ circuit. The client maps BSB-LAN parameter `902` for circuit 1 and `1202` for
78
+ circuit 2 to `target_temperature_high`; the duplicate decimal parameters
79
+ `902.1` and `902.2` are not used.
80
+
81
+ When available, cooling setpoint validation uses `905`/`1205` (comfort setpoint
82
+ minimum) as the lower bound and `903`/`1203` (room temperature reduced setpoint)
83
+ as the upper bound. Parameters `908` and `1208` are flow setpoints and are not
84
+ used for room setpoint validation.
85
+
86
+ Cooling support is optional. During section validation, unsupported parameters
87
+ are removed from the active API map, so integrations can detect support by
88
+ checking whether `state.target_temperature_high` is present.
89
+
90
+ ```python
91
+ async with BSBLAN(config) as client:
92
+ state = await client.state(include=["target_temperature_high"])
93
+
94
+ if state.target_temperature_high is not None:
95
+ print(f"Cooling setpoint: {state.target_temperature_high.value}")
96
+ await client.thermostat(target_temperature_high="24.0")
97
+ ```
98
+
99
+ BSB-LAN writes one parameter at a time. If an application exposes a heat/cool
100
+ temperature range, write `target_temperature` and `target_temperature_high` with
101
+ separate `thermostat()` calls.
102
+
66
103
  ## PPS bus support
67
104
 
68
105
  PPS bus devices are detected from the device metadata returned by BSB-LAN. The
@@ -7,8 +7,9 @@ Asynchronous Python client for [BSB-LAN](https://github.com/fredlcore/bsb_lan) d
7
7
  - Async/await support using `aiohttp`
8
8
  - Read heating state, sensor data, and device information
9
9
  - Control thermostat settings and hot water parameters
10
+ - Detect optional cooling setpoints for heat/cool range controls
10
11
  - Fully typed with PEP 561 support
11
- - Automatic API version detection (v1/v3)
12
+ - API v3 parameter support
12
13
  - Lazy loading with per-section validation
13
14
 
14
15
  ## Quick example