python-bsblan 3.1.4__tar.gz → 3.1.5__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-3.1.4 → python_bsblan-3.1.5}/.github/LICENSE.md +1 -1
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/renovate.json +12 -3
- python_bsblan-3.1.5/.github/workflows/auto-approve-renovate.yml +24 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/workflows/codeql.yaml +2 -2
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/workflows/linting.yaml +8 -6
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/workflows/lock.yaml +1 -1
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/workflows/release.yaml +1 -1
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/workflows/tests.yaml +7 -5
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/workflows/typing.yaml +9 -5
- python_bsblan-3.1.5/.nvmrc +1 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.pre-commit-config.yaml +3 -3
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/PKG-INFO +6 -8
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/README.md +5 -7
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/examples/control.py +60 -1
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/pyproject.toml +9 -9
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/src/bsblan/__init__.py +16 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/src/bsblan/constants.py +95 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/src/bsblan/utility.py +5 -7
- python_bsblan-3.1.5/tests/test_constants.py +311 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_thermostat.py +13 -3
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/uv.lock +308 -379
- python_bsblan-3.1.4/.github/workflows/auto-approve-renovate.yml +0 -20
- python_bsblan-3.1.4/.nvmrc +0 -1
- python_bsblan-3.1.4/tests/test_constants.py +0 -152
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.editorconfig +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.gitattributes +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/CODE_OF_CONDUCT.md +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/CONTRIBUTING.md +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/ISSUE_TEMPLATE/PULL_REQUEST_TEMPLATE.md +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/copilot-instructions.md +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/labels.yml +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/release-drafter.yml +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/workflows/labels.yaml +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/workflows/pr-labels.yaml +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/workflows/release-drafter.yaml +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.github/workflows/stale.yaml +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.gitignore +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.prettierignore +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/.yamllint +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/AGENTS.md +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/CLAUDE.md +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/examples/ruff.toml +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/package-lock.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/package.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/sonar-project.properties +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/src/bsblan/bsblan.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/src/bsblan/exceptions.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/src/bsblan/models.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/src/bsblan/py.typed +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/__init__.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/conftest.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/fixtures/device.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/fixtures/dict_version.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/fixtures/hot_water_state.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/fixtures/info.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/fixtures/password.txt +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/fixtures/sensor.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/fixtures/state.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/fixtures/static_state.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/fixtures/thermostat_hvac.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/fixtures/thermostat_temp.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/fixtures/time.json +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/ruff.toml +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_api_initialization.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_api_validation.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_auth.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_backoff_retry.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_bsblan.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_bsblan_edge_cases.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_configuration.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_context_manager.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_device.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_dhw_time_switch.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_entity_info.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_hot_water_additional.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_hotwater_state.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_info.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_initialization.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_reset_validation.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_schedule_models.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_sensor.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_set_hot_water_schedule.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_set_hotwater.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_state.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_static_state.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_temperature_unit.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_temperature_validation.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_time.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_utility.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_utility_additional.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_utility_edge_cases.py +0 -0
- {python_bsblan-3.1.4 → python_bsblan-3.1.5}/tests/test_version_errors.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2023-
|
|
3
|
+
Copyright (c) 2023-2026 Willem-Jan L. van Rootselaar
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"rebaseWhen": "behind-base-branch",
|
|
5
5
|
"dependencyDashboard": true,
|
|
6
6
|
"labels": ["dependencies", "no-stale"],
|
|
7
|
+
"platformAutomerge": true,
|
|
7
8
|
"lockFileMaintenance": {
|
|
8
9
|
"enabled": true,
|
|
9
10
|
"automerge": true
|
|
@@ -11,16 +12,17 @@
|
|
|
11
12
|
"commitMessagePrefix": "⬆️",
|
|
12
13
|
"packageRules": [
|
|
13
14
|
{
|
|
14
|
-
"matchManagers": ["
|
|
15
|
+
"matchManagers": ["uv"],
|
|
15
16
|
"addLabels": ["python"]
|
|
16
17
|
},
|
|
17
18
|
{
|
|
18
|
-
"matchManagers": ["
|
|
19
|
+
"matchManagers": ["uv"],
|
|
19
20
|
"matchDepTypes": ["dev"],
|
|
20
21
|
"rangeStrategy": "pin"
|
|
21
22
|
},
|
|
22
23
|
{
|
|
23
|
-
"
|
|
24
|
+
"description": "Automerge minor/patch Python updates (after CI passes)",
|
|
25
|
+
"matchManagers": ["uv"],
|
|
24
26
|
"matchUpdateTypes": ["minor", "patch"],
|
|
25
27
|
"automerge": true
|
|
26
28
|
},
|
|
@@ -40,9 +42,16 @@
|
|
|
40
42
|
"rangeStrategy": "pin"
|
|
41
43
|
},
|
|
42
44
|
{
|
|
45
|
+
"description": "Automerge minor/patch GitHub Actions updates (after CI passes)",
|
|
43
46
|
"matchManagers": ["github-actions"],
|
|
44
47
|
"matchUpdateTypes": ["minor", "patch"],
|
|
45
48
|
"automerge": true
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"description": "Don't automerge major GitHub Actions updates",
|
|
52
|
+
"matchManagers": ["github-actions"],
|
|
53
|
+
"matchUpdateTypes": ["major"],
|
|
54
|
+
"automerge": false
|
|
46
55
|
}
|
|
47
56
|
]
|
|
48
57
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Auto-approve Renovate PRs
|
|
3
|
+
|
|
4
|
+
# yamllint disable-line rule:truthy
|
|
5
|
+
on:
|
|
6
|
+
pull_request_target:
|
|
7
|
+
types:
|
|
8
|
+
- opened
|
|
9
|
+
- synchronize
|
|
10
|
+
- reopened
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
pull-requests: write
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
auto-approve:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
if: >-
|
|
19
|
+
github.actor == 'renovate[bot]' &&
|
|
20
|
+
github.actor_id == '29139614' &&
|
|
21
|
+
github.event.pull_request.head.repo.full_name == github.repository
|
|
22
|
+
steps:
|
|
23
|
+
# yamllint disable-line rule:line-length
|
|
24
|
+
- uses: hmarr/auto-approve-action@f0939ea97e9205ef24d872e76833fa908a770363 # v4.0.0
|
|
@@ -19,6 +19,6 @@ jobs:
|
|
|
19
19
|
- name: ⤵️ Check out code from GitHub
|
|
20
20
|
uses: actions/checkout@v6.0.1
|
|
21
21
|
- name: 🏗 Initialize CodeQL
|
|
22
|
-
uses: github/codeql-action/init@v3.31.
|
|
22
|
+
uses: github/codeql-action/init@v3.31.9
|
|
23
23
|
- name: 🚀 Perform CodeQL Analysis
|
|
24
|
-
uses: github/codeql-action/analyze@v3.31.
|
|
24
|
+
uses: github/codeql-action/analyze@v3.31.9
|
|
@@ -4,7 +4,9 @@ name: Linting
|
|
|
4
4
|
# yamllint disable-line rule:truthy
|
|
5
5
|
on:
|
|
6
6
|
push:
|
|
7
|
+
branches: [main]
|
|
7
8
|
pull_request:
|
|
9
|
+
branches: [main]
|
|
8
10
|
workflow_dispatch:
|
|
9
11
|
|
|
10
12
|
env:
|
|
@@ -18,7 +20,7 @@ jobs:
|
|
|
18
20
|
- name: ⤵️ Check out code from GitHub
|
|
19
21
|
uses: actions/checkout@v6.0.1
|
|
20
22
|
- name: 🏗 Set up uv
|
|
21
|
-
uses: astral-sh/setup-uv@
|
|
23
|
+
uses: astral-sh/setup-uv@v7
|
|
22
24
|
with:
|
|
23
25
|
enable-cache: true
|
|
24
26
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -38,7 +40,7 @@ jobs:
|
|
|
38
40
|
- name: ⤵️ Check out code from GitHub
|
|
39
41
|
uses: actions/checkout@v6.0.1
|
|
40
42
|
- name: 🏗 Set up uv
|
|
41
|
-
uses: astral-sh/setup-uv@
|
|
43
|
+
uses: astral-sh/setup-uv@v7
|
|
42
44
|
with:
|
|
43
45
|
enable-cache: true
|
|
44
46
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -60,7 +62,7 @@ jobs:
|
|
|
60
62
|
- name: ⤵️ Check out code from GitHub
|
|
61
63
|
uses: actions/checkout@v6.0.1
|
|
62
64
|
- name: 🏗 Set up uv
|
|
63
|
-
uses: astral-sh/setup-uv@
|
|
65
|
+
uses: astral-sh/setup-uv@v7
|
|
64
66
|
with:
|
|
65
67
|
enable-cache: true
|
|
66
68
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -104,7 +106,7 @@ jobs:
|
|
|
104
106
|
- name: ⤵️ Check out code from GitHub
|
|
105
107
|
uses: actions/checkout@v6.0.1
|
|
106
108
|
- name: 🏗 Set up uv
|
|
107
|
-
uses: astral-sh/setup-uv@
|
|
109
|
+
uses: astral-sh/setup-uv@v7
|
|
108
110
|
with:
|
|
109
111
|
enable-cache: true
|
|
110
112
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -124,7 +126,7 @@ jobs:
|
|
|
124
126
|
- name: ⤵️ Check out code from GitHub
|
|
125
127
|
uses: actions/checkout@v6.0.1
|
|
126
128
|
- name: 🏗 Set up uv
|
|
127
|
-
uses: astral-sh/setup-uv@
|
|
129
|
+
uses: astral-sh/setup-uv@v7
|
|
128
130
|
with:
|
|
129
131
|
enable-cache: true
|
|
130
132
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -144,7 +146,7 @@ jobs:
|
|
|
144
146
|
- name: ⤵️ Check out code from GitHub
|
|
145
147
|
uses: actions/checkout@v6.0.1
|
|
146
148
|
- name: 🏗 Set up uv
|
|
147
|
-
uses: astral-sh/setup-uv@
|
|
149
|
+
uses: astral-sh/setup-uv@v7
|
|
148
150
|
with:
|
|
149
151
|
enable-cache: true
|
|
150
152
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -4,7 +4,9 @@ name: Testing
|
|
|
4
4
|
# yamllint disable-line rule:truthy
|
|
5
5
|
on:
|
|
6
6
|
push:
|
|
7
|
+
branches: [main]
|
|
7
8
|
pull_request:
|
|
9
|
+
branches: [main]
|
|
8
10
|
workflow_dispatch:
|
|
9
11
|
|
|
10
12
|
env:
|
|
@@ -21,7 +23,7 @@ jobs:
|
|
|
21
23
|
- name: ⤵️ Check out code from GitHub
|
|
22
24
|
uses: actions/checkout@v6.0.1
|
|
23
25
|
- name: 🏗 Set up uv
|
|
24
|
-
uses: astral-sh/setup-uv@
|
|
26
|
+
uses: astral-sh/setup-uv@v7
|
|
25
27
|
with:
|
|
26
28
|
enable-cache: true
|
|
27
29
|
- name: 🏗 Set up Python ${{ matrix.python }}
|
|
@@ -34,7 +36,7 @@ jobs:
|
|
|
34
36
|
- name: 🚀 Run pytest
|
|
35
37
|
run: uv run pytest --cov=bsblan tests
|
|
36
38
|
- name: ⬆️ Upload coverage artifact
|
|
37
|
-
uses: actions/upload-artifact@
|
|
39
|
+
uses: actions/upload-artifact@v6.0.0
|
|
38
40
|
with:
|
|
39
41
|
name: coverage-${{ matrix.python }}
|
|
40
42
|
include-hidden-files: true
|
|
@@ -49,9 +51,9 @@ jobs:
|
|
|
49
51
|
with:
|
|
50
52
|
fetch-depth: 0
|
|
51
53
|
- name: ⬇️ Download coverage data
|
|
52
|
-
uses: actions/download-artifact@
|
|
54
|
+
uses: actions/download-artifact@v7.0.0
|
|
53
55
|
- name: 🏗 Set up uv
|
|
54
|
-
uses: astral-sh/setup-uv@
|
|
56
|
+
uses: astral-sh/setup-uv@v7
|
|
55
57
|
with:
|
|
56
58
|
enable-cache: true
|
|
57
59
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -66,7 +68,7 @@ jobs:
|
|
|
66
68
|
uv run coverage combine coverage*/.coverage*
|
|
67
69
|
uv run coverage xml -i
|
|
68
70
|
- name: 🚀 Upload coverage report
|
|
69
|
-
uses: codecov/codecov-action@v5.5.
|
|
71
|
+
uses: codecov/codecov-action@v5.5.2
|
|
70
72
|
with:
|
|
71
73
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
72
74
|
- name: SonarCloud Scan
|
|
@@ -4,21 +4,25 @@ name: Typing
|
|
|
4
4
|
# yamllint disable-line rule:truthy
|
|
5
5
|
on:
|
|
6
6
|
push:
|
|
7
|
+
branches: [main]
|
|
7
8
|
pull_request:
|
|
9
|
+
branches: [main]
|
|
8
10
|
workflow_dispatch:
|
|
9
11
|
|
|
10
12
|
env:
|
|
11
13
|
DEFAULT_PYTHON: "3.13"
|
|
12
14
|
|
|
13
15
|
jobs:
|
|
14
|
-
|
|
15
|
-
name:
|
|
16
|
+
ty:
|
|
17
|
+
name: ty
|
|
16
18
|
runs-on: ubuntu-latest
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
17
21
|
steps:
|
|
18
22
|
- name: ⤵️ Check out code from GitHub
|
|
19
23
|
uses: actions/checkout@v6.0.1
|
|
20
24
|
- name: 🏗 Set up uv
|
|
21
|
-
uses: astral-sh/setup-uv@
|
|
25
|
+
uses: astral-sh/setup-uv@v7
|
|
22
26
|
with:
|
|
23
27
|
enable-cache: true
|
|
24
28
|
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
@@ -28,5 +32,5 @@ jobs:
|
|
|
28
32
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
29
33
|
- name: 🏗 Install dependencies
|
|
30
34
|
run: uv sync --dev
|
|
31
|
-
- name: 🚀 Run
|
|
32
|
-
run: uv run
|
|
35
|
+
- name: 🚀 Run ty
|
|
36
|
+
run: uv run ty check examples src tests
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24.12.0
|
|
@@ -83,11 +83,11 @@ repos:
|
|
|
83
83
|
types: [text]
|
|
84
84
|
entry: uv run end-of-file-fixer
|
|
85
85
|
stages: [pre-commit, pre-push, manual]
|
|
86
|
-
- id:
|
|
87
|
-
name: 🆎 Static type checking using
|
|
86
|
+
- id: ty
|
|
87
|
+
name: 🆎 Static type checking using ty
|
|
88
88
|
language: system
|
|
89
89
|
types: [python]
|
|
90
|
-
entry: uv run
|
|
90
|
+
entry: uv run ty check
|
|
91
91
|
require_serial: true
|
|
92
92
|
- id: no-commit-to-branch
|
|
93
93
|
name: 🛑 Don't commit to main branch
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-bsblan
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.5
|
|
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
|
|
@@ -48,7 +48,7 @@ Asynchronous Python client for BSBLan.
|
|
|
48
48
|
|
|
49
49
|
## About
|
|
50
50
|
|
|
51
|
-
This package allows you to control and monitor
|
|
51
|
+
This package allows you to control and monitor a BSBLan device
|
|
52
52
|
programmatically. It is mainly created to allow third-party programs to automate
|
|
53
53
|
the behavior of [BSBLan][bsblanmodule].
|
|
54
54
|
|
|
@@ -121,9 +121,9 @@ async def main() -> None:
|
|
|
121
121
|
print("\nSetting temperature to 18°C")
|
|
122
122
|
await bsblan.thermostat(target_temperature="18")
|
|
123
123
|
|
|
124
|
-
# Set HVAC mode
|
|
124
|
+
# Set HVAC mode (using raw integer: 0=off, 1=auto, 2=eco, 3=heat)
|
|
125
125
|
print("Setting HVAC mode to heat")
|
|
126
|
-
await bsblan.thermostat(hvac_mode=
|
|
126
|
+
await bsblan.thermostat(hvac_mode=3)
|
|
127
127
|
|
|
128
128
|
# Get and print sensor information
|
|
129
129
|
sensor: Sensor = await bsblan.sensor()
|
|
@@ -211,7 +211,7 @@ check [the contributor's page][contributors].
|
|
|
211
211
|
|
|
212
212
|
MIT License
|
|
213
213
|
|
|
214
|
-
Copyright (c) 2023-
|
|
214
|
+
Copyright (c) 2023-2026 WJ van Rootselaar
|
|
215
215
|
|
|
216
216
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
217
217
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -243,9 +243,7 @@ SOFTWARE.
|
|
|
243
243
|
[keepchangelog]: http://keepachangelog.com/en/1.0.0/
|
|
244
244
|
[license-shield]: https://img.shields.io/badge/license-MIT-blue.svg
|
|
245
245
|
[liudger]: https://github.com/liudger
|
|
246
|
-
[maintenance-shield]: https://img.shields.io/maintenance/yes/
|
|
247
|
-
[poetry]: https://python-poetry.org
|
|
248
|
-
[poetry-install]: https://python-poetry.org/docs/#installation
|
|
246
|
+
[maintenance-shield]: https://img.shields.io/maintenance/yes/2026.svg
|
|
249
247
|
[uv]: https://docs.astral.sh/uv/
|
|
250
248
|
[uv-install]: https://docs.astral.sh/uv/getting-started/installation/
|
|
251
249
|
[pre-commit]: https://pre-commit.com/
|
|
@@ -16,7 +16,7 @@ Asynchronous Python client for BSBLan.
|
|
|
16
16
|
|
|
17
17
|
## About
|
|
18
18
|
|
|
19
|
-
This package allows you to control and monitor
|
|
19
|
+
This package allows you to control and monitor a BSBLan device
|
|
20
20
|
programmatically. It is mainly created to allow third-party programs to automate
|
|
21
21
|
the behavior of [BSBLan][bsblanmodule].
|
|
22
22
|
|
|
@@ -89,9 +89,9 @@ async def main() -> None:
|
|
|
89
89
|
print("\nSetting temperature to 18°C")
|
|
90
90
|
await bsblan.thermostat(target_temperature="18")
|
|
91
91
|
|
|
92
|
-
# Set HVAC mode
|
|
92
|
+
# Set HVAC mode (using raw integer: 0=off, 1=auto, 2=eco, 3=heat)
|
|
93
93
|
print("Setting HVAC mode to heat")
|
|
94
|
-
await bsblan.thermostat(hvac_mode=
|
|
94
|
+
await bsblan.thermostat(hvac_mode=3)
|
|
95
95
|
|
|
96
96
|
# Get and print sensor information
|
|
97
97
|
sensor: Sensor = await bsblan.sensor()
|
|
@@ -179,7 +179,7 @@ check [the contributor's page][contributors].
|
|
|
179
179
|
|
|
180
180
|
MIT License
|
|
181
181
|
|
|
182
|
-
Copyright (c) 2023-
|
|
182
|
+
Copyright (c) 2023-2026 WJ van Rootselaar
|
|
183
183
|
|
|
184
184
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
185
185
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -211,9 +211,7 @@ SOFTWARE.
|
|
|
211
211
|
[keepchangelog]: http://keepachangelog.com/en/1.0.0/
|
|
212
212
|
[license-shield]: https://img.shields.io/badge/license-MIT-blue.svg
|
|
213
213
|
[liudger]: https://github.com/liudger
|
|
214
|
-
[maintenance-shield]: https://img.shields.io/maintenance/yes/
|
|
215
|
-
[poetry]: https://python-poetry.org
|
|
216
|
-
[poetry-install]: https://python-poetry.org/docs/#installation
|
|
214
|
+
[maintenance-shield]: https://img.shields.io/maintenance/yes/2026.svg
|
|
217
215
|
[uv]: https://docs.astral.sh/uv/
|
|
218
216
|
[uv-install]: https://docs.astral.sh/uv/getting-started/installation/
|
|
219
217
|
[pre-commit]: https://pre-commit.com/
|
|
@@ -18,6 +18,13 @@ from typing import Any
|
|
|
18
18
|
|
|
19
19
|
from bsblan import (
|
|
20
20
|
BSBLAN,
|
|
21
|
+
BSBLAN_HVAC_ACTION_COOLING,
|
|
22
|
+
BSBLAN_HVAC_ACTION_DEFROSTING,
|
|
23
|
+
BSBLAN_HVAC_ACTION_DRYING,
|
|
24
|
+
BSBLAN_HVAC_ACTION_FAN,
|
|
25
|
+
BSBLAN_HVAC_ACTION_HEATING,
|
|
26
|
+
BSBLAN_HVAC_ACTION_OFF,
|
|
27
|
+
BSBLAN_HVAC_ACTION_PREHEATING,
|
|
21
28
|
BSBLANConfig,
|
|
22
29
|
Device,
|
|
23
30
|
DeviceTime,
|
|
@@ -66,6 +73,44 @@ def print_attributes(title: str, attributes: dict[str, str]) -> None:
|
|
|
66
73
|
print(f"{label}: {value}")
|
|
67
74
|
|
|
68
75
|
|
|
76
|
+
def get_hvac_action_name(status_code: int) -> str:
|
|
77
|
+
"""Map BSB-LAN parameter 8000 status code to a human-readable HVAC action.
|
|
78
|
+
|
|
79
|
+
BSB-LAN parameter 8000 ("Status heating circuit 1") returns vendor-specific
|
|
80
|
+
status codes. This function maps those codes to simplified HVAC action states
|
|
81
|
+
compatible with Home Assistant and other automation systems.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
status_code: The raw status code from parameter 8000 (hvac_action.value).
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
str: Human-readable HVAC action name. Returns "idle" for unmapped codes.
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
>>> state = await bsblan.state()
|
|
91
|
+
>>> if state.hvac_action is not None:
|
|
92
|
+
... action = get_hvac_action_name(state.hvac_action.value)
|
|
93
|
+
... print(f"Current HVAC action: {action}")
|
|
94
|
+
|
|
95
|
+
"""
|
|
96
|
+
# Map status code sets to action names
|
|
97
|
+
action_mappings: list[tuple[set[int], str]] = [
|
|
98
|
+
(BSBLAN_HVAC_ACTION_HEATING, "heating"),
|
|
99
|
+
(BSBLAN_HVAC_ACTION_COOLING, "cooling"),
|
|
100
|
+
(BSBLAN_HVAC_ACTION_PREHEATING, "preheating"),
|
|
101
|
+
(BSBLAN_HVAC_ACTION_DEFROSTING, "defrosting"),
|
|
102
|
+
(BSBLAN_HVAC_ACTION_DRYING, "drying"),
|
|
103
|
+
(BSBLAN_HVAC_ACTION_FAN, "fan"),
|
|
104
|
+
(BSBLAN_HVAC_ACTION_OFF, "off"),
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
for action_set, action_name in action_mappings:
|
|
108
|
+
if status_code in action_set:
|
|
109
|
+
return action_name
|
|
110
|
+
|
|
111
|
+
return "idle"
|
|
112
|
+
|
|
113
|
+
|
|
69
114
|
async def print_state(state: State) -> None:
|
|
70
115
|
"""Print the current state of the BSBLan device.
|
|
71
116
|
|
|
@@ -73,8 +118,22 @@ async def print_state(state: State) -> None:
|
|
|
73
118
|
state (State): The current state of the BSBLan device.
|
|
74
119
|
|
|
75
120
|
"""
|
|
121
|
+
# Get the HVAC action - both the raw value and mapped action name
|
|
122
|
+
hvac_action_desc = await get_attribute(state.hvac_action, "desc", "Unknown Action")
|
|
123
|
+
hvac_action_value = await get_attribute(state.hvac_action, "value", "N/A")
|
|
124
|
+
|
|
125
|
+
# Map the raw status code to a simplified action name
|
|
126
|
+
hvac_action_mapped = "N/A"
|
|
127
|
+
if hvac_action_value != "N/A":
|
|
128
|
+
try:
|
|
129
|
+
hvac_action_mapped = get_hvac_action_name(int(hvac_action_value))
|
|
130
|
+
except (ValueError, TypeError):
|
|
131
|
+
hvac_action_mapped = "unknown"
|
|
132
|
+
|
|
76
133
|
attributes = {
|
|
77
|
-
"HVAC Action
|
|
134
|
+
"HVAC Action (raw value)": str(hvac_action_value),
|
|
135
|
+
"HVAC Action (device desc)": hvac_action_desc,
|
|
136
|
+
"HVAC Action (mapped)": hvac_action_mapped,
|
|
78
137
|
"HVAC Mode": await get_attribute(state.hvac_mode, "desc", "Unknown Mode"),
|
|
79
138
|
"Current Temperature": await get_attribute(
|
|
80
139
|
state.current_temperature, "value", "N/A"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "python-bsblan"
|
|
3
|
-
version = "3.1.
|
|
3
|
+
version = "3.1.5"
|
|
4
4
|
description = "Asynchronous Python client for BSBLAN API"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Willem-Jan van Rootselaar", email = "liudgervr@gmail.com"}
|
|
@@ -178,27 +178,27 @@ build-backend = "hatchling.build"
|
|
|
178
178
|
dev = [
|
|
179
179
|
"aresponses==3.0.0",
|
|
180
180
|
"bandit==1.9.2",
|
|
181
|
-
"black==25.
|
|
181
|
+
"black==25.12.0",
|
|
182
182
|
"blacken-docs==1.20.0",
|
|
183
183
|
"codespell==2.4.1",
|
|
184
184
|
"covdefaults==2.3.0",
|
|
185
|
-
"coverage==7.
|
|
185
|
+
"coverage==7.13.1",
|
|
186
186
|
"darglint==1.8.1",
|
|
187
187
|
"flake8==7.3.0",
|
|
188
|
-
"flake8-simplify==0.
|
|
188
|
+
"flake8-simplify==0.30.0",
|
|
189
189
|
# hatch is required to support type hinting and proper packaging of the py.typed file.
|
|
190
190
|
"hatch>=1.14.1",
|
|
191
|
-
"isort==
|
|
192
|
-
"
|
|
193
|
-
"pre-commit==4.5.
|
|
191
|
+
"isort==7.0.0",
|
|
192
|
+
"ty==0.0.8",
|
|
193
|
+
"pre-commit==4.5.1",
|
|
194
194
|
"pre-commit-hooks==6.0.0",
|
|
195
|
-
"pylint==
|
|
195
|
+
"pylint==4.0.4",
|
|
196
196
|
"pytest>=8.3.5",
|
|
197
197
|
"pytest-asyncio==1.3.0",
|
|
198
198
|
"pytest-cov==7.0.0",
|
|
199
199
|
"pytest-xdist>=3.8.0",
|
|
200
200
|
"pyupgrade==3.21.2",
|
|
201
|
-
"ruff==0.14.
|
|
201
|
+
"ruff==0.14.10",
|
|
202
202
|
"safety==3.7.0",
|
|
203
203
|
"vulture==2.14",
|
|
204
204
|
"yamllint==1.37.1",
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
"""Asynchronous Python client for BSBLAN."""
|
|
2
2
|
|
|
3
3
|
from .bsblan import BSBLAN, BSBLANConfig
|
|
4
|
+
from .constants import (
|
|
5
|
+
BSBLAN_HVAC_ACTION_COOLING,
|
|
6
|
+
BSBLAN_HVAC_ACTION_DEFROSTING,
|
|
7
|
+
BSBLAN_HVAC_ACTION_DRYING,
|
|
8
|
+
BSBLAN_HVAC_ACTION_FAN,
|
|
9
|
+
BSBLAN_HVAC_ACTION_HEATING,
|
|
10
|
+
BSBLAN_HVAC_ACTION_OFF,
|
|
11
|
+
BSBLAN_HVAC_ACTION_PREHEATING,
|
|
12
|
+
)
|
|
4
13
|
from .exceptions import BSBLANAuthError, BSBLANConnectionError, BSBLANError
|
|
5
14
|
from .models import (
|
|
6
15
|
DaySchedule,
|
|
@@ -22,6 +31,13 @@ from .models import (
|
|
|
22
31
|
|
|
23
32
|
__all__ = [
|
|
24
33
|
"BSBLAN",
|
|
34
|
+
"BSBLAN_HVAC_ACTION_COOLING",
|
|
35
|
+
"BSBLAN_HVAC_ACTION_DEFROSTING",
|
|
36
|
+
"BSBLAN_HVAC_ACTION_DRYING",
|
|
37
|
+
"BSBLAN_HVAC_ACTION_FAN",
|
|
38
|
+
"BSBLAN_HVAC_ACTION_HEATING",
|
|
39
|
+
"BSBLAN_HVAC_ACTION_OFF",
|
|
40
|
+
"BSBLAN_HVAC_ACTION_PREHEATING",
|
|
25
41
|
"BSBLANAuthError",
|
|
26
42
|
"BSBLANConfig",
|
|
27
43
|
"BSBLANConnectionError",
|
|
@@ -131,6 +131,101 @@ API_VERSIONS: Final[dict[str, APIConfig]] = {
|
|
|
131
131
|
# Valid HVAC mode values for validation
|
|
132
132
|
VALID_HVAC_MODES: Final[set[int]] = {0, 1, 2, 3}
|
|
133
133
|
|
|
134
|
+
# Parameter 8000 ("Status heating circuit 1") yields many vendor specific status
|
|
135
|
+
# codes. These sets capture the most common codes we need for Home Assistant's
|
|
136
|
+
# simplified HVAC action states. Any unmapped code will fall back to IDLE.
|
|
137
|
+
# Status code definitions from BSB-LAN ENUM8000 (LANG_DE_LEGACY.h / LANG_EN.h)
|
|
138
|
+
BSBLAN_HVAC_ACTION_HEATING: Final[set[int]] = {
|
|
139
|
+
0x04, # Manual control active
|
|
140
|
+
0x11, # Overrun active
|
|
141
|
+
0x16, # Frost protection plant active
|
|
142
|
+
0x17, # Frost protection active
|
|
143
|
+
0x18, # (Reserved heating state)
|
|
144
|
+
0x38, # Overheat protection active
|
|
145
|
+
0x65, # Room frost protection active
|
|
146
|
+
0x67, # Limited, boiler protection
|
|
147
|
+
0x68, # Limited, DHW priority
|
|
148
|
+
0x69, # Limited, buffer
|
|
149
|
+
0x6A, # Heating operation limited
|
|
150
|
+
0x72, # Heating operation comfort
|
|
151
|
+
0x73, # Switch-off optimization
|
|
152
|
+
0x74, # Heating operation reduced
|
|
153
|
+
0x75, # Flow frost protection active
|
|
154
|
+
0x77, # Day eco active
|
|
155
|
+
0x78, # Setback to reduced
|
|
156
|
+
0x79, # Setback to frost protection
|
|
157
|
+
0x89, # (Reserved heating state)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
BSBLAN_HVAC_ACTION_PREHEATING: Final[set[int]] = {
|
|
161
|
+
0x6F, # Switch-on optimization + quick heat-up
|
|
162
|
+
0x70, # Switch-on optimization
|
|
163
|
+
0x71, # Quick heat-up
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
BSBLAN_HVAC_ACTION_DRYING: Final[set[int]] = {
|
|
167
|
+
0x66, # Screed function active (floor drying)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
BSBLAN_HVAC_ACTION_FAN: Final[set[int]] = {
|
|
171
|
+
0x6B, # Forced consumption buffer
|
|
172
|
+
0x6C, # Forced consumption DHW
|
|
173
|
+
0x6D, # Forced consumption generator
|
|
174
|
+
0x6E, # Forced consumption
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
BSBLAN_HVAC_ACTION_COOLING: Final[set[int]] = {
|
|
178
|
+
0x7F, # Active cooling mode
|
|
179
|
+
0x80, # Passive cooling mode
|
|
180
|
+
0x81, # Cooling down evaporator
|
|
181
|
+
0x84, # (Cooling related)
|
|
182
|
+
0x85, # Dew point monitor active
|
|
183
|
+
0x86, # Cooling limit outdoor temp active
|
|
184
|
+
0x88, # Cooling mode
|
|
185
|
+
0x90, # Cooling operation limited
|
|
186
|
+
0x91, # Cooling limit outside temp max active
|
|
187
|
+
0x94, # (Reserved cooling state)
|
|
188
|
+
0x95, # (Reserved cooling state)
|
|
189
|
+
0x96, # Cooling operation comfort
|
|
190
|
+
0xB1, # Limit flow min dew point
|
|
191
|
+
0xB2, # Limit flow min outdoor temp
|
|
192
|
+
0xB3, # Flow limit reached
|
|
193
|
+
0x8E, # Recooling via DHW/HC
|
|
194
|
+
0xC4, # Limit source temp min cooling
|
|
195
|
+
0xCF, # Compressor runtime min active, cooling
|
|
196
|
+
0xD0, # Compressor 1 and 2 on, cooling
|
|
197
|
+
0xD1, # Compressor 1 on, cooling
|
|
198
|
+
0xD2, # Compressor 2 on, cooling
|
|
199
|
+
0x11D, # Protection mode cooling
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
BSBLAN_HVAC_ACTION_OFF: Final[set[int]] = {
|
|
203
|
+
0x02, # Fault/error state
|
|
204
|
+
0x19, # (Off/standby state)
|
|
205
|
+
0x76, # Off
|
|
206
|
+
0x8C, # (Off/standby state)
|
|
207
|
+
0x8A, # Cooling mode off
|
|
208
|
+
0x92, # Cooling operation locked
|
|
209
|
+
0xA1, # Heating operation generator off
|
|
210
|
+
0xA2, # Heating operation off
|
|
211
|
+
0xCC, # Locked, heating mode
|
|
212
|
+
0xCD, # Locked, generator
|
|
213
|
+
0xCE, # Locked, buffer
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
BSBLAN_HVAC_ACTION_DEFROSTING: Final[set[int]] = {
|
|
217
|
+
0x7D, # Defrost active
|
|
218
|
+
0x7E, # Drip-off
|
|
219
|
+
0x82, # Pre-heating for defrost
|
|
220
|
+
0x83, # (Defrost related)
|
|
221
|
+
0xCA, # Frost protection cooling active
|
|
222
|
+
0xC0, # Forced defrost compressor
|
|
223
|
+
0xC1, # Forced defrost fan
|
|
224
|
+
0xC2, # Defrost with compressor
|
|
225
|
+
0xC3, # Defrost with fan
|
|
226
|
+
0x101, # (Defrost related)
|
|
227
|
+
}
|
|
228
|
+
|
|
134
229
|
# Error Messages
|
|
135
230
|
NO_STATE_ERROR_MSG: Final[str] = "No state provided."
|
|
136
231
|
NO_SCHEDULE_ERROR_MSG: Final[str] = "No schedule provided."
|
|
@@ -5,10 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import logging
|
|
6
6
|
import re
|
|
7
7
|
from dataclasses import dataclass, field
|
|
8
|
-
from typing import
|
|
9
|
-
|
|
10
|
-
if TYPE_CHECKING:
|
|
11
|
-
from constants import APIConfig
|
|
8
|
+
from typing import Any
|
|
12
9
|
|
|
13
10
|
logger = logging.getLogger(__name__)
|
|
14
11
|
|
|
@@ -76,7 +73,8 @@ def validate_time_format(
|
|
|
76
73
|
class APIValidator:
|
|
77
74
|
"""Validates and maintains BSB-LAN API configuration."""
|
|
78
75
|
|
|
79
|
-
|
|
76
|
+
# Flexible type for API data (accepts `APIConfig`, plain dicts, or None)
|
|
77
|
+
api_config: Any # intentionally permissive to support tests and dynamic data
|
|
80
78
|
validated_sections: set[str] = field(default_factory=set)
|
|
81
79
|
|
|
82
80
|
def validate_section(self, section: str, request_data: dict[str, Any]) -> None:
|
|
@@ -89,7 +87,7 @@ class APIValidator:
|
|
|
89
87
|
|
|
90
88
|
"""
|
|
91
89
|
# Check if the section exists in the APIConfig object
|
|
92
|
-
if section not in self.api_config:
|
|
90
|
+
if not self.api_config or section not in self.api_config:
|
|
93
91
|
logger.warning("Unknown section '%s' in API configuration", section)
|
|
94
92
|
return
|
|
95
93
|
|
|
@@ -141,7 +139,7 @@ class APIValidator:
|
|
|
141
139
|
|
|
142
140
|
def get_section_params(self, section: str) -> Any:
|
|
143
141
|
"""Get the parameter mapping for a section."""
|
|
144
|
-
return self.api_config.get(section, {}).copy()
|
|
142
|
+
return (self.api_config or {}).get(section, {}).copy()
|
|
145
143
|
|
|
146
144
|
def is_section_validated(self, section: str) -> bool:
|
|
147
145
|
"""Check if a section has been validated."""
|