velbus-aio 2024.4.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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: velbus-aio
3
- Version: 2024.4.1
3
+ Version: 2024.7.0
4
4
  Summary: Open-source home automation platform running on Python 3.
5
5
  Author-email: Maikel Punie <maikel.punie@gmail.com>
6
6
  License: MIT
@@ -26,7 +26,7 @@ Requires-Python: >=3.8.0
26
26
  Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
28
  Requires-Dist: pyserial >=3.5.0
29
- Requires-Dist: pyserial-asyncio >=0.5
29
+ Requires-Dist: pyserial-asyncio-fast >=0.11
30
30
  Requires-Dist: backoff >=1.10.0
31
31
 
32
32
  ![CI](https://github.com/Cereal2nd/velbus-aio/actions/workflows/main.yml/badge.svg)
@@ -1,16 +1,16 @@
1
1
  velbusaio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- velbusaio/channels.py,sha256=v7hZoT7X3-fNBAvTKgVpxCY33HhUC9Ga4XTCMqJ3dTA,22799
2
+ velbusaio/channels.py,sha256=YdIMneLYj_zTa8e2dHmhZvGKcD3iyv5jMeZpfuORcTU,22910
3
3
  velbusaio/command_registry.py,sha256=j9KuLmada41PMEdtfPvLmXVOAZOkBM9zi-3kluwYffk,5140
4
- velbusaio/const.py,sha256=aysTlQcpACZaDVqoXAjWmaEDGlCB7KT04Rv-SAusPFc,1575
5
- velbusaio/controller.py,sha256=O9BAOFTb3GjTRX8EOrkIifqWN9GIp9Pm4x6__m5wa7A,8756
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=jTWIV26ibAqs3rcTku81NIHlPUeyWjStSih1Nfx-an0,5946
8
+ velbusaio/handler.py,sha256=UF-F2LeVEocDniXKLE8FmUMO-o9QdRtDQoYog6d4rag,10867
9
9
  velbusaio/helpers.py,sha256=iqpoereRH4JY5WAkozIqWvXWyRmhko-J-UGXDylFyEM,2537
10
- velbusaio/message.py,sha256=gnhXpA5NAG4qnI9vlxwlWQKETWneDb1lp0P9w_-nxhQ,5020
11
- velbusaio/module.py,sha256=sHOnGjuV--_aw83yh_Gx5CiRPDOBRr1PemVg5z5YYoQ,35467
12
- velbusaio/protocol.json,sha256=7PifRZXctU5zSsodDGsUAhnnSsGNlcGcim1gr2su720,250396
13
- velbusaio/protocol.py,sha256=ofDwJfwyrVQDQ40WX2QdH1nTK-y2HibQPTo5dFY7SkQ,8224
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=VQXQ-c_yFbj9_64SjfiM-PLk1hV7xUWIcQ1DZYTpKhI,1464
55
- velbusaio/messages/module_type.py,sha256=toHlsLoek61cQ9JXG3alJyAuQtny1RSY7K6cM4hfNOk,4116
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
@@ -69,7 +69,7 @@ velbusaio/messages/sensor_temp_request.py,sha256=bTChj9udOjIww9vJ2SWmMtbry_8jLDv
69
69
  velbusaio/messages/sensor_temperature.py,sha256=ThA_4FFhQutCSt8147yCy7ocUFfizWaUV2JHoNzesNc,1558
70
70
  velbusaio/messages/set_date.py,sha256=jcWb8c5mB71HNBwLCQ9ocEPSHQdZP0yfGJL4_swsRWQ,1359
71
71
  velbusaio/messages/set_daylight_saving.py,sha256=CpoKrSt62UzzNxl8SDcpDcb0GKnLcD1f73-ULzqDEzU,1049
72
- velbusaio/messages/set_dimmer.py,sha256=f2XWLbxoo0Tbj8_DUu28XSiib32VLUDtw8QRzRZBDdA,1975
72
+ velbusaio/messages/set_dimmer.py,sha256=b32lBHrvZRQ7kKIFhQwuBqk9rXagnBtcJxo1V7V5x_0,2050
73
73
  velbusaio/messages/set_led.py,sha256=X8tIDzJgtf5EsH92rjoUyrECNFW-ECgAQoQ9TnvJEQk,900
74
74
  velbusaio/messages/set_realtime_clock.py,sha256=h34pCvyPm7tSQ5BdB8yC8SRulrtldeX-ZJFv3Wcvdk0,1209
75
75
  velbusaio/messages/set_temperature.py,sha256=78LfGOBZo2bw7eF38uyXOjUcJrsSvQPDW2qbXLOYd0o,959
@@ -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.4.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
100
- velbus_aio-2024.4.1.dist-info/METADATA,sha256=gQoruLEsogCEK-x7B7ls4F_zLud9p4LJDDiPv2rreJ8,3253
101
- velbus_aio-2024.4.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
102
- velbus_aio-2024.4.1.dist-info/top_level.txt,sha256=W0-lSOwD23mm8FqaIe9vY20fKicBMIdUVjF-zmfxRnY,15
103
- velbus_aio-2024.4.1.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (70.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
velbusaio/channels.py CHANGED
@@ -137,7 +137,10 @@ class Channel:
137
137
  }
138
138
 
139
139
  def to_cache(self) -> dict:
140
- return {"name": self._name, "type": type(self).__name__}
140
+ dst = {"name": self._name, "type": type(self).__name__}
141
+ if hasattr(self, "_Unit"):
142
+ dst["Unit"] = self._Unit
143
+ return dst
141
144
 
142
145
  def __setstate__(self, state):
143
146
  self.__dict__.update(state)
@@ -505,6 +508,7 @@ class Temperature(Channel):
505
508
  _min = None
506
509
  _target = 0
507
510
  _cmode = None
511
+ _coolmode = None
508
512
  _cstatus = None
509
513
  _thermostat = False
510
514
  _sleep_timer = 0
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
- LOAD_TIMEOUT: Final = 600
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
@@ -11,10 +11,9 @@ import time
11
11
  from urllib.parse import urlparse
12
12
 
13
13
  import serial
14
- import serial_asyncio
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.send, 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 _on_end_of_scan(self) -> None:
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
- self._log.info(f"Found module: type:{typ} address:{addr}")
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
- self._modules[addr].initialize(self.send)
96
- await self._modules[addr].load()
88
+ module.initialize(self.send)
89
+ self._modules[addr] = module
90
+ self._log.info(f"Found module {addr}: {module}")
97
91
 
98
- async def add_submodules(self, addr: int, subList: dict[int, int]) -> None:
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
- self._modules[addr]._sub_address[sub_num] = sub_addr
105
- self._modules[sub_addr] = self._modules[addr]
106
- self._modules[addr].cleanupSubChannels()
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: str) -> None | Module:
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: str) -> None | dict:
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:
@@ -159,16 +154,18 @@ class Velbus:
159
154
  else:
