velbus-aio 2024.5.1__py3-none-any.whl → 2024.7.0__py3-none-any.whl
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.
Potentially problematic release.
This version of velbus-aio might be problematic. Click here for more details.
- {velbus_aio-2024.5.1.dist-info → velbus_aio-2024.7.0.dist-info}/METADATA +1 -1
- {velbus_aio-2024.5.1.dist-info → velbus_aio-2024.7.0.dist-info}/RECORD +15 -15
- {velbus_aio-2024.5.1.dist-info → velbus_aio-2024.7.0.dist-info}/WHEEL +1 -1
- velbusaio/channels.py +1 -0
- velbusaio/const.py +7 -1
- velbusaio/controller.py +21 -55
- velbusaio/handler.py +212 -99
- velbusaio/message.py +6 -6
- velbusaio/messages/module_subtype.py +3 -3
- velbusaio/messages/module_type.py +5 -5
- velbusaio/module.py +11 -16
- velbusaio/protocol.json +68 -0
- velbusaio/protocol.py +4 -10
- {velbus_aio-2024.5.1.dist-info → velbus_aio-2024.7.0.dist-info}/LICENSE +0 -0
- {velbus_aio-2024.5.1.dist-info → velbus_aio-2024.7.0.dist-info}/top_level.txt +0 -0
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
velbusaio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
velbusaio/channels.py,sha256=
|
|
2
|
+
velbusaio/channels.py,sha256=YdIMneLYj_zTa8e2dHmhZvGKcD3iyv5jMeZpfuORcTU,22910
|
|
3
3
|
velbusaio/command_registry.py,sha256=j9KuLmada41PMEdtfPvLmXVOAZOkBM9zi-3kluwYffk,5140
|
|
4
|
-
velbusaio/const.py,sha256=
|
|
5
|
-
velbusaio/controller.py,sha256=
|
|
4
|
+
velbusaio/const.py,sha256=HyBLMJkzZ0ApJRjCffcWlSGr2c3Qgt6kFpdhLQwl--c,1862
|
|
5
|
+
velbusaio/controller.py,sha256=XcASgSs5DBwmjAjV3Aa9L4wyJEuLpn8qEUqwfWNkTPA,7369
|
|
6
6
|
velbusaio/discovery.py,sha256=Px6qoZl4QhF17aMz6JxstCORBpLzZGWEK9h4Vyvg57o,1649
|
|
7
7
|
velbusaio/exceptions.py,sha256=FHkXaM3dK5Gkk-QGAf9dLE3FPlCU2FRZWUyY-4KRNnA,515
|
|
8
|
-
velbusaio/handler.py,sha256=
|
|
8
|
+
velbusaio/handler.py,sha256=UF-F2LeVEocDniXKLE8FmUMO-o9QdRtDQoYog6d4rag,10867
|
|
9
9
|
velbusaio/helpers.py,sha256=iqpoereRH4JY5WAkozIqWvXWyRmhko-J-UGXDylFyEM,2537
|
|
10
|
-
velbusaio/message.py,sha256=
|
|
11
|
-
velbusaio/module.py,sha256=
|
|
12
|
-
velbusaio/protocol.json,sha256=
|
|
13
|
-
velbusaio/protocol.py,sha256=
|
|
10
|
+
velbusaio/message.py,sha256=_MRuI4XnMcqeduCDJ3AwhAPL1B8VMstFDnjjLo8QuxM,5018
|
|
11
|
+
velbusaio/module.py,sha256=OMFIa8VhJR-G_rg2PcGUdpmubLD6gDwVruN4ZCmwpMY,35215
|
|
12
|
+
velbusaio/protocol.json,sha256=cQ2RsXomOjB-sd4fIu53QthOlkcfJSa7M0oGkzi9fJQ,253142
|
|
13
|
+
velbusaio/protocol.py,sha256=3XhSFYe2GS-3fis5uClJV4tJxI_XO0x-JKZW0cD5cKs,8004
|
|
14
14
|
velbusaio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
velbusaio/raw_message.py,sha256=ODoTPVAvyXXQSMXxtQO1U5OxcoQ4W8JJN5fotEDGSTo,4690
|
|
16
16
|
velbusaio/util.py,sha256=FW6YCiPYWOCgqHDs8-LbzME0h81mqftYVG0qqZ-oo7Y,1568
|
|
@@ -51,8 +51,8 @@ velbusaio/messages/memory_data_block.py,sha256=3QufrOahEkIhJjivWtOXp62YuVaUwx6tr
|
|
|
51
51
|
velbusaio/messages/memory_dump_request.py,sha256=xrPsdpygD9DUF3lp-BzJacuq1dNkqz2wtHpqu_F1zVM,699
|
|
52
52
|
velbusaio/messages/module_status.py,sha256=83UrRFXgRRngcF36fogyw8G5XPFlyaoZoW7XlxwvnQE,6637
|
|
53
53
|
velbusaio/messages/module_status_request.py,sha256=s3h8F6Dpmjcb4hqDQKIzdOgocW1F_st40cctcW7WmQ4,961
|
|
54
|
-
velbusaio/messages/module_subtype.py,sha256=
|
|
55
|
-
velbusaio/messages/module_type.py,sha256=
|
|
54
|
+
velbusaio/messages/module_subtype.py,sha256=KG-OC0eW-ZRnP1P8EWmcay6O-no6Mn2cklEjokaIW9M,1487
|
|
55
|
+
velbusaio/messages/module_type.py,sha256=fb4q_SP3B6h_f9jGxgMvNoIpgBPXx36nwjCmtf0DfBw,4154
|
|
56
56
|
velbusaio/messages/module_type_request.py,sha256=fntMYCF1WVwSseCy3zRoZQH6JeGz8JDfTkwHC3AvcB4,754
|
|
57
57
|
velbusaio/messages/push_button_status.py,sha256=SRFLvNC00FdKDTSb4kNVSHeXI1i5Ey5ENxpsB3B8FuU,1554
|
|
58
58
|
velbusaio/messages/raw.py,sha256=pepZSUVY-ro5jv3ZyO6BI3ap5RZ2g_Bd9dlbaICFldQ,1964
|
|
@@ -96,8 +96,8 @@ velbusaio/messages/very_fast_blinking_led.py,sha256=vlMEern8PoOvtO5JaAk9erMR4IKJ
|
|
|
96
96
|
velbusaio/messages/write_data_to_memory.py,sha256=gr6bi4SzK8Mw8fnp8yV-STq5jts7NoeV7zZgdptH5Vs,1039
|
|
97
97
|
velbusaio/messages/write_memory_block.py,sha256=zGnNxx_M66HpBQ8S7kagtNw8_qSRHsOLk1MuiS0uygM,1032
|
|
98
98
|
velbusaio/messages/write_module_address_and_serial_number.py,sha256=6y57j-md3btNtQddX5CUREtSs1Dzgkd953sQPZ3Pioo,1597
|
|
99
|
-
velbus_aio-2024.
|
|
100
|
-
velbus_aio-2024.
|
|
101
|
-
velbus_aio-2024.
|
|
102
|
-
velbus_aio-2024.
|
|
103
|
-
velbus_aio-2024.
|
|
99
|
+
velbus_aio-2024.7.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
100
|
+
velbus_aio-2024.7.0.dist-info/METADATA,sha256=Y590SRjMvVtmhl4FQ5A57mqBqo-KCGF_ABRZwKYVACM,3259
|
|
101
|
+
velbus_aio-2024.7.0.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
|
|
102
|
+
velbus_aio-2024.7.0.dist-info/top_level.txt,sha256=W0-lSOwD23mm8FqaIe9vY20fKicBMIdUVjF-zmfxRnY,15
|
|
103
|
+
velbus_aio-2024.7.0.dist-info/RECORD,,
|
velbusaio/channels.py
CHANGED
velbusaio/const.py
CHANGED
|
@@ -38,7 +38,13 @@ RTR: Final = 0x40
|
|
|
38
38
|
NO_RTR: Final = 0x00
|
|
39
39
|
|
|
40
40
|
CACHEDIR: Final = ".velbuscache"
|
|
41
|
-
|
|
41
|
+
|
|
42
|
+
# Module scan timeout values (in mSec)
|
|
43
|
+
SCAN_MODULETYPE_TIMEOUT: Final = 2000 # time to wait for ModuleTypeRequest
|
|
44
|
+
SCAN_MODULEINFO_TIMEOUT_INITIAL: Final = 1000 # time to wait for first info (status)
|
|
45
|
+
SCAN_MODULEINFO_TIMEOUT_INTERVAL: Final = (
|
|
46
|
+
150 # time to wait for info interval (between next message)
|
|
47
|
+
)
|
|
42
48
|
|
|
43
49
|
DEVICE_CLASS_ILLUMINANCE: Final = "illuminance"
|
|
44
50
|
DEVICE_CLASS_TEMPERATURE: Final = "temperature"
|
velbusaio/controller.py
CHANGED
|
@@ -14,7 +14,6 @@ import serial
|
|
|
14
14
|
import serial_asyncio_fast
|
|
15
15
|
|
|
16
16
|
from velbusaio.channels import Channel
|
|
17
|
-
from velbusaio.const import LOAD_TIMEOUT
|
|
18
17
|
from velbusaio.exceptions import VelbusConnectionFailed
|
|
19
18
|
from velbusaio.handler import PacketHandler
|
|
20
19
|
from velbusaio.helpers import get_cache_dir
|
|
@@ -42,17 +41,16 @@ class Velbus:
|
|
|
42
41
|
self._protocol = VelbusProtocol(
|
|
43
42
|
message_received_callback=self._on_message_received,
|
|
44
43
|
connection_lost_callback=self._on_connection_lost,
|
|
45
|
-
end_of_scan_callback=self._on_end_of_scan,
|
|
46
44
|
)
|
|
47
45
|
self._closing = False
|
|
48
46
|
self._auto_reconnect = True
|
|
49
47
|
|
|
50
48
|
self._dsn = dsn
|
|
51
|
-
self._handler = PacketHandler(self
|
|
49
|
+
self._handler = PacketHandler(self)
|
|
52
50
|
self._modules: dict[int, Module] = {}
|
|
53
51
|
self._submodules: list[int] = []
|
|
54
|
-
self._send_queue = asyncio.Queue()
|
|
55
|
-
self._cache_dir = cache_dir
|
|
52
|
+
self._send_queue: asyncio.Queue = asyncio.Queue()
|
|
53
|
+
self._cache_dir: str = cache_dir
|
|
56
54
|
# make sure the cachedir exists
|
|
57
55
|
pathlib.Path(self._cache_dir).mkdir(parents=True, exist_ok=True)
|
|
58
56
|
|
|
@@ -66,11 +64,7 @@ class Velbus:
|
|
|
66
64
|
self._log.debug("Reconnecting to transport")
|
|
67
65
|
asyncio.ensure_future(self.connect())
|
|
68
66
|
|
|
69
|
-
def
|
|
70
|
-
"""Notify the scan failure."""
|
|
71
|
-
self._handler.scan_finished()
|
|
72
|
-
|
|
73
|
-
async def add_module(
|
|
67
|
+
def add_module(
|
|
74
68
|
self,
|
|
75
69
|
addr: int,
|
|
76
70
|
typ: int,
|
|
@@ -81,8 +75,7 @@ class Velbus:
|
|
|
81
75
|
build_week: int | None = None,
|
|
82
76
|
) -> None:
|
|
83
77
|
"""Add a found module to the module cache."""
|
|
84
|
-
|
|
85
|
-
self._modules[addr] = Module.factory(
|
|
78
|
+
module = Module.factory(
|
|
86
79
|
addr,
|
|
87
80
|
typ,
|
|
88
81
|
data,
|
|
@@ -92,30 +85,31 @@ class Velbus:
|
|
|
92
85
|
memorymap=memorymap,
|
|
93
86
|
cache_dir=self._cache_dir,
|
|
94
87
|
)
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
module.initialize(self.send)
|
|
89
|
+
self._modules[addr] = module
|
|
90
|
+
self._log.info(f"Found module {addr}: {module}")
|
|
97
91
|
|
|
98
|
-
|
|
92
|
+
def add_submodules(self, module: Module, subList: dict[int, int]) -> None:
|
|
99
93
|
"""Add submodules address to module."""
|
|
100
94
|
for sub_num, sub_addr in subList.items():
|
|
101
95
|
if sub_addr == 0xFF:
|
|
102
96
|
continue
|
|
103
97
|
self._submodules.append(sub_addr)
|
|
104
|
-
|
|
105
|
-
self._modules[sub_addr] =
|
|
106
|
-
|
|
98
|
+
module._sub_address[sub_num] = sub_addr
|
|
99
|
+
self._modules[sub_addr] = module
|
|
100
|
+
module.cleanupSubChannels()
|
|
107
101
|
|
|
108
102
|
def get_modules(self) -> dict:
|
|
109
103
|
"""Return the module cache."""
|
|
110
104
|
return self._modules
|
|
111
105
|
|
|
112
|
-
def get_module(self, addr:
|
|
106
|
+
def get_module(self, addr: int) -> None | Module:
|
|
113
107
|
"""Get a module on an address."""
|
|
114
108
|
if addr in self._modules:
|
|
115
109
|
return self._modules[addr]
|
|
116
110
|
return None
|
|
117
111
|
|
|
118
|
-
def get_channels(self, addr:
|
|
112
|
+
def get_channels(self, addr: int) -> None | dict:
|
|
119
113
|
"""Get the channels for an address."""
|
|
120
114
|
if addr in self._modules:
|
|
121
115
|
return (self._modules[addr]).get_channels()
|
|
@@ -129,6 +123,7 @@ class Velbus:
|
|
|
129
123
|
|
|
130
124
|
async def connect(self, test_connect: bool = False) -> None:
|
|
131
125
|
"""Connect to the bus and load all the data."""
|
|
126
|
+
await self._handler.read_protocol_data()
|
|
132
127
|
auth = None
|
|
133
128
|
# connect to the bus
|
|
134
129
|
if ":" in self._dsn:
|
|
@@ -181,44 +176,15 @@ class Velbus:
|
|
|
181
176
|
await self._protocol.write_auth_key(auth)
|
|
182
177
|
|
|
183
178
|
# scan the bus
|
|
184
|
-
await self.scan()
|
|
179
|
+
await self._handler.scan()
|
|
185
180
|
|
|
186
181
|
async def scan(self) -> None:
|
|
187
|
-
"""
|
|
188
|
-
self._handler.
|
|
189
|
-
for addr in range(1, 256):
|
|
190
|
-
msg = ModuleTypeRequestMessage(addr)
|
|
191
|
-
await self.send(msg)
|
|
192
|
-
await self._handler._scan_complete_event.wait()
|
|
193
|
-
# calculate how long to wait
|
|
194
|
-
calc_timeout = len(self._modules) * 30
|
|
195
|
-
if calc_timeout < LOAD_TIMEOUT:
|
|
196
|
-
timeout = calc_timeout
|
|
197
|
-
else:
|
|
198
|
-
timeout = LOAD_TIMEOUT
|
|
199
|
-
# create a task to wait until we have all modules loaded
|
|
200
|
-
tsk = asyncio.Task(self._check_if_modules_are_loaded())
|
|
201
|
-
try:
|
|
202
|
-
await asyncio.wait_for(tsk, timeout=timeout)
|
|
203
|
-
except asyncio.TimeoutError:
|
|
204
|
-
self._log.error(
|
|
205
|
-
f"Not all modules are loaded within a timeout of {LOAD_TIMEOUT} seconds, continuing with the loaded modules"
|
|
206
|
-
)
|
|
182
|
+
"""Service endpoint to restart the scan"""
|
|
183
|
+
await self._handler.scan(True)
|
|
207
184
|
|
|
208
|
-
async def
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
mods_loaded = 0
|
|
212
|
-
for mod in (self.get_modules()).values():
|
|
213
|
-
if mod.is_loaded():
|
|
214
|
-
mods_loaded += 1
|
|
215
|
-
else:
|
|
216
|
-
self._log.warning(f"Waiting for module {mod._address}")
|
|
217
|
-
if mods_loaded == len(self.get_modules()):
|
|
218
|
-
self._log.info("All modules loaded")
|
|
219
|
-
return
|
|
220
|
-
self._log.info("Not all modules loaded yet, waiting 15 seconds")
|
|
221
|
-
await asyncio.sleep(15)
|
|
185
|
+
async def sendTypeRequestMessage(self, address: int) -> None:
|
|
186
|
+
msg = ModuleTypeRequestMessage(address)
|
|
187
|
+
await self.send(msg)
|
|
222
188
|
|
|
223
189
|
async def send(self, msg: Message) -> None:
|
|
224
190
|
"""Send a packet."""
|
velbusaio/handler.py
CHANGED
|
@@ -5,9 +5,19 @@ Velbus packet handler
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
+
from velbusaio.const import SCAN_MODULETYPE_TIMEOUT
|
|
9
|
+
from velbusaio.const import SCAN_MODULEINFO_TIMEOUT_INITIAL
|
|
10
|
+
from velbusaio.const import SCAN_MODULEINFO_TIMEOUT_INTERVAL
|
|
11
|
+
|
|
8
12
|
import asyncio
|
|
9
13
|
import json
|
|
10
14
|
import logging
|
|
15
|
+
import threading
|
|
16
|
+
import os
|
|
17
|
+
import pathlib
|
|
18
|
+
|
|
19
|
+
from aiofile import async_open
|
|
20
|
+
|
|
11
21
|
from typing import TYPE_CHECKING, Awaitable, Callable
|
|
12
22
|
import pkg_resources
|
|
13
23
|
|
|
@@ -15,8 +25,10 @@ from velbusaio.command_registry import commandRegistry
|
|
|
15
25
|
from velbusaio.helpers import h2, keys_exists
|
|
16
26
|
from velbusaio.message import Message
|
|
17
27
|
from velbusaio.messages.module_subtype import ModuleSubTypeMessage
|
|
18
|
-
from velbusaio.messages.module_type import ModuleTypeMessage
|
|
28
|
+
from velbusaio.messages.module_type import ModuleTypeMessage, ModuleType2Message
|
|
19
29
|
from velbusaio.raw_message import RawMessage
|
|
30
|
+
from velbusaio.helpers import get_cache_dir
|
|
31
|
+
|
|
20
32
|
|
|
21
33
|
if TYPE_CHECKING:
|
|
22
34
|
from velbusaio.controller import Velbus
|
|
@@ -29,27 +41,102 @@ class PacketHandler:
|
|
|
29
41
|
|
|
30
42
|
def __init__(
|
|
31
43
|
self,
|
|
32
|
-
writer: Callable[[Message], Awaitable[None]],
|
|
33
44
|
velbus: Velbus,
|
|
34
45
|
) -> None:
|
|
35
|
-
self._log = logging.getLogger("velbus-
|
|
36
|
-
self.
|
|
46
|
+
self._log = logging.getLogger("velbus-handler")
|
|
47
|
+
self._log.setLevel(logging.DEBUG)
|
|
37
48
|
self._velbus = velbus
|
|
49
|
+
self._typeResponseReceived = asyncio.Event()
|
|
50
|
+
self._scanLock = threading.Lock()
|
|
51
|
+
self._modulescan_address = 0
|
|
38
52
|
self._scan_complete = False
|
|
39
|
-
self.
|
|
40
|
-
|
|
53
|
+
self._scan_delay_msec = 0
|
|
54
|
+
|
|
55
|
+
async def read_protocol_data(self):
|
|
56
|
+
async with async_open(
|
|
41
57
|
pkg_resources.resource_filename(__name__, "protocol.json")
|
|
42
58
|
) as protocol_file:
|
|
43
|
-
self.pdata = json.
|
|
59
|
+
self.pdata = json.loads(await protocol_file.read())
|
|
44
60
|
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
def empty_cache(self) -> bool:
|
|
62
|
+
if (
|
|
63
|
+
len(
|
|
64
|
+
[
|
|
65
|
+
name
|
|
66
|
+
for name in os.listdir(f"{get_cache_dir()}")
|
|
67
|
+
if os.path.isfile(f"{get_cache_dir()}/{name}")
|
|
68
|
+
]
|
|
69
|
+
)
|
|
70
|
+
== 0
|
|
71
|
+
):
|
|
72
|
+
return True
|
|
73
|
+
return False
|
|
49
74
|
|
|
50
|
-
def
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
async def scan(self, reload_cache: bool = False) -> None:
|
|
76
|
+
if reload_cache:
|
|
77
|
+
self._modulescan_address = 0
|
|
78
|
+
self._scan_complete = False
|
|
79
|
+
# non-blocking check to see if the cache_dir is empty
|
|
80
|
+
loop = asyncio.get_running_loop()
|
|
81
|
+
if not reload_cache and await loop.run_in_executor(None, self.empty_cache):
|
|
82
|
+
self._log.info("No cache yet, so forcing a bus scan")
|
|
83
|
+
reload_cache = True
|
|
84
|
+
self._log.info("Start module scan")
|
|
85
|
+
while self._modulescan_address < 254:
|
|
86
|
+
address = 0
|
|
87
|
+
module = None
|
|
88
|
+
with self._scanLock:
|
|
89
|
+
self._modulescan_address = self._modulescan_address + 1
|
|
90
|
+
address = self._modulescan_address
|
|
91
|
+
module = self._velbus.get_module(address)
|
|
92
|
+
|
|
93
|
+
self._log.info(f"Starting handling scan {address}")
|
|
94
|
+
|
|
95
|
+
cfile = pathlib.Path(f"{get_cache_dir()}/{address}.json")
|
|
96
|
+
# cleanup the old module cache if needed
|
|
97
|
+
scanModule = reload_cache
|
|
98
|
+
if scanModule and os.path.isfile(cfile):
|
|
99
|
+
os.remove(cfile)
|
|
100
|
+
elif os.path.isfile(cfile):
|
|
101
|
+
scanModule = os.path.isfile(cfile)
|
|
102
|
+
if scanModule:
|
|
103
|
+
try:
|
|
104
|
+
self._log.info(f"Starting scan {address}")
|
|
105
|
+
self._typeResponseReceived.clear()
|
|
106
|
+
await self._velbus.sendTypeRequestMessage(address)
|
|
107
|
+
await asyncio.wait_for(
|
|
108
|
+
self._typeResponseReceived.wait(),
|
|
109
|
+
SCAN_MODULETYPE_TIMEOUT / 1000.0,
|
|
110
|
+
)
|
|
111
|
+
with self._scanLock:
|
|
112
|
+
module = self._velbus.get_module(address)
|
|
113
|
+
except asyncio.TimeoutError:
|
|
114
|
+
self._log.info(
|
|
115
|
+
f"Scan module {address} failed: not present or unavailable"
|
|
116
|
+
)
|
|
117
|
+
if module is not None:
|
|
118
|
+
try:
|
|
119
|
+
self._log.debug(f"Module {address} detected: start loading")
|
|
120
|
+
await asyncio.wait_for(
|
|
121
|
+
module.load(from_cache=True),
|
|
122
|
+
SCAN_MODULEINFO_TIMEOUT_INITIAL / 1000.0,
|
|
123
|
+
)
|
|
124
|
+
self._scan_delay_msec = SCAN_MODULEINFO_TIMEOUT_INITIAL
|
|
125
|
+
while self._scan_delay_msec > 50 and not module.is_loaded():
|
|
126
|
+
# self._log.debug(
|
|
127
|
+
# f"\t... waiting {self._scan_delay_msec} is_loaded={module.is_loaded()}"
|
|
128
|
+
# )
|
|
129
|
+
self._scan_delay_msec = self._scan_delay_msec - 50
|
|
130
|
+
await asyncio.sleep(0.05)
|
|
131
|
+
self._log.info(
|
|
132
|
+
f"Scan module {address} completed, module loaded={module.is_loaded()}"
|
|
133
|
+
)
|
|
134
|
+
except asyncio.TimeoutError:
|
|
135
|
+
self._log.error(
|
|
136
|
+
f"Module {address} did not respond to info requests after successful type request"
|
|
137
|
+
)
|
|
138
|
+
self._scan_complete = True
|
|
139
|
+
self._log.info("Module scan completed")
|
|
53
140
|
|
|
54
141
|
async def handle(self, rawmsg: RawMessage) -> None:
|
|
55
142
|
"""
|
|
@@ -66,98 +153,124 @@ class PacketHandler:
|
|
|
66
153
|
command_value = rawmsg.command
|
|
67
154
|
data = rawmsg.data_only
|
|
68
155
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
156
|
+
# handle module type response message
|
|
157
|
+
if command_value == 0xFF:
|
|
158
|
+
if not self._scan_complete:
|
|
159
|
+
tmsg: ModuleTypeMessage = ModuleTypeMessage()
|
|
160
|
+
tmsg.populate(priority, address, rtr, data)
|
|
161
|
+
with self._scanLock:
|
|
162
|
+
self._handle_module_type(tmsg)
|
|
163
|
+
if address == self._modulescan_address:
|
|
164
|
+
self._typeResponseReceived.set()
|
|
165
|
+
else:
|
|
166
|
+
self._log.debug(
|
|
167
|
+
f"Unexpected module type message module address {address}, Velbuslink scan?"
|
|
168
|
+
)
|
|
169
|
+
self._modulescan_address = address - 1
|
|
170
|
+
|
|
171
|
+
self._typeResponseReceived.set()
|
|
172
|
+
|
|
173
|
+
# handle module subtype response message
|
|
174
|
+
elif command_value in (0xB0, 0xA7, 0xA6):
|
|
175
|
+
if not self._scan_complete:
|
|
176
|
+
msg: ModuleSubTypeMessage = ModuleSubTypeMessage()
|
|
177
|
+
msg.populate(priority, address, rtr, data)
|
|
178
|
+
if command_value == 0xB0:
|
|
179
|
+
msg.sub_address_offset = 0
|
|
180
|
+
elif command_value == 0xA7:
|
|
181
|
+
msg.sub_address_offset = 4
|
|
182
|
+
elif command_value == 0xA6:
|
|
183
|
+
msg.sub_address_offset = 8
|
|
184
|
+
with self._scanLock:
|
|
185
|
+
self._scan_delay_msec = SCAN_MODULEINFO_TIMEOUT_INITIAL
|
|
186
|
+
self._handle_module_subtype(msg)
|
|
86
187
|
|
|
87
|
-
|
|
88
|
-
await self._handle_module_subtype(msg)
|
|
188
|
+
# ignore broadcast
|
|
89
189
|
elif command_value in self.pdata["MessagesBroadCast"]:
|
|
90
190
|
self._log.debug(
|
|
91
191
|
"Received broadcast message {} from {}, ignoring".format(
|
|
92
|
-
self.pdata["MessageBroadCast"][command_value.upper()], address
|
|
93
|
-
)
|
|
94
|
-
)
|
|
95
|
-
elif address in self._velbus.get_modules().keys():
|
|
96
|
-
module_type = self._velbus.get_module(address).get_type()
|
|
97
|
-
if commandRegistry.has_command(int(command_value), module_type):
|
|
98
|
-
command = commandRegistry.get_command(command_value, module_type)
|
|
99
|
-
msg = command()
|
|
100
|
-
msg.populate(priority, address, rtr, data)
|
|
101
|
-
self._log.debug(f"Received {msg}")
|
|
102
|
-
# send the message to the modules
|
|
103
|
-
await self._velbus.get_module(msg.address).on_message(msg)
|
|
104
|
-
else:
|
|
105
|
-
self._log.warning(
|
|
106
|
-
"NOT FOUND IN command_registry: addr={} cmd={} packet={}".format(
|
|
107
|
-
address,
|
|
108
|
-
command_value,
|
|
109
|
-
":".join(format(x, "02x") for x in data),
|
|
110
|
-
)
|
|
111
|
-
)
|
|
112
|
-
elif self._scan_complete:
|
|
113
|
-
# this should only happen once the scan is complete, of its not complete suspended the error message
|
|
114
|
-
self._log.warning(
|
|
115
|
-
"UNKNOWN module, you should initialize a full new velbus scan: packet={}, address={}, modules={}".format(
|
|
116
|
-
":".join(format(x, "02x") for x in data),
|
|
117
|
-
address,
|
|
118
|
-
self._velbus.get_modules().keys(),
|
|
192
|
+
self.pdata["MessageBroadCast"][str(command_value).upper()], address
|
|
119
193
|
)
|
|
120
194
|
)
|
|
121
195
|
|
|
122
|
-
|
|
196
|
+
# handle other messages for modules that are already scanned
|
|
197
|
+
else:
|
|
198
|
+
module = None
|
|
199
|
+
with self._scanLock:
|
|
200
|
+
module = self._velbus.get_module(address)
|
|
201
|
+
if module is not None:
|
|
202
|
+
module_type = module.get_type()
|
|
203
|
+
if commandRegistry.has_command(int(command_value), module_type):
|
|
204
|
+
command = commandRegistry.get_command(command_value, module_type)
|
|
205
|
+
if not command:
|
|
206
|
+
return
|
|
207
|
+
msg = command()
|
|
208
|
+
msg.populate(priority, address, rtr, data)
|
|
209
|
+
# restart the info completion time when info message received
|
|
210
|
+
if command_value in (
|
|
211
|
+
0xF0,
|
|
212
|
+
0xF1,
|
|
213
|
+
0xF2,
|
|
214
|
+
0xFB,
|
|
215
|
+
0xFE,
|
|
216
|
+
0xCC,
|
|
217
|
+
): # names, memory data, memory block
|
|
218
|
+
self._scan_delay_msec = SCAN_MODULEINFO_TIMEOUT_INTERVAL
|
|
219
|
+
# self._log.debug(f"Restart timeout {msg}")
|
|
220
|
+
# send the message to the modules
|
|
221
|
+
await module.on_message(msg)
|
|
222
|
+
else:
|
|
223
|
+
self._log.warning(f"NOT FOUND IN command_registry: {rawmsg}")
|
|
224
|
+
|
|
225
|
+
def _handle_module_type(self, msg: ModuleTypeMessage | ModuleType2Message) -> None:
|
|
123
226
|
"""
|
|
124
227
|
load the module data
|
|
125
228
|
"""
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
229
|
+
if msg is not None:
|
|
230
|
+
module = self._velbus.get_module(msg.address)
|
|
231
|
+
if module is None:
|
|
232
|
+
data = keys_exists(self.pdata, "ModuleTypes", h2(msg.module_type))
|
|
233
|
+
if not data:
|
|
234
|
+
self._log.warning(f"Module not recognized: {msg.module_type}")
|
|
235
|
+
return
|
|
236
|
+
self._velbus.add_module(
|
|
237
|
+
msg.address,
|
|
238
|
+
msg.module_type,
|
|
239
|
+
data,
|
|
240
|
+
memorymap=msg.memory_map_version,
|
|
241
|
+
build_year=msg.build_year,
|
|
242
|
+
build_week=msg.build_week,
|
|
243
|
+
serial=msg.serial,
|
|
244
|
+
)
|
|
245
|
+
else:
|
|
246
|
+
self._log.debug(
|
|
247
|
+
f"***Module already exists scanAddr={self._modulescan_address} addr={msg.address} {msg}"
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# else:
|
|
251
|
+
# self._log.debug("*** handle_module_type called without response message")
|
|
252
|
+
|
|
253
|
+
def _handle_module_subtype(self, msg: ModuleSubTypeMessage) -> None:
|
|
254
|
+
module = self._velbus.get_module(msg.address)
|
|
255
|
+
if module is not None:
|
|
256
|
+
addrList = {
|
|
257
|
+
(msg.sub_address_offset + 1): msg.sub_address_1,
|
|
258
|
+
(msg.sub_address_offset + 2): msg.sub_address_2,
|
|
259
|
+
(msg.sub_address_offset + 3): msg.sub_address_3,
|
|
260
|
+
(msg.sub_address_offset + 4): msg.sub_address_4,
|
|
261
|
+
}
|
|
262
|
+
self._velbus.add_submodules(module, addrList)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# def _channel_convert(self, module: str, channel: str, ctype: str) -> None | int:
|
|
266
|
+
# data = keys_exists(
|
|
267
|
+
# self.pdata, "ModuleTypes", h2(module), "ChannelNumbers", ctype
|
|
268
|
+
# )
|
|
269
|
+
# if data and "Map" in data and h2(channel) in data["Map"]:
|
|
270
|
+
# return data["Map"][h2(channel)]
|
|
271
|
+
# if data and "Convert" in data:
|
|
272
|
+
# return int(channel)
|
|
273
|
+
# for offset in range(0, 8):
|
|
274
|
+
# if channel & (1 << offset):
|
|
275
|
+
# return offset + 1
|
|
276
|
+
# return None
|
velbusaio/message.py
CHANGED
|
@@ -20,19 +20,19 @@ class Message:
|
|
|
20
20
|
Base Velbus message
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
def __init__(self, address: int =
|
|
23
|
+
def __init__(self, address: int = 0) -> None:
|
|
24
24
|
self.priority = PRIORITY_LOW
|
|
25
|
-
self.address =
|
|
26
|
-
self.rtr = False
|
|
25
|
+
self.address: int = 0
|
|
26
|
+
self.rtr: bool = False
|
|
27
27
|
self.data = bytearray()
|
|
28
28
|
self.set_defaults(address)
|
|
29
29
|
|
|
30
|
-
def set_attributes(self, priority: int, address: int, rtr:
|
|
30
|
+
def set_attributes(self, priority: int, address: int, rtr: bool) -> None:
|
|
31
31
|
self.priority = priority
|
|
32
32
|
self.address = address
|
|
33
33
|
self.rtr = rtr
|
|
34
34
|
|
|
35
|
-
def populate(self, priority: int, address: int, rtr:
|
|
35
|
+
def populate(self, priority: int, address: int, rtr: bool, data: int) -> None:
|
|
36
36
|
raise NotImplementedError
|
|
37
37
|
|
|
38
38
|
def set_defaults(self, address: int | None) -> None:
|
|
@@ -66,7 +66,7 @@ class Message:
|
|
|
66
66
|
if callable(getattr(self, key)) or key.startswith("__"):
|
|
67
67
|
del me[key]
|
|
68
68
|
if isinstance(me[key], (bytes, bytearray)):
|
|
69
|
-
me[key] = str(me[key]
|
|
69
|
+
me[key] = str(me[key])
|
|
70
70
|
return me
|
|
71
71
|
|
|
72
72
|
def to_json(self) -> str:
|
|
@@ -25,7 +25,7 @@ class ModuleSubTypeMessage(Message):
|
|
|
25
25
|
|
|
26
26
|
# pylint: disable-msg=R0902
|
|
27
27
|
|
|
28
|
-
def __init__(self, address=None, sub_address_offset: int = 0):
|
|
28
|
+
def __init__(self, address=None, sub_address_offset: int = 0) -> None:
|
|
29
29
|
Message.__init__(self)
|
|
30
30
|
self.module_type = 0x00
|
|
31
31
|
self.sub_address_1 = 0xFF
|
|
@@ -36,13 +36,13 @@ class ModuleSubTypeMessage(Message):
|
|
|
36
36
|
self.serial = 0
|
|
37
37
|
self.sub_address_offset = sub_address_offset
|
|
38
38
|
|
|
39
|
-
def module_name(self):
|
|
39
|
+
def module_name(self) -> str:
|
|
40
40
|
"""
|
|
41
41
|
:return: str
|
|
42
42
|
"""
|
|
43
43
|
return "Unknown"
|
|
44
44
|
|
|
45
|
-
def populate(self, priority, address, rtr, data):
|
|
45
|
+
def populate(self, priority, address, rtr, data) -> None:
|
|
46
46
|
"""
|
|
47
47
|
:return: None
|
|
48
48
|
"""
|
|
@@ -98,7 +98,7 @@ class ModuleTypeMessage(Message):
|
|
|
98
98
|
|
|
99
99
|
# pylint: disable-msg=R0902
|
|
100
100
|
|
|
101
|
-
def __init__(self, address=None):
|
|
101
|
+
def __init__(self, address=None) -> None:
|
|
102
102
|
Message.__init__(self)
|
|
103
103
|
self.module_type = 0x00
|
|
104
104
|
self.led_on = []
|
|
@@ -110,13 +110,13 @@ class ModuleTypeMessage(Message):
|
|
|
110
110
|
self.build_week = 0
|
|
111
111
|
self.set_defaults(address)
|
|
112
112
|
|
|
113
|
-
def module_name(self):
|
|
113
|
+
def module_name(self) -> str:
|
|
114
114
|
"""
|
|
115
115
|
:return: str
|
|
116
116
|
"""
|
|
117
117
|
return "Unknown"
|
|
118
118
|
|
|
119
|
-
def populate(self, priority, address, rtr, data):
|
|
119
|
+
def populate(self, priority, address, rtr, data) -> None:
|
|
120
120
|
"""
|
|
121
121
|
:return: None
|
|
122
122
|
"""
|
|
@@ -151,7 +151,7 @@ class ModuleTypeMessage(Message):
|
|
|
151
151
|
],
|
|
152
152
|
)
|
|
153
153
|
class ModuleType2Message(Message):
|
|
154
|
-
def __init__(self, address=None):
|
|
154
|
+
def __init__(self, address=None) -> None:
|
|
155
155
|
Message.__init__(self)
|
|
156
156
|
self.module_type = 0x00
|
|
157
157
|
self.led_on = []
|
|
@@ -164,7 +164,7 @@ class ModuleType2Message(Message):
|
|
|
164
164
|
self.term = 0
|
|
165
165
|
self.set_defaults(address)
|
|
166
166
|
|
|
167
|
-
def module_name(self):
|
|
167
|
+
def module_name(self) -> str:
|
|
168
168
|
"""
|
|
169
169
|
:return: str
|
|
170
170
|
"""
|
velbusaio/module.py
CHANGED
|
@@ -9,6 +9,7 @@ import pathlib
|
|
|
9
9
|
import struct
|
|
10
10
|
import sys
|
|
11
11
|
import json
|
|
12
|
+
import os
|
|
12
13
|
from typing import Awaitable, Callable
|
|
13
14
|
|
|
14
15
|
from velbusaio.channels import (
|
|
@@ -252,6 +253,7 @@ class Module:
|
|
|
252
253
|
"""
|
|
253
254
|
Process received message
|
|
254
255
|
"""
|
|
256
|
+
self._log.debug(f"RX: {message}")
|
|
255
257
|
_channel_offset = self.calc_channel_offset(message.address)
|
|
256
258
|
|
|
257
259
|
if isinstance(
|
|
@@ -515,7 +517,7 @@ class Module:
|
|
|
515
517
|
try:
|
|
516
518
|
await self._channels[channel].update(updates)
|
|
517
519
|
except KeyError:
|
|
518
|
-
self._log.
|
|
520
|
+
self._log.info(
|
|
519
521
|
f"channel {channel} does not exist for module @ address {self}"
|
|
520
522
|
)
|
|
521
523
|
|
|
@@ -526,18 +528,6 @@ class Module:
|
|
|
526
528
|
return self._channels
|
|
527
529
|
|
|
528
530
|
async def load(self, from_cache: bool = False) -> None:
|
|
529
|
-
"""
|
|
530
|
-
Retrieve names of channels
|
|
531
|
-
"""
|
|
532
|
-
# did we already start the loading?
|
|
533
|
-
# this is needed for the submodules,
|
|
534
|
-
# as the submodule address maps to the main module
|
|
535
|
-
# this method can be called multiple times
|
|
536
|
-
if self._is_loading or self.loaded:
|
|
537
|
-
if from_cache:
|
|
538
|
-
await self._request_module_status()
|
|
539
|
-
return
|
|
540
|
-
self._log.info("Load Module")
|
|
541
531
|
# start the loading
|
|
542
532
|
self._is_loading = True
|
|
543
533
|
# see if we have a cache
|
|
@@ -548,14 +538,15 @@ class Module:
|
|
|
548
538
|
except OSError:
|
|
549
539
|
cache = {}
|
|
550
540
|
# load default channels
|
|
551
|
-
await self.
|
|
541
|
+
await self.__load_default_channels()
|
|
542
|
+
|
|
552
543
|
# load the data from memory ( the stuff that we need)
|
|
553
544
|
if "name" in cache and cache["name"] != "":
|
|
554
545
|
self._name = cache["name"]
|
|
555
546
|
else:
|
|
556
547
|
await self.__load_memory()
|
|
557
548
|
# load the module status
|
|
558
|
-
await self._request_module_status()
|
|
549
|
+
# await self._request_module_status()
|
|
559
550
|
# load the channel names
|
|
560
551
|
if "channels" in cache:
|
|
561
552
|
for num, chan in cache["channels"].items():
|
|
@@ -569,6 +560,7 @@ class Module:
|
|
|
569
560
|
self._load()
|
|
570
561
|
# stop the loading
|
|
571
562
|
self._is_loading = False
|
|
563
|
+
await self._request_module_status()
|
|
572
564
|
|
|
573
565
|
def _load(self) -> None:
|
|
574
566
|
"""
|
|
@@ -674,6 +666,8 @@ class Module:
|
|
|
674
666
|
if "Channels" not in self._data:
|
|
675
667
|
# some modules have no channels
|
|
676
668
|
return
|
|
669
|
+
self._log.info(f"Request module status {self._address}")
|
|
670
|
+
|
|
677
671
|
mod_stat_req_msg = ModuleStatusRequestMessage(self._address)
|
|
678
672
|
counter_msg = None
|
|
679
673
|
for chan, chan_data in self._data["Channels"].items():
|
|
@@ -728,7 +722,7 @@ class Module:
|
|
|
728
722
|
msg.low_address = addr[1]
|
|
729
723
|
await self._writer(msg)
|
|
730
724
|
|
|
731
|
-
async def
|
|
725
|
+
async def __load_default_channels(self) -> None:
|
|
732
726
|
if "Channels" not in self._data:
|
|
733
727
|
return
|
|
734
728
|
|
|
@@ -777,6 +771,7 @@ class VmbDali(Module):
|
|
|
777
771
|
self.group_members: dict[int, set[int]] = {}
|
|
778
772
|
|
|
779
773
|
async def _load_default_channels(self) -> None:
|
|
774
|
+
await super().load()
|
|
780
775
|
for chan in range(1, 64 + 1):
|
|
781
776
|
self._channels[chan] = Channel(
|
|
782
777
|
self, chan, "placeholder", True, self._writer, self._address
|
velbusaio/protocol.json
CHANGED
|
@@ -9045,6 +9045,74 @@
|
|
|
9045
9045
|
"Type": "SelectedProgram"
|
|
9046
9046
|
}
|
|
9047
9047
|
},
|
|
9048
|
+
"Memory": {
|
|
9049
|
+
"Address": {
|
|
9050
|
+
"049C": { "ModuleName": "0:Start" },
|
|
9051
|
+
"049D": { "ModuleName": "1" },
|
|
9052
|
+
"049E": { "ModuleName": "2" },
|
|
9053
|
+
"049F": { "ModuleName": "3" },
|
|
9054
|
+
"04A0": { "ModuleName": "4" },
|
|
9055
|
+
"04A1": { "ModuleName": "5" },
|
|
9056
|
+
"04A2": { "ModuleName": "6" },
|
|
9057
|
+
"04A3": { "ModuleName": "7" },
|
|
9058
|
+
"04A4": { "ModuleName": "8" },
|
|
9059
|
+
"04A5": { "ModuleName": "9" },
|
|
9060
|
+
"04A6": { "ModuleName": "10" },
|
|
9061
|
+
"04A7": { "ModuleName": "11" },
|
|
9062
|
+
"04A8": { "ModuleName": "12" },
|
|
9063
|
+
"04A9": { "ModuleName": "13" },
|
|
9064
|
+
"04AA": { "ModuleName": "14" },
|
|
9065
|
+
"04AB": { "ModuleName": "15" },
|
|
9066
|
+
"04AC": { "ModuleName": "16" },
|
|
9067
|
+
"04AD": { "ModuleName": "17" },
|
|
9068
|
+
"04AE": { "ModuleName": "18" },
|
|
9069
|
+
"04AF": { "ModuleName": "19" },
|
|
9070
|
+
"04B0": { "ModuleName": "20" },
|
|
9071
|
+
"04B1": { "ModuleName": "21" },
|
|
9072
|
+
"04B2": { "ModuleName": "22" },
|
|
9073
|
+
"04B3": { "ModuleName": "23" },
|
|
9074
|
+
"04B4": { "ModuleName": "24" },
|
|
9075
|
+
"04B5": { "ModuleName": "25" },
|
|
9076
|
+
"04B6": { "ModuleName": "26" },
|
|
9077
|
+
"04B7": { "ModuleName": "27" },
|
|
9078
|
+
"04B8": { "ModuleName": "28" },
|
|
9079
|
+
"04B9": { "ModuleName": "29" },
|
|
9080
|
+
"04BA": { "ModuleName": "30" },
|
|
9081
|
+
"04BB": { "ModuleName": "31" },
|
|
9082
|
+
"04BC": { "ModuleName": "32" },
|
|
9083
|
+
"04BD": { "ModuleName": "33" },
|
|
9084
|
+
"04BE": { "ModuleName": "34" },
|
|
9085
|
+
"04BF": { "ModuleName": "35" },
|
|
9086
|
+
"04C0": { "ModuleName": "36" },
|
|
9087
|
+
"04C1": { "ModuleName": "37" },
|
|
9088
|
+
"04C2": { "ModuleName": "38" },
|
|
9089
|
+
"04C3": { "ModuleName": "39" },
|
|
9090
|
+
"04C4": { "ModuleName": "40" },
|
|
9091
|
+
"04C5": { "ModuleName": "41" },
|
|
9092
|
+
"04C6": { "ModuleName": "42" },
|
|
9093
|
+
"04C7": { "ModuleName": "43" },
|
|
9094
|
+
"04C8": { "ModuleName": "44" },
|
|
9095
|
+
"04C9": { "ModuleName": "45" },
|
|
9096
|
+
"04CA": { "ModuleName": "46" },
|
|
9097
|
+
"04CB": { "ModuleName": "47" },
|
|
9098
|
+
"04CC": { "ModuleName": "48" },
|
|
9099
|
+
"04CD": { "ModuleName": "49" },
|
|
9100
|
+
"04CE": { "ModuleName": "50" },
|
|
9101
|
+
"04CF": { "ModuleName": "51" },
|
|
9102
|
+
"04D0": { "ModuleName": "52" },
|
|
9103
|
+
"04D1": { "ModuleName": "53" },
|
|
9104
|
+
"04D2": { "ModuleName": "54" },
|
|
9105
|
+
"04D3": { "ModuleName": "55" },
|
|
9106
|
+
"04D4": { "ModuleName": "56" },
|
|
9107
|
+
"04D5": { "ModuleName": "57" },
|
|
9108
|
+
"04D6": { "ModuleName": "58" },
|
|
9109
|
+
"04D7": { "ModuleName": "59" },
|
|
9110
|
+
"04D8": { "ModuleName": "60" },
|
|
9111
|
+
"04D9": { "ModuleName": "61" },
|
|
9112
|
+
"04DA": { "ModuleName": "62" },
|
|
9113
|
+
"04DB": { "ModuleName": "64:Save" }
|
|
9114
|
+
}
|
|
9115
|
+
},
|
|
9048
9116
|
"Info": "8 channel 0 to 10 V dimmer control module",
|
|
9049
9117
|
"Type": "VMB8DC-20"
|
|
9050
9118
|
},
|
velbusaio/protocol.py
CHANGED
|
@@ -15,9 +15,7 @@ from velbusaio.raw_message import create as create_message_info
|
|
|
15
15
|
|
|
16
16
|
def _on_write_backoff(details):
|
|
17
17
|
logging.debug(
|
|
18
|
-
"Transport is not open, waiting {wait} seconds after {tries}"
|
|
19
|
-
wait=details.wait,
|
|
20
|
-
tries=details.tries,
|
|
18
|
+
f"Transport is not open, waiting {details.wait} seconds after {details.tries}"
|
|
21
19
|
)
|
|
22
20
|
|
|
23
21
|
|
|
@@ -31,13 +29,11 @@ class VelbusProtocol(asyncio.BufferedProtocol):
|
|
|
31
29
|
self,
|
|
32
30
|
message_received_callback: t.Callable[[RawMessage], t.Awaitable[None]],
|
|
33
31
|
connection_lost_callback=None,
|
|
34
|
-
end_of_scan_callback=None,
|
|
35
32
|
) -> None:
|
|
36
33
|
super().__init__()
|
|
37
34
|
self._log = logging.getLogger("velbus-protocol")
|
|
38
35
|
self._message_received_callback = message_received_callback
|
|
39
36
|
self._connection_lost_callback = connection_lost_callback
|
|
40
|
-
self._end_of_scan_callback = end_of_scan_callback
|
|
41
37
|
|
|
42
38
|
# everything for reading from Velbus
|
|
43
39
|
self._buffer = bytearray(MAXIMUM_MESSAGE_SIZE)
|
|
@@ -63,14 +59,14 @@ class VelbusProtocol(asyncio.BufferedProtocol):
|
|
|
63
59
|
self._restart_writer = True
|
|
64
60
|
self.restart_writing()
|
|
65
61
|
|
|
66
|
-
async def pause_writing(self):
|
|
62
|
+
async def pause_writing(self) -> None:
|
|
67
63
|
"""Pause writing."""
|
|
68
64
|
self._restart_writer = False
|
|
69
65
|
if self._writer_task:
|
|
70
66
|
self._send_queue.put_nowait(None)
|
|
71
67
|
await asyncio.sleep(0.1)
|
|
72
68
|
|
|
73
|
-
def restart_writing(self):
|
|
69
|
+
def restart_writing(self) -> None:
|
|
74
70
|
"""Resume writing."""
|
|
75
71
|
if self._restart_writer and not self._write_transport_lock.locked():
|
|
76
72
|
self._writer_task = asyncio.ensure_future(
|
|
@@ -78,7 +74,7 @@ class VelbusProtocol(asyncio.BufferedProtocol):
|
|
|
78
74
|
)
|
|
79
75
|
self._writer_task.add_done_callback(lambda _future: self.restart_writing())
|
|
80
76
|
|
|
81
|
-
def close(self):
|
|
77
|
+
def close(self) -> None:
|
|
82
78
|
self._closing = True
|
|
83
79
|
self._restart_writer = False
|
|
84
80
|
if self.transport:
|
|
@@ -197,8 +193,6 @@ class VelbusProtocol(asyncio.BufferedProtocol):
|
|
|
197
193
|
queue_sleep_time = SLEEP_TIME * 33
|
|
198
194
|
else:
|
|
199
195
|
queue_sleep_time = SLEEP_TIME
|
|
200
|
-
if msg_info.rtr and msg_info.address == 0xFF:
|
|
201
|
-
self._end_of_scan_callback()
|
|
202
196
|
await asyncio.sleep(queue_sleep_time)
|
|
203
197
|
except (asyncio.CancelledError, GeneratorExit) as exc:
|
|
204
198
|
if not self._closing:
|
|
File without changes
|
|
File without changes
|