python-bsblan 0.5.19__tar.gz → 0.6.0__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.5.19 → python_bsblan-0.6.0}/PKG-INFO +3 -4
- {python_bsblan-0.5.19 → python_bsblan-0.6.0}/pyproject.toml +3 -5
- {python_bsblan-0.5.19 → python_bsblan-0.6.0}/src/bsblan/__init__.py +2 -1
- {python_bsblan-0.5.19 → python_bsblan-0.6.0}/src/bsblan/bsblan.py +72 -64
- {python_bsblan-0.5.19 → python_bsblan-0.6.0}/src/bsblan/exceptions.py +3 -1
- {python_bsblan-0.5.19 → python_bsblan-0.6.0}/src/bsblan/models.py +1 -0
- {python_bsblan-0.5.19 → python_bsblan-0.6.0}/README.md +0 -0
- {python_bsblan-0.5.19 → python_bsblan-0.6.0}/src/bsblan/constants.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-bsblan
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Asynchronous Python client for BSBLAN
|
|
5
5
|
Home-page: https://github.com/liudger/python-bsblan
|
|
6
6
|
License: MIT
|
|
@@ -9,16 +9,15 @@ Author: Willem-Jan van Rootselaar
|
|
|
9
9
|
Author-email: liudgervr@gmail.com
|
|
10
10
|
Maintainer: Willem-Jan van Rootselaar
|
|
11
11
|
Maintainer-email: liudgervr@gmail.com
|
|
12
|
-
Requires-Python: >=3.
|
|
12
|
+
Requires-Python: >=3.12,<4.0
|
|
13
13
|
Classifier: Development Status :: 3 - Alpha
|
|
14
14
|
Classifier: Framework :: AsyncIO
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
16
|
Classifier: License :: OSI Approved :: MIT License
|
|
17
17
|
Classifier: Natural Language :: English
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
22
|
Requires-Dist: aiohttp (>=3.8.1)
|
|
24
23
|
Requires-Dist: async-timeout (>=4.0.3,<5.0.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-bsblan"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.00"
|
|
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>"]
|
|
@@ -15,8 +15,8 @@ classifiers = [
|
|
|
15
15
|
"Framework :: AsyncIO",
|
|
16
16
|
"Intended Audience :: Developers",
|
|
17
17
|
"Natural Language :: English",
|
|
18
|
-
"Programming Language :: Python :: 3.10",
|
|
19
18
|
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
20
|
"Programming Language :: Python :: 3",
|
|
21
21
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
22
22
|
]
|
|
@@ -25,7 +25,7 @@ packages = [
|
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
[tool.poetry.dependencies]
|
|
28
|
-
python = "^3.
|
|
28
|
+
python = "^3.12"
|
|
29
29
|
aiohttp = ">=3.8.1"
|
|
30
30
|
yarl = ">=1.7.2"
|
|
31
31
|
packaging = ">=21.3"
|
|
@@ -57,7 +57,6 @@ darglint = "^1.8.1"
|
|
|
57
57
|
safety = "^3.0.0"
|
|
58
58
|
codespell = "^2.2.2"
|
|
59
59
|
bandit = "^1.7.4"
|
|
60
|
-
pytest-mock = "^3.10.0"
|
|
61
60
|
|
|
62
61
|
[tool.poetry.urls]
|
|
63
62
|
"Bug Tracker" = "https://github.com/liudger/python-bsblan/issues"
|
|
@@ -80,7 +79,6 @@ multi_line_output = 3
|
|
|
80
79
|
# free to run mypy on Windows, Linux, or macOS and get consistent
|
|
81
80
|
# results.
|
|
82
81
|
platform = "linux"
|
|
83
|
-
python_version = "3.10"
|
|
84
82
|
|
|
85
83
|
# show error messages from unrelated files
|
|
86
84
|
follow_imports = "normal"
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"""Asynchronous Python client for BSBLAN."""
|
|
2
2
|
|
|
3
|
-
from .bsblan import BSBLAN
|
|
3
|
+
from .bsblan import BSBLAN, BSBLANConfig
|
|
4
4
|
from .exceptions import BSBLANConnectionError, BSBLANError
|
|
5
5
|
from .models import Device, Info, Sensor, State, StaticState
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"BSBLAN",
|
|
9
|
+
"BSBLANConfig",
|
|
9
10
|
"BSBLANConnectionError",
|
|
10
11
|
"BSBLANError",
|
|
11
12
|
"Info",
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
"""Asynchronous Python client for BSB-Lan."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import asyncio
|
|
5
6
|
import logging
|
|
6
|
-
import socket
|
|
7
7
|
from asyncio.log import logger
|
|
8
8
|
from dataclasses import dataclass, field
|
|
9
9
|
from importlib import metadata
|
|
10
|
-
from typing import Any, TypedDict, cast
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Mapping, TypedDict, cast
|
|
11
11
|
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
from aiohttp.client import ClientError, ClientResponseError, ClientSession
|
|
12
|
+
import aiohttp
|
|
13
|
+
from aiohttp.client import ClientSession
|
|
15
14
|
from aiohttp.hdrs import METH_POST
|
|
16
|
-
from aiohttp.helpers import BasicAuth
|
|
17
15
|
from packaging import version as pkg_version
|
|
18
16
|
from typing_extensions import Self
|
|
19
17
|
from yarl import URL
|
|
@@ -41,12 +39,15 @@ from .exceptions import (
|
|
|
41
39
|
)
|
|
42
40
|
from .models import Device, Info, Sensor, State, StaticState
|
|
43
41
|
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from aiohttp.helpers import BasicAuth
|
|
44
|
+
|
|
44
45
|
logging.basicConfig(level=logging.DEBUG)
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
@dataclass
|
|
48
|
-
class
|
|
49
|
-
"""
|
|
49
|
+
class BSBLANConfig:
|
|
50
|
+
"""Configuration for BSBLAN."""
|
|
50
51
|
|
|
51
52
|
host: str
|
|
52
53
|
username: str | None = None
|
|
@@ -54,7 +55,12 @@ class BSBLAN:
|
|
|
54
55
|
passkey: str | None = None
|
|
55
56
|
port: int = 80
|
|
56
57
|
request_timeout: int = 10
|
|
57
|
-
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class BSBLAN:
|
|
62
|
+
"""Main class for handling connections with BSBLAN."""
|
|
63
|
+
|
|
58
64
|
_version: str = ""
|
|
59
65
|
_heating_params: list[str] | None = None
|
|
60
66
|
_string_circuit1: str | None = None
|
|
@@ -69,14 +75,49 @@ class BSBLAN:
|
|
|
69
75
|
_auth: BasicAuth | None = None
|
|
70
76
|
_close_session: bool = False
|
|
71
77
|
|
|
78
|
+
def __init__(self, config: BSBLANConfig) -> None:
|
|
79
|
+
"""Initialize the BSBLAN object.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
----
|
|
83
|
+
config: Configuration for the BSBLAN object.
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
self.config = config
|
|
87
|
+
self.session: ClientSession | None = None
|
|
88
|
+
self._close_session = False
|
|
89
|
+
|
|
90
|
+
async def __aenter__(self) -> Self:
|
|
91
|
+
"""Enter method for the context manager.
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
Self: The current instance of the context manager.
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
if self.session is None:
|
|
99
|
+
self.session = aiohttp.ClientSession()
|
|
100
|
+
self._close_session = True
|
|
101
|
+
return self
|
|
102
|
+
|
|
103
|
+
async def __aexit__(self, *args: object) -> None:
|
|
104
|
+
"""Exit method for the context manager.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
----
|
|
108
|
+
*args: Arguments passed to the exit method.
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
if self._close_session and self.session:
|
|
112
|
+
await self.session.close()
|
|
113
|
+
|
|
72
114
|
# cSpell:ignore BSBLAN
|
|
73
|
-
@backoff.on_exception(backoff.expo, BSBLANConnectionError, max_tries=3, logger=None)
|
|
74
115
|
async def _request(
|
|
75
116
|
self,
|
|
76
117
|
method: str = METH_POST,
|
|
77
118
|
base_path: str = "/JQ",
|
|
78
119
|
data: dict[str, object] | None = None,
|
|
79
|
-
params:
|
|
120
|
+
params: Mapping[str, str | int] | str | None = None,
|
|
80
121
|
) -> dict[str, Any]:
|
|
81
122
|
"""Handle a request to a BSBLAN device.
|
|
82
123
|
|
|
@@ -110,18 +151,19 @@ class BSBLAN:
|
|
|
110
151
|
version = "0.0.0"
|
|
111
152
|
|
|
112
153
|
# retrieve passkey for custom url
|
|
113
|
-
if self.passkey
|
|
114
|
-
base_path = f"/{self.passkey}{base_path}"
|
|
154
|
+
if self.config.passkey:
|
|
155
|
+
base_path = f"/{self.config.passkey}{base_path}"
|
|
115
156
|
|
|
116
157
|
url = URL.build(
|
|
117
158
|
scheme="http",
|
|
118
|
-
host=self.host,
|
|
119
|
-
port=self.port,
|
|
159
|
+
host=self.config.host,
|
|
160
|
+
port=self.config.port,
|
|
120
161
|
path=base_path,
|
|
121
|
-
)
|
|
162
|
+
)
|
|
122
163
|
|
|
123
|
-
|
|
124
|
-
|
|
164
|
+
auth = None
|
|
165
|
+
if self.config.username and self.config.password:
|
|
166
|
+
auth = aiohttp.BasicAuth(self.config.username, self.config.password)
|
|
125
167
|
|
|
126
168
|
headers = {
|
|
127
169
|
"User-Agent": f"PythonBSBLAN/{version}",
|
|
@@ -133,31 +175,23 @@ class BSBLAN:
|
|
|
133
175
|
self._close_session = True
|
|
134
176
|
|
|
135
177
|
try:
|
|
136
|
-
async with
|
|
137
|
-
|
|
178
|
+
async with asyncio.timeout(self.config.request_timeout):
|
|
179
|
+
async with self.session.request(
|
|
138
180
|
method,
|
|
139
181
|
url,
|
|
140
|
-
auth=
|
|
182
|
+
auth=auth,
|
|
141
183
|
params=params,
|
|
142
184
|
json=data,
|
|
143
185
|
headers=headers,
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
raise BSBLANConnectionError(BSBLANConnectionError.message) from exception
|
|
154
|
-
|
|
155
|
-
content_type = response.headers.get("Content-Type", "")
|
|
156
|
-
if "application/json" not in content_type:
|
|
157
|
-
text = await response.text()
|
|
158
|
-
raise BSBLANError(BSBLANError.message + f" ({text})")
|
|
159
|
-
|
|
160
|
-
return cast(dict[str, Any], await response.json())
|
|
186
|
+
) as response:
|
|
187
|
+
response.raise_for_status()
|
|
188
|
+
return cast(dict[str, Any], await response.json())
|
|
189
|
+
except asyncio.TimeoutError as e:
|
|
190
|
+
raise BSBLANConnectionError(BSBLANConnectionError.message_timeout) from e
|
|
191
|
+
except aiohttp.ClientError as e:
|
|
192
|
+
raise BSBLANConnectionError(BSBLANConnectionError.message_error) from e
|
|
193
|
+
except ValueError as e:
|
|
194
|
+
raise BSBLANError(str(e)) from e
|
|
161
195
|
|
|
162
196
|
async def state(self) -> State:
|
|
163
197
|
"""Get the current state from BSBLAN device.
|
|
@@ -361,29 +395,3 @@ class BSBLAN:
|
|
|
361
395
|
# Now it only checks if it could set value.
|
|
362
396
|
response = await self._request(base_path="/JS", data=dict(state))
|
|
363
397
|
logger.debug("response for setting: %s", response)
|
|
364
|
-
|
|
365
|
-
async def close(self) -> None:
|
|
366
|
-
"""Close open client session."""
|
|
367
|
-
if self.session and self._close_session:
|
|
368
|
-
await self.session.close()
|
|
369
|
-
|
|
370
|
-
async def __aenter__(self) -> Self:
|
|
371
|
-
"""Async enter.
|
|
372
|
-
|
|
373
|
-
Returns
|
|
374
|
-
-------
|
|
375
|
-
The BSBLAN object.
|
|
376
|
-
|
|
377
|
-
"""
|
|
378
|
-
logger.debug("BSBLAN: %s", self)
|
|
379
|
-
return self
|
|
380
|
-
|
|
381
|
-
async def __aexit__(self, *_exc_info: object) -> None:
|
|
382
|
-
"""Async exit.
|
|
383
|
-
|
|
384
|
-
Args:
|
|
385
|
-
----
|
|
386
|
-
*_exc_info: Exec type.
|
|
387
|
-
|
|
388
|
-
"""
|
|
389
|
-
await self.close()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Exceptions for for BSB-Lan."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
|
|
@@ -33,7 +34,8 @@ class BSBLANConnectionError(BSBLANError):
|
|
|
33
34
|
|
|
34
35
|
"""
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
message_timeout = "Timeout occurred while connecting to BSBLAN device."
|
|
38
|
+
message_error = "Error occurred while connecting to BSBLAN device."
|
|
37
39
|
|
|
38
40
|
def __init__(self, response: str | None = None) -> None:
|
|
39
41
|
"""Initialize a new instance of the BSBLANConnectionError class.
|
|
File without changes
|
|
File without changes
|