160
155
  # serial port
161
156
  try:
162
- _transport, _protocol = await serial_asyncio.create_serial_connection(
163
- asyncio.get_event_loop(),
164
- lambda: self._protocol,
165
- url=self._dsn,
166
- baudrate=38400,
167
- bytesize=serial.EIGHTBITS,
168
- parity=serial.PARITY_NONE,
169
- stopbits=serial.STOPBITS_ONE,
170
- xonxoff=0,
171
- rtscts=1,
157
+ _transport, _protocol = (
158
+ await serial_asyncio_fast.create_serial_connection(
159
+ asyncio.get_event_loop(),
160
+ lambda: self._protocol,
161
+ url=self._dsn,
162
+ baudrate=38400,
163
+ bytesize=serial.EIGHTBITS,
164
+ parity=serial.PARITY_NONE,
165
+ stopbits=serial.STOPBITS_ONE,
166
+ xonxoff=0,
167
+ rtscts=1,
168
+ )
172
169
  )
173
170
  except (FileNotFoundError, serial.SerialException) as err:
174
171
  raise VelbusConnectionFailed from err
@@ -179,44 +176,15 @@ class Velbus:
179
176
  await self._protocol.write_auth_key(auth)
180
177
 
181
178
  # scan the bus
182
- await self.scan()
179
+ await self._handler.scan()
183
180
 
184
181
  async def scan(self) -> None:
185
- """Scan the bus."""
186
- self._handler.scan_started()
187
- for addr in range(1, 256):
188
- msg = ModuleTypeRequestMessage(addr)
189
- await self.send(msg)
190
- await self._handler._scan_complete_event.wait()
191
- # calculate how long to wait
192
- calc_timeout = len(self._modules) * 30
193
- if calc_timeout < LOAD_TIMEOUT:
194
- timeout = calc_timeout
195
- else:
196
- timeout = LOAD_TIMEOUT
197
- # create a task to wait until we have all modules loaded
198
- tsk = asyncio.Task(self._check_if_modules_are_loaded())
199
- try:
200
- await asyncio.wait_for(tsk, timeout=timeout)
201
- except asyncio.TimeoutError:
202
- self._log.error(
203
- f"Not all modules are loaded within a timeout of {LOAD_TIMEOUT} seconds, continuing with the loaded modules"
204
- )
182
+ """Service endpoint to restart the scan"""
183
+ await self._handler.scan(True)
205
184
 
206
- async def _check_if_modules_are_loaded(self) -> None:
207
- """Task to wait until modules are loaded."""
208
- while True:
209
- mods_loaded = 0
210
- for mod in (self.get_modules()).values():
211
- if mod.is_loaded():
212
- mods_loaded += 1
213
- else:
214
- self._log.warning(f"Waiting for module {mod._address}")
215
- if mods_loaded == len(self.get_modules()):
216
- self._log.info("All modules loaded")
217
- return
218
- self._log.info("Not all modules loaded yet, waiting 15 seconds")
219
- await asyncio.sleep(15)
185
+ async def sendTypeRequestMessage(self, address: int) -> None:
186
+ msg = ModuleTypeRequestMessage(address)
187
+ await self.send(msg)
220
188
 
221
189
  async def send(self, msg: Message) -> None:
222
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-packet")
36
- self._writer = writer
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._scan_complete_event = asyncio.Event()
40
- with open(
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.load(protocol_file)
59
+ self.pdata = json.loads(await protocol_file.read())
44
60
 
45
- def scan_finished(self) -> None:
46
- self._scan_complete = True
47
- self._scan_complete_event.set()
48
- self._log.debug("Scan complete")
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 scan_started(self) -> None:
51
- self._scan_complete = False
52
- self._scan_complete_event.clear()
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
- if command_value == 0xFF and not self._scan_complete:
70
- msg = ModuleTypeMessage()
71
- msg.populate(priority, address, rtr, data)
72
- self._log.debug(f"Received {msg}")
73
- await self._handle_module_type(msg)
74
- elif command_value in (0xB0, 0xA7, 0xA6) and not self._scan_complete:
75
- msg = ModuleSubTypeMessage()
76
- msg.populate(priority, address, rtr, data)
77
-
78
- if command_value == 0xB0:
79
- msg.sub_address_offset = 0
80
- elif command_value == 0xA7:
81
- msg.sub_address_offset = 4
82
- elif command_value == 0xA6:
83
- msg.sub_address_offset = 8
84
- else:
85
- raise RuntimeError("Unreachable code reached => bug here")
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
- self._log.debug(f"Received {msg}")
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
- async def _handle_module_type(self, msg: Message) -> None:
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
- data = keys_exists(self.pdata, "ModuleTypes", h2(msg.module_type))
127
- if not data:
128
- self._log.warning(f"Module not recognized: {msg.module_type}")
129
- return
130
- # create the module
131
- await self._velbus.add_module(
132
- msg.address,
133
- msg.module_type,
134
- data,
135
- memorymap=msg.memory_map_version,
136
- build_year=msg.build_year,
137
- build_week=msg.build_week,
138
- serial=msg.serial,
139
- )
140
-
141
- async def _handle_module_subtype(self, msg: Message) -> None:
142
- if msg.address not in self._velbus.get_modules():
143
- return
144
- addrList = {
145
- (msg.sub_address_offset + 1): msg.sub_address_1,
146
- (msg.sub_address_offset + 2): msg.sub_address_2,
147
- (msg.sub_address_offset + 3): msg.sub_address_3,
148
- (msg.sub_address_offset + 4): msg.sub_address_4,
149
- }
150
- await self._velbus.add_submodules(msg.address, addrList)
151
-
152
- def _channel_convert(self, module: str, channel: str, ctype: str) -> None | int:
153
- data = keys_exists(
154
- self.pdata, "ModuleTypes", h2(module), "ChannelNumbers", ctype
155
- )
156
- if data and "Map" in data and h2(channel) in data["Map"]:
157
- return data["Map"][h2(channel)]
158
- if data and "Convert" in data:
159
- return int(channel)
160
- for offset in range(0, 8):
161
- if channel & (1 << offset):
162
- return offset + 1
163
- return None
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 = None) -> None:
23
+ def __init__(self, address: int = 0) -> None:
24
24
  self.priority = PRIORITY_LOW
25
- self.address = None
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: int) -> None:
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: int, data: int) -> None:
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], "utf-8")
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
  """
@@ -1,5 +1,4 @@
1
- """
2
- :author: Frank van Breugel
1
+ """:author: Frank van Breugel
3
2
  """
