python-bsblan 0.6.0__tar.gz → 0.6.2__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-0.6.0 → python_bsblan-0.6.2}/PKG-INFO +58 -23
- {python_bsblan-0.6.0 → python_bsblan-0.6.2}/README.md +57 -22
- {python_bsblan-0.6.0 → python_bsblan-0.6.2}/pyproject.toml +1 -1
- {python_bsblan-0.6.0 → python_bsblan-0.6.2}/src/bsblan/bsblan.py +28 -22
- {python_bsblan-0.6.0 → python_bsblan-0.6.2}/src/bsblan/__init__.py +0 -0
- {python_bsblan-0.6.0 → python_bsblan-0.6.2}/src/bsblan/constants.py +0 -0
- {python_bsblan-0.6.0 → python_bsblan-0.6.2}/src/bsblan/exceptions.py +0 -0
- {python_bsblan-0.6.0 → python_bsblan-0.6.2}/src/bsblan/models.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-bsblan
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Asynchronous Python client for BSBLAN
|
|
5
5
|
Home-page: https://github.com/liudger/python-bsblan
|
|
6
6
|
License: MIT
|
|
@@ -66,11 +66,37 @@ pip install python-bsblan
|
|
|
66
66
|
"""Asynchronous Python client for BSBLan."""
|
|
67
67
|
|
|
68
68
|
import asyncio
|
|
69
|
+
import os
|
|
69
70
|
|
|
70
|
-
from bsblan import
|
|
71
|
+
from bsblan import BSBLAN, BSBLANConfig, Device, Info, Sensor, State, StaticState
|
|
71
72
|
|
|
72
73
|
|
|
73
|
-
async def
|
|
74
|
+
async def print_state(state: State) -> None:
|
|
75
|
+
"""Print the current state of the BSBLan device."""
|
|
76
|
+
print(f"HVAC Action: {state.hvac_action.desc}")
|
|
77
|
+
print(f"HVAC Mode: {state.hvac_mode.desc}")
|
|
78
|
+
print(f"Current Temperature: {state.current_temperature.value}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def print_sensor(sensor: Sensor) -> None:
|
|
82
|
+
"""Print sensor information from the BSBLan device."""
|
|
83
|
+
print(f"Outside Temperature: {sensor.outside_temperature.value}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
async def print_device_info(device: Device, info: Info) -> None:
|
|
87
|
+
"""Print device and general information."""
|
|
88
|
+
print(f"Device Name: {device.name}")
|
|
89
|
+
print(f"Version: {device.version}")
|
|
90
|
+
print(f"Device Identification: {info.device_identification.value}")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def print_static_state(static_state: StaticState) -> None:
|
|
94
|
+
"""Print static state information."""
|
|
95
|
+
print(f"Min Temperature: {static_state.min_temp.value}")
|
|
96
|
+
print(f"Max Temperature: {static_state.max_temp.value}")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async def main() -> None:
|
|
74
100
|
"""Show example on controlling your BSBLan device.
|
|
75
101
|
|
|
76
102
|
Options:
|
|
@@ -78,35 +104,44 @@ async def main(loop):
|
|
|
78
104
|
- username and password if your device is setup for username/password authentication
|
|
79
105
|
|
|
80
106
|
"""
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
107
|
+
# Create a configuration object
|
|
108
|
+
config = BSBLANConfig(
|
|
109
|
+
host="10.0.2.60",
|
|
110
|
+
passkey=None,
|
|
111
|
+
username=os.getenv("USERNAME"), # Compliant
|
|
112
|
+
password=os.getenv("PASSWORD"), # Compliant
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Initialize BSBLAN with the configuration object
|
|
116
|
+
async with BSBLAN(config) as bsblan:
|
|
117
|
+
# Get and print state
|
|
85
118
|
state: State = await bsblan.state()
|
|
86
|
-
|
|
119
|
+
await print_state(state)
|
|
87
120
|
|
|
88
|
-
#
|
|
89
|
-
|
|
121
|
+
# Set thermostat temperature
|
|
122
|
+
print("\nSetting temperature to 18°C")
|
|
123
|
+
await bsblan.thermostat(target_temperature="18")
|
|
90
124
|
|
|
91
|
-
#
|
|
92
|
-
|
|
125
|
+
# Set HVAC mode
|
|
126
|
+
print("Setting HVAC mode to heat")
|
|
127
|
+
await bsblan.thermostat(hvac_mode="heat")
|
|
93
128
|
|
|
94
|
-
#
|
|
95
|
-
|
|
96
|
-
|
|
129
|
+
# Get and print sensor information
|
|
130
|
+
sensor: Sensor = await bsblan.sensor()
|
|
131
|
+
await print_sensor(sensor)
|
|
97
132
|
|
|
98
|
-
#
|
|
133
|
+
# Get and print device and general info
|
|
99
134
|
device: Device = await bsblan.device()
|
|
100
|
-
|
|
135
|
+
info: Info = await bsblan.info()
|
|
136
|
+
await print_device_info(device, info)
|
|
101
137
|
|
|
102
|
-
#
|
|
103
|
-
|
|
104
|
-
|
|
138
|
+
# Get and print static state
|
|
139
|
+
static_state: StaticState = await bsblan.static_values()
|
|
140
|
+
await print_static_state(static_state)
|
|
105
141
|
|
|
106
142
|
|
|
107
143
|
if __name__ == "__main__":
|
|
108
|
-
|
|
109
|
-
loop.run_until_complete(main())
|
|
144
|
+
asyncio.run(main())
|
|
110
145
|
```
|
|
111
146
|
|
|
112
147
|
## Changelog & Releases
|
|
@@ -139,7 +174,7 @@ This Python project is fully managed using the [Poetry][poetry] dependency manag
|
|
|
139
174
|
|
|
140
175
|
You need at least:
|
|
141
176
|
|
|
142
|
-
- Python 3.
|
|
177
|
+
- Python 3.12+
|
|
143
178
|
- [Poetry][poetry-install]
|
|
144
179
|
- NodeJS 16+ (including NPM)
|
|
145
180
|
|
|
@@ -33,11 +33,37 @@ pip install python-bsblan
|
|
|
33
33
|
"""Asynchronous Python client for BSBLan."""
|
|
34
34
|
|
|
35
35
|
import asyncio
|
|
36
|
+
import os
|
|
36
37
|
|
|
37
|
-
from bsblan import
|
|
38
|
+
from bsblan import BSBLAN, BSBLANConfig, Device, Info, Sensor, State, StaticState
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
async def
|
|
41
|
+
async def print_state(state: State) -> None:
|
|
42
|
+
"""Print the current state of the BSBLan device."""
|
|
43
|
+
print(f"HVAC Action: {state.hvac_action.desc}")
|
|
44
|
+
print(f"HVAC Mode: {state.hvac_mode.desc}")
|
|
45
|
+
print(f"Current Temperature: {state.current_temperature.value}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def print_sensor(sensor: Sensor) -> None:
|
|
49
|
+
"""Print sensor information from the BSBLan device."""
|
|
50
|
+
print(f"Outside Temperature: {sensor.outside_temperature.value}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def print_device_info(device: Device, info: Info) -> None:
|
|
54
|
+
"""Print device and general information."""
|
|
55
|
+
print(f"Device Name: {device.name}")
|
|
56
|
+
print(f"Version: {device.version}")
|
|
57
|
+
print(f"Device Identification: {info.device_identification.value}")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
async def print_static_state(static_state: StaticState) -> None:
|
|
61
|
+
"""Print static state information."""
|
|
62
|
+
print(f"Min Temperature: {static_state.min_temp.value}")
|
|
63
|
+
print(f"Max Temperature: {static_state.max_temp.value}")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
async def main() -> None:
|
|
41
67
|
"""Show example on controlling your BSBLan device.
|
|
42
68
|
|
|
43
69
|
Options:
|
|
@@ -45,35 +71,44 @@ async def main(loop):
|
|
|
45
71
|
- username and password if your device is setup for username/password authentication
|
|
46
72
|
|
|
47
73
|
"""
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
74
|
+
# Create a configuration object
|
|
75
|
+
config = BSBLANConfig(
|
|
76
|
+
host="10.0.2.60",
|
|
77
|
+
passkey=None,
|
|
78
|
+
username=os.getenv("USERNAME"), # Compliant
|
|
79
|
+
password=os.getenv("PASSWORD"), # Compliant
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Initialize BSBLAN with the configuration object
|
|
83
|
+
async with BSBLAN(config) as bsblan:
|
|
84
|
+
# Get and print state
|
|
52
85
|
state: State = await bsblan.state()
|
|
53
|
-
|
|
86
|
+
await print_state(state)
|
|
54
87
|
|
|
55
|
-
#
|
|
56
|
-
|
|
88
|
+
# Set thermostat temperature
|
|
89
|
+
print("\nSetting temperature to 18°C")
|
|
90
|
+
await bsblan.thermostat(target_temperature="18")
|
|
57
91
|
|
|
58
|
-
#
|
|
59
|
-
|
|
92
|
+
# Set HVAC mode
|
|
93
|
+
print("Setting HVAC mode to heat")
|
|
94
|
+
await bsblan.thermostat(hvac_mode="heat")
|
|
60
95
|
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
|
|
96
|
+
# Get and print sensor information
|
|
97
|
+
sensor: Sensor = await bsblan.sensor()
|
|
98
|
+
await print_sensor(sensor)
|
|
64
99
|
|
|
65
|
-
#
|
|
100
|
+
# Get and print device and general info
|
|
66
101
|
device: Device = await bsblan.device()
|
|
67
|
-
|
|
102
|
+
info: Info = await bsblan.info()
|
|
103
|
+
await print_device_info(device, info)
|
|
68
104
|
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
|
|
105
|
+
# Get and print static state
|
|
106
|
+
static_state: StaticState = await bsblan.static_values()
|
|
107
|
+
await print_static_state(static_state)
|
|
72
108
|
|
|
73
109
|
|
|
74
110
|
if __name__ == "__main__":
|
|
75
|
-
|
|
76
|
-
loop.run_until_complete(main())
|
|
111
|
+
asyncio.run(main())
|
|
77
112
|
```
|
|
78
113
|
|
|
79
114
|
## Changelog & Releases
|
|
@@ -106,7 +141,7 @@ This Python project is fully managed using the [Poetry][poetry] dependency manag
|
|
|
106
141
|
|
|
107
142
|
You need at least:
|
|
108
143
|
|
|
109
|
-
- Python 3.
|
|
144
|
+
- Python 3.12+
|
|
110
145
|
- [Poetry][poetry-install]
|
|
111
146
|
- NodeJS 16+ (including NPM)
|
|
112
147
|
|
|
@@ -6,12 +6,12 @@ import asyncio
|
|
|
6
6
|
import logging
|
|
7
7
|
from asyncio.log import logger
|
|
8
8
|
from dataclasses import dataclass, field
|
|
9
|
-
from
|
|
10
|
-
from typing import TYPE_CHECKING, Any, Mapping, TypedDict, cast
|
|
9
|
+
from typing import Any, Mapping, TypedDict, cast
|
|
11
10
|
|
|
12
11
|
import aiohttp
|
|
13
12
|
from aiohttp.client import ClientSession
|
|
14
13
|
from aiohttp.hdrs import METH_POST
|
|
14
|
+
from aiohttp.helpers import BasicAuth
|
|
15
15
|
from packaging import version as pkg_version
|
|
16
16
|
from typing_extensions import Self
|
|
17
17
|
from yarl import URL
|
|
@@ -39,9 +39,6 @@ from .exceptions import (
|
|
|
39
39
|
)
|
|
40
40
|
from .models import Device, Info, Sensor, State, StaticState
|
|
41
41
|
|
|
42
|
-
if TYPE_CHECKING:
|
|
43
|
-
from aiohttp.helpers import BasicAuth
|
|
44
|
-
|
|
45
42
|
logging.basicConfig(level=logging.DEBUG)
|
|
46
43
|
|
|
47
44
|
|
|
@@ -61,7 +58,6 @@ class BSBLANConfig:
|
|
|
61
58
|
class BSBLAN:
|
|
62
59
|
"""Main class for handling connections with BSBLAN."""
|
|
63
60
|
|
|
64
|
-
_version: str = ""
|
|
65
61
|
_heating_params: list[str] | None = None
|
|
66
62
|
_string_circuit1: str | None = None
|
|
67
63
|
_sensor_params: list[str] | None = None
|
|
@@ -75,17 +71,30 @@ class BSBLAN:
|
|
|
75
71
|
_auth: BasicAuth | None = None
|
|
76
72
|
_close_session: bool = False
|
|
77
73
|
|
|
78
|
-
def __init__(
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
config: BSBLANConfig,
|
|
77
|
+
session: ClientSession | None = None,
|
|
78
|
+
) -> None:
|
|
79
79
|
"""Initialize the BSBLAN object.
|
|
80
80
|
|
|
81
81
|
Args:
|
|
82
82
|
----
|
|
83
83
|
config: Configuration for the BSBLAN object.
|
|
84
|
+
session: The aiohttp session to use for the connection.
|
|
84
85
|
|
|
85
86
|
"""
|
|
86
87
|
self.config = config
|
|
87
|
-
self.session
|
|
88
|
-
self._close_session =
|
|
88
|
+
self.session = session
|
|
89
|
+
self._close_session = session is None
|
|
90
|
+
self._firmware_version: str | None = None
|
|
91
|
+
|
|
92
|
+
async def _fetch_firmware_version(self) -> None:
|
|
93
|
+
"""Fetch the firmware version if not already available."""
|
|
94
|
+
if self._firmware_version is None:
|
|
95
|
+
device = await self.device()
|
|
96
|
+
self._firmware_version = device.version
|
|
97
|
+
logger.debug("BSBLAN version: %s", self._firmware_version)
|
|
89
98
|
|
|
90
99
|
async def __aenter__(self) -> Self:
|
|
91
100
|
"""Enter method for the context manager.
|
|
@@ -145,11 +154,6 @@ class BSBLAN:
|
|
|
145
154
|
response.
|
|
146
155
|
|
|
147
156
|
"""
|
|
148
|
-
try:
|
|
149
|
-
version = metadata.version(__package__ or __name__)
|
|
150
|
-
except metadata.PackageNotFoundError:
|
|
151
|
-
version = "0.0.0"
|
|
152
|
-
|
|
153
157
|
# retrieve passkey for custom url
|
|
154
158
|
if self.config.passkey:
|
|
155
159
|
base_path = f"/{self.config.passkey}{base_path}"
|
|
@@ -163,10 +167,10 @@ class BSBLAN:
|
|
|
163
167
|
|
|
164
168
|
auth = None
|
|
165
169
|
if self.config.username and self.config.password:
|
|
166
|
-
auth =
|
|
170
|
+
auth = BasicAuth(self.config.username, self.config.password)
|
|
167
171
|
|
|
168
172
|
headers = {
|
|
169
|
-
"User-Agent": f"PythonBSBLAN/{
|
|
173
|
+
"User-Agent": f"PythonBSBLAN/{self._firmware_version}",
|
|
170
174
|
"Accept": "application/json, */*",
|
|
171
175
|
}
|
|
172
176
|
|
|
@@ -265,18 +269,20 @@ class BSBLAN:
|
|
|
265
269
|
A dictionary with dicts
|
|
266
270
|
|
|
267
271
|
"""
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
272
|
+
await self._fetch_firmware_version()
|
|
273
|
+
|
|
274
|
+
if self._firmware_version is None:
|
|
275
|
+
msg = "Unable to fetch firmware version"
|
|
276
|
+
raise BSBLANError(msg)
|
|
277
|
+
|
|
278
|
+
if pkg_version.parse(self._firmware_version) < pkg_version.parse("1.2.0"):
|
|
273
279
|
return {
|
|
274
280
|
"heating": HEATING_CIRCUIT1_API_V1,
|
|
275
281
|
"staticValues": STATIC_VALUES_API_V1,
|
|
276
282
|
"device": DEVICE_INFO_API_V1,
|
|
277
283
|
"sensor": SENSORS_API_V1,
|
|
278
284
|
}
|
|
279
|
-
if pkg_version.parse(self.
|
|
285
|
+
if pkg_version.parse(self._firmware_version) > pkg_version.parse("3.0.0"):
|
|
280
286
|
return {
|
|
281
287
|
"heating": HEATING_CIRCUIT1_API_V3,
|
|
282
288
|
"staticValues": STATIC_VALUES_API_V3,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|