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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-bsblan
3
- Version: 0.6.0
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 BSBLan, Info, State
71
+ from bsblan import BSBLAN, BSBLANConfig, Device, Info, Sensor, State, StaticState
71
72
 
72
73
 
73
- async def main(loop):
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
- async with BSBLan(
82
- host="10.0.1.60", passkey=None, username=None, password=None, loop=loop
83
- ) as bsblan:
84
- # get state from bsblan device
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
- print(state)
119
+ await print_state(state)
87
120
 
88
- # set temp thermostat
89
- await bsblan.thermostat(target_temperature=19.0)
121
+ # Set thermostat temperature
122
+ print("\nSetting temperature to 18°C")
123
+ await bsblan.thermostat(target_temperature="18")
90
124
 
91
- # set hvac_mode (0-3) (protection,auto,reduced,comfort)
92
- await bsblan.thermostat(hvac_mode=3)
125
+ # Set HVAC mode
126
+ print("Setting HVAC mode to heat")
127
+ await bsblan.thermostat(hvac_mode="heat")
93
128
 
94
- # get some generic info from the heater
95
- info: Info = await bsblan.info()
96
- print(info)
129
+ # Get and print sensor information
130
+ sensor: Sensor = await bsblan.sensor()
131
+ await print_sensor(sensor)
97
132
 
98
- # get device info
133
+ # Get and print device and general info
99
134
  device: Device = await bsblan.device()
100
- print(device)
135
+ info: Info = await bsblan.info()
136
+ await print_device_info(device, info)
101
137
 
102
- # get sensor from bsblan device
103
- sensor: Sensor = await bsblan.sensor()
104
- print(f"outside temperature: {sensor.outside_temperature.value}")
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
- loop = asyncio.get_event_loop_policy().get_event_loop()
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.10+
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 BSBLan, Info, State
38
+ from bsblan import BSBLAN, BSBLANConfig, Device, Info, Sensor, State, StaticState
38
39
 
39
40
 
40
- async def main(loop):
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
- async with BSBLan(
49
- host="10.0.1.60", passkey=None, username=None, password=None, loop=loop
50
- ) as bsblan:
51
- # get state from bsblan device
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
- print(state)
86
+ await print_state(state)
54
87
 
55
- # set temp thermostat
56
- await bsblan.thermostat(target_temperature=19.0)
88
+ # Set thermostat temperature
89
+ print("\nSetting temperature to 18°C")
90
+ await bsblan.thermostat(target_temperature="18")
57
91
 
58
- # set hvac_mode (0-3) (protection,auto,reduced,comfort)
59
- await bsblan.thermostat(hvac_mode=3)
92
+ # Set HVAC mode
93
+ print("Setting HVAC mode to heat")
94
+ await bsblan.thermostat(hvac_mode="heat")
60
95
 
61
- # get some generic info from the heater
62
- info: Info = await bsblan.info()
63
- print(info)
96
+ # Get and print sensor information
97
+ sensor: Sensor = await bsblan.sensor()
98
+ await print_sensor(sensor)
64
99
 
65
- # get device info
100
+ # Get and print device and general info
66
101
  device: Device = await bsblan.device()
67
- print(device)
102
+ info: Info = await bsblan.info()
103
+ await print_device_info(device, info)
68
104
 
69
- # get sensor from bsblan device
70
- sensor: Sensor = await bsblan.sensor()
71
- print(f"outside temperature: {sensor.outside_temperature.value}")
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
- loop = asyncio.get_event_loop_policy().get_event_loop()
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.10+
144
+ - Python 3.12+
110
145
  - [Poetry][poetry-install]
111
146
  - NodeJS 16+ (including NPM)
112
147
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-bsblan"
3
- version = "0.6.00"
3
+ version = "0.6.2"
4
4
  description = "Asynchronous Python client for BSBLAN"
5
5
  authors = ["Willem-Jan van Rootselaar <liudgervr@gmail.com>"]
6
6
  maintainers = ["Willem-Jan van Rootselaar <liudgervr@gmail.com>"]
@@ -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 importlib import metadata
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__(self, config: BSBLANConfig) -> None:
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: ClientSession | None = None
88
- self._close_session = False
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 = aiohttp.BasicAuth(self.config.username, self.config.password)
170
+ auth = BasicAuth(self.config.username, self.config.password)
167
171
 
168
172
  headers = {
169
- "User-Agent": f"PythonBSBLAN/{version}",
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
- if not self._version:
269
- device = await self.device()
270
- self._version = device.version
271
- logger.debug("BSBLAN version: %s", self._version)
272
- if pkg_version.parse(self._version) < pkg_version.parse("1.2.0"):
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._version) > pkg_version.parse("3.0.0"):
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,