4
3
 
5
4
  from __future__ import annotations
@@ -12,10 +11,10 @@ COMMAND_CODE = 0x07
12
11
 
13
12
  @register(
14
13
  COMMAND_CODE,
15
- ["VMB1DM", "VMBDME", "VMB4DC", "VMBDMI", "VMBDMI-R", "VMB1LED", "VMB8DC-20"],
14
+ ["VMB1DM", "VMBDME", "VMB4DC", "VMB1LED"],
16
15
  )
17
16
  class SetDimmerMessage(Message):
18
- """
17
+ """with this message the channel numbering is a bitnumber
19
18
  send by:
20
19
  received by: VMBDME, VMB4DC
21
20
  """
@@ -34,9 +33,7 @@ class SetDimmerMessage(Message):
34
33
  self.set_no_rtr()
35
34
 
36
35
  def populate(self, priority, address, rtr, data):
37
- """
38
- :return: None
39
- """
36
+ """:return: None"""
40
37
  self.needs_high_priority(priority)
41
38
  self.needs_no_rtr(rtr)
42
39
  self.needs_data(data, 4)
@@ -48,9 +45,7 @@ class SetDimmerMessage(Message):
48
45
  )
49
46
 
50
47
  def data_to_binary(self):
51
- """
52
- :return: bytes
53
- """
48
+ """:return: bytes"""
54
49
  return bytes(
55
50
  [
56
51
  COMMAND_CODE,
@@ -60,9 +55,9 @@ class SetDimmerMessage(Message):
60
55
  ) + self.dimmer_transitiontime.to_bytes(2, byteorder="big", signed=False)
61
56
 
62
57
 
63
- @register(COMMAND_CODE, ["VMBDALI", "VMBDALI-20"])
58
+ @register(COMMAND_CODE, ["VMBDALI", "VMBDALI-20", "VMBDMI", "VMBDMI-R", "VMB8DC-20"])
64
59
  class SetDimmerMessage2(SetDimmerMessage):
65
- """
60
+ """This with this message the channel numbering is an integer
66
61
  send by:
67
62
  received by: VMBDALI
68
63
  """
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.error(
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
@@ -549,17 +539,20 @@ class Module:
549
539
  cache = {}
550
540
  # load default channels
551
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():
562
553
  self._channels[int(num)]._name = chan["name"]
554
+ if "Unit" in chan:
555
+ self._channels[int(num)]._Unit = chan["Unit"]
563
556
  self._channels[int(num)]._is_loaded = True
564
557
  else:
565
558
  await self._request_channel_name()
@@ -567,6 +560,7 @@ class Module:
567
560
  self._load()
568
561
  # stop the loading
569
562
  self._is_loading = False
563
+ await self._request_module_status()
570
564
 
571
565
  def _load(self) -> None:
572
566
  """
@@ -672,6 +666,8 @@ class Module:
672
666
  if "Channels" not in self._data:
673
667
  # some modules have no channels
674
668
  return
669
+ self._log.info(f"Request module status {self._address}")
670
+
675
671
  mod_stat_req_msg = ModuleStatusRequestMessage(self._address)
676
672
  counter_msg = None
677
673
  for chan, chan_data in self._data["Channels"].items():
@@ -774,19 +770,16 @@ class VmbDali(Module):
774
770
  )
775
771
  self.group_members: dict[int, set[int]] = {}
776
772
 
777
- async def load(self, from_cache: bool = False) -> None:
778
- await super().load(from_cache)
779
-
780
- if not from_cache:
781
- for chan in range(1, 64 + 1):
782
- self._channels[chan] = Channel(
783
- self, chan, "placeholder", True, self._writer, self._address
784
- )
785
- # Placeholders will keep this module loading
786
- # Until the DaliDeviceSettings messages either delete or replace these placeholder's
787
- # with actual channels
788
-
789
- await self._request_dali_channels()
773
+ async def _load_default_channels(self) -> None:
774
+ await super().load()
775
+ for chan in range(1, 64 + 1):
776
+ self._channels[chan] = Channel(
777
+ self, chan, "placeholder", True, self._writer, self._address
778
+ )
779
+ # Placeholders will keep this module loading
780
+ # Until the DaliDeviceSettings messages either delete or replace these placeholder's
781
+ # with actual channels
782
+ await self._request_dali_channels()
790
783
 
791
784
  async def _request_dali_channels(self):
792
785
  msg_type = commandRegistry.get_command(
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: