velbus-aio 2023.12.0__py3-none-any.whl → 2025.11.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.
- scripts/parse_specs.py +156 -0
- {velbus_aio-2023.12.0.dist-info → velbus_aio-2025.11.0.dist-info}/METADATA +24 -8
- velbus_aio-2025.11.0.dist-info/RECORD +194 -0
- {velbus_aio-2023.12.0.dist-info → velbus_aio-2025.11.0.dist-info}/WHEEL +1 -1
- {velbus_aio-2023.12.0.dist-info → velbus_aio-2025.11.0.dist-info}/top_level.txt +1 -0
- velbusaio/channels.py +35 -14
- velbusaio/command_registry.py +32 -5
- velbusaio/const.py +10 -2
- velbusaio/controller.py +183 -145
- velbusaio/handler.py +286 -154
- velbusaio/helpers.py +2 -1
- velbusaio/message.py +14 -8
- velbusaio/messages/__init__.py +6 -2
- velbusaio/messages/blind_status.py +4 -2
- velbusaio/messages/bus_active.py +1 -0
- velbusaio/messages/bus_error_counter_status.py +1 -0
- velbusaio/messages/bus_error_counter_status_request.py +1 -0
- velbusaio/messages/bus_off.py +1 -0
- velbusaio/messages/channel_name_part1.py +18 -0
- velbusaio/messages/channel_name_part2.py +18 -0
- velbusaio/messages/channel_name_part3.py +18 -0
- velbusaio/messages/channel_name_request.py +2 -1
- velbusaio/messages/clear_led.py +1 -0
- velbusaio/messages/counter_status.py +1 -0
- velbusaio/messages/counter_status_request.py +1 -0
- velbusaio/messages/counter_value.py +44 -0
- velbusaio/messages/cover_down.py +2 -1
- velbusaio/messages/cover_off.py +2 -3
- velbusaio/messages/cover_position.py +3 -4
- velbusaio/messages/cover_up.py +2 -1
- velbusaio/messages/dali_device_settings.py +2 -1
- velbusaio/messages/dali_device_settings_request.py +2 -1
- velbusaio/messages/dali_dim_value_status.py +4 -1
- velbusaio/messages/dimmer_channel_status.py +5 -1
- velbusaio/messages/dimmer_status.py +13 -1
- velbusaio/messages/edge_set_color.py +1 -0
- velbusaio/messages/edge_set_custom_color.py +1 -0
- velbusaio/messages/fast_blinking_led.py +1 -0
- velbusaio/messages/forced_off.py +1 -0
- velbusaio/messages/forced_on.py +1 -0
- velbusaio/messages/interface_status_request.py +1 -0
- velbusaio/messages/ir_receiver_status.py +1 -0
- velbusaio/messages/kwh_status.py +1 -0
- velbusaio/messages/light_value_request.py +1 -0
- velbusaio/messages/memo_text.py +1 -0
- velbusaio/messages/memory_data.py +1 -0
- velbusaio/messages/memory_data_block.py +1 -0
- velbusaio/messages/memory_dump_request.py +1 -0
- velbusaio/messages/module_status.py +13 -0
- velbusaio/messages/module_status_request.py +5 -2
- velbusaio/messages/module_subtype.py +4 -3
- velbusaio/messages/module_type.py +17 -7
- velbusaio/messages/module_type_request.py +1 -0
- velbusaio/messages/psu_load.py +56 -0
- velbusaio/messages/psu_values.py +53 -0
- velbusaio/messages/push_button_status.py +1 -0
- velbusaio/messages/raw.py +1 -0
- velbusaio/messages/read_data_block_from_memory.py +1 -0
- velbusaio/messages/read_data_from_memory.py +1 -0
- velbusaio/messages/realtime_clock_status_request.py +1 -0
- velbusaio/messages/receive_buffer_full.py +1 -0
- velbusaio/messages/receive_ready.py +1 -0
- velbusaio/messages/relay_status.py +1 -0
- velbusaio/messages/restore_dimmer.py +16 -2
- velbusaio/messages/select_program.py +1 -0
- velbusaio/messages/sensor_settings_request.py +1 -0
- velbusaio/messages/sensor_temp_request.py +1 -0
- velbusaio/messages/sensor_temperature.py +1 -0
- velbusaio/messages/set_date.py +5 -10
- velbusaio/messages/set_daylight_saving.py +3 -6
- velbusaio/messages/set_dimmer.py +22 -13
- velbusaio/messages/set_led.py +1 -0
- velbusaio/messages/set_realtime_clock.py +5 -10
- velbusaio/messages/set_temperature.py +1 -0
- velbusaio/messages/slider_status.py +15 -1
- velbusaio/messages/slow_blinking_led.py +1 -0
- velbusaio/messages/start_relay_blinking_timer.py +1 -0
- velbusaio/messages/start_relay_timer.py +1 -0
- velbusaio/messages/switch_relay_off.py +1 -0
- velbusaio/messages/switch_relay_on.py +1 -0
- velbusaio/messages/switch_to_comfort.py +1 -0
- velbusaio/messages/switch_to_day.py +1 -0
- velbusaio/messages/switch_to_night.py +1 -0
- velbusaio/messages/switch_to_safe.py +1 -0
- velbusaio/messages/temp_sensor_settings_part1.py +1 -0
- velbusaio/messages/temp_sensor_settings_part2.py +1 -0
- velbusaio/messages/temp_sensor_settings_part3.py +1 -0
- velbusaio/messages/temp_sensor_settings_part4.py +1 -0
- velbusaio/messages/temp_sensor_settings_request.py +1 -0
- velbusaio/messages/temp_sensor_status.py +1 -0
- velbusaio/messages/temp_set_cooling.py +1 -0
- velbusaio/messages/temp_set_heating.py +1 -0
- velbusaio/messages/update_led_status.py +1 -0
- velbusaio/messages/very_fast_blinking_led.py +1 -0
- velbusaio/messages/write_data_to_memory.py +1 -0
- velbusaio/messages/write_memory_block.py +1 -0
- velbusaio/messages/write_module_address_and_serial_number.py +1 -0
- velbusaio/module.py +214 -102
- velbusaio/module_spec/01.json +62 -0
- velbusaio/module_spec/02.json +16 -0
- velbusaio/module_spec/03.json +23 -0
- velbusaio/module_spec/04.json +283 -0
- velbusaio/module_spec/05.json +54 -0
- velbusaio/module_spec/06.json +110 -0
- velbusaio/module_spec/07.json +16 -0
- velbusaio/module_spec/08.json +38 -0
- velbusaio/module_spec/09.json +30 -0
- velbusaio/module_spec/0A.json +58 -0
- velbusaio/module_spec/0B.json +58 -0
- velbusaio/module_spec/0C.json +18 -0
- velbusaio/module_spec/0E.json +25 -0
- velbusaio/module_spec/0F.json +16 -0
- velbusaio/module_spec/10.json +111 -0
- velbusaio/module_spec/11.json +111 -0
- velbusaio/module_spec/12.json +73 -0
- velbusaio/module_spec/13.json +4 -0
- velbusaio/module_spec/14.json +16 -0
- velbusaio/module_spec/15.json +83 -0
- velbusaio/module_spec/16.json +129 -0
- velbusaio/module_spec/17.json +129 -0
- velbusaio/module_spec/18.json +129 -0
- velbusaio/module_spec/1A.json +79 -0
- velbusaio/module_spec/1B.json +107 -0
- velbusaio/module_spec/1D.json +89 -0
- velbusaio/module_spec/1E.json +306 -0
- velbusaio/module_spec/1F.json +178 -0
- velbusaio/module_spec/20.json +178 -0
- velbusaio/module_spec/21.json +326 -0
- velbusaio/module_spec/22.json +426 -0
- velbusaio/module_spec/23.json +129 -0
- velbusaio/module_spec/24.json +30 -0
- velbusaio/module_spec/25.json +3 -0
- velbusaio/module_spec/28.json +454 -0
- velbusaio/module_spec/29.json +235 -0
- velbusaio/module_spec/2A.json +239 -0
- velbusaio/module_spec/2B.json +239 -0
- velbusaio/module_spec/2C.json +257 -0
- velbusaio/module_spec/2D.json +270 -0
- velbusaio/module_spec/2E.json +215 -0
- velbusaio/module_spec/2F.json +211 -0
- velbusaio/module_spec/30.json +58 -0
- velbusaio/module_spec/31.json +465 -0
- velbusaio/module_spec/32.json +385 -0
- velbusaio/module_spec/33.json +249 -0
- velbusaio/module_spec/34.json +313 -0
- velbusaio/module_spec/35.json +313 -0
- velbusaio/module_spec/36.json +313 -0
- velbusaio/module_spec/37.json +333 -0
- velbusaio/module_spec/38.json +111 -0
- velbusaio/module_spec/39.json +4 -0
- velbusaio/module_spec/3A.json +306 -0
- velbusaio/module_spec/3B.json +306 -0
- velbusaio/module_spec/3C.json +306 -0
- velbusaio/module_spec/3D.json +454 -0
- velbusaio/module_spec/3E.json +302 -0
- velbusaio/module_spec/3F.json +4 -0
- velbusaio/module_spec/40.json +4 -0
- velbusaio/module_spec/41.json +241 -0
- velbusaio/module_spec/42.json +4 -0
- velbusaio/module_spec/43.json +23 -0
- velbusaio/module_spec/44.json +38 -0
- velbusaio/module_spec/45.json +4 -0
- velbusaio/module_spec/48.json +111 -0
- velbusaio/module_spec/49.json +111 -0
- velbusaio/module_spec/4A.json +89 -0
- velbusaio/module_spec/4B.json +138 -0
- velbusaio/module_spec/4C.json +129 -0
- velbusaio/module_spec/4D.json +108 -0
- velbusaio/module_spec/4E.json +787 -0
- velbusaio/module_spec/4F.json +114 -0
- velbusaio/module_spec/50.json +114 -0
- velbusaio/module_spec/51.json +114 -0
- velbusaio/module_spec/52.json +456 -0
- velbusaio/module_spec/54.json +270 -0
- velbusaio/module_spec/55.json +270 -0
- velbusaio/module_spec/56.json +270 -0
- velbusaio/module_spec/57.json +260 -0
- velbusaio/module_spec/5A.json +4 -0
- velbusaio/module_spec/5B.json +4 -0
- velbusaio/module_spec/5C.json +90 -0
- velbusaio/module_spec/5F.json +78 -0
- velbusaio/module_spec/60.json +4 -0
- velbusaio/module_spec/61.json +89 -0
- velbusaio/module_spec/broadcast.json +67 -0
- velbusaio/module_spec/ignore.json +22 -0
- velbusaio/protocol.py +34 -17
- velbusaio/raw_message.py +6 -6
- velbusaio/util.py +4 -0
- velbusaio/vlp_reader.py +249 -0
- velbus_aio-2023.12.0.dist-info/RECORD +0 -103
- velbusaio/moduleprotocol/protocol.json +0 -26507
- {velbus_aio-2023.12.0.dist-info → velbus_aio-2025.11.0.dist-info/licenses}/LICENSE +0 -0
velbusaio/protocol.py
CHANGED
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import binascii
|
|
5
5
|
import logging
|
|
6
|
+
import time
|
|
6
7
|
import typing as t
|
|
7
8
|
from asyncio import transports
|
|
8
9
|
|
|
@@ -15,9 +16,7 @@ from velbusaio.raw_message import create as create_message_info
|
|
|
15
16
|
|
|
16
17
|
def _on_write_backoff(details):
|
|
17
18
|
logging.debug(
|
|
18
|
-
"Transport is not open, waiting {wait} seconds after {tries}"
|
|
19
|
-
wait=details.wait,
|
|
20
|
-
tries=details.tries,
|
|
19
|
+
f"Transport is not open, waiting {details.wait} seconds after {details.tries}"
|
|
21
20
|
)
|
|
22
21
|
|
|
23
22
|
|
|
@@ -31,13 +30,11 @@ class VelbusProtocol(asyncio.BufferedProtocol):
|
|
|
31
30
|
self,
|
|
32
31
|
message_received_callback: t.Callable[[RawMessage], t.Awaitable[None]],
|
|
33
32
|
connection_lost_callback=None,
|
|
34
|
-
end_of_scan_callback=None,
|
|
35
33
|
) -> None:
|
|
36
34
|
super().__init__()
|
|
37
35
|
self._log = logging.getLogger("velbus-protocol")
|
|
38
36
|
self._message_received_callback = message_received_callback
|
|
39
37
|
self._connection_lost_callback = connection_lost_callback
|
|
40
|
-
self._end_of_scan_callback = end_of_scan_callback
|
|
41
38
|
|
|
42
39
|
# everything for reading from Velbus
|
|
43
40
|
self._buffer = bytearray(MAXIMUM_MESSAGE_SIZE)
|
|
@@ -63,14 +60,14 @@ class VelbusProtocol(asyncio.BufferedProtocol):
|
|
|
63
60
|
self._restart_writer = True
|
|
64
61
|
self.restart_writing()
|
|
65
62
|
|
|
66
|
-
async def pause_writing(self):
|
|
63
|
+
async def pause_writing(self) -> None:
|
|
67
64
|
"""Pause writing."""
|
|
68
65
|
self._restart_writer = False
|
|
69
66
|
if self._writer_task:
|
|
70
67
|
self._send_queue.put_nowait(None)
|
|
71
68
|
await asyncio.sleep(0.1)
|
|
72
69
|
|
|
73
|
-
def restart_writing(self):
|
|
70
|
+
def restart_writing(self) -> None:
|
|
74
71
|
"""Resume writing."""
|
|
75
72
|
if self._restart_writer and not self._write_transport_lock.locked():
|
|
76
73
|
self._writer_task = asyncio.ensure_future(
|
|
@@ -78,7 +75,7 @@ class VelbusProtocol(asyncio.BufferedProtocol):
|
|
|
78
75
|
)
|
|
79
76
|
self._writer_task.add_done_callback(lambda _future: self.restart_writing())
|
|
80
77
|
|
|
81
|
-
def close(self):
|
|
78
|
+
def close(self) -> None:
|
|
82
79
|
self._closing = True
|
|
83
80
|
self._restart_writer = False
|
|
84
81
|
if self.transport:
|
|
@@ -165,7 +162,7 @@ class VelbusProtocol(asyncio.BufferedProtocol):
|
|
|
165
162
|
self._buffer_view = memoryview(self._buffer)
|
|
166
163
|
|
|
167
164
|
async def _process_message(self, msg: RawMessage) -> None:
|
|
168
|
-
self._log.debug(f"RX: {msg}")
|
|
165
|
+
# self._log.debug(f"RX: {msg}")
|
|
169
166
|
await self._message_received_callback(msg)
|
|
170
167
|
|
|
171
168
|
# Everything write-related
|
|
@@ -184,22 +181,22 @@ class VelbusProtocol(asyncio.BufferedProtocol):
|
|
|
184
181
|
await self._write_transport_lock.acquire()
|
|
185
182
|
while self._restart_writer:
|
|
186
183
|
# wait for an item from the queue
|
|
187
|
-
msg_info = await self._send_queue.get()
|
|
184
|
+
msg_info: RawMessage | None = await self._send_queue.get()
|
|
188
185
|
if msg_info is None:
|
|
189
186
|
self._restart_writer = False
|
|
190
187
|
return
|
|
191
188
|
message_sent = False
|
|
192
189
|
try:
|
|
190
|
+
start_time = time.perf_counter()
|
|
193
191
|
while not message_sent:
|
|
194
192
|
message_sent = await self._write_message(msg_info)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if msg_info.rtr and msg_info.address == 0xFF:
|
|
201
|
-
self._end_of_scan_callback()
|
|
193
|
+
send_time = time.perf_counter() - start_time
|
|
194
|
+
|
|
195
|
+
self._send_queue.task_done() # indicate that the item of the queue has been processed
|
|
196
|
+
|
|
197
|
+
queue_sleep_time = self._calculate_queue_sleep_time(msg_info, send_time)
|
|
202
198
|
await asyncio.sleep(queue_sleep_time)
|
|
199
|
+
|
|
203
200
|
except (asyncio.CancelledError, GeneratorExit) as exc:
|
|
204
201
|
if not self._closing:
|
|
205
202
|
self._log.error(f"Stopping Velbus writer due to {exc!r}")
|
|
@@ -211,6 +208,22 @@ class VelbusProtocol(asyncio.BufferedProtocol):
|
|
|
211
208
|
self._write_transport_lock.release()
|
|
212
209
|
self._log.debug("Ending Velbus write message from send queue")
|
|
213
210
|
|
|
211
|
+
@staticmethod
|
|
212
|
+
def _calculate_queue_sleep_time(msg_info, send_time):
|
|
213
|
+
sleep_time = SLEEP_TIME
|
|
214
|
+
|
|
215
|
+
if msg_info.rtr:
|
|
216
|
+
sleep_time = SLEEP_TIME # this is a scan command. We could be quicker?
|
|
217
|
+
|
|
218
|
+
if msg_info.command == 0xEF:
|
|
219
|
+
# 'channel name request' command provokes in worst case 99 answer packets from VMBGPOD
|
|
220
|
+
sleep_time = SLEEP_TIME * 33 # TODO make this adaptable on module_type
|
|
221
|
+
|
|
222
|
+
if send_time > sleep_time:
|
|
223
|
+
return 0 # no need to wait, we are already late
|
|
224
|
+
else:
|
|
225
|
+
return sleep_time - send_time
|
|
226
|
+
|
|
214
227
|
@backoff.on_predicate(
|
|
215
228
|
backoff.expo,
|
|
216
229
|
lambda is_sent: not is_sent,
|
|
@@ -224,3 +237,7 @@ class VelbusProtocol(asyncio.BufferedProtocol):
|
|
|
224
237
|
return True
|
|
225
238
|
else:
|
|
226
239
|
return False
|
|
240
|
+
|
|
241
|
+
async def wait_on_all_messages_sent_async(self) -> None:
|
|
242
|
+
self._log.debug("Waiting on all messages sent")
|
|
243
|
+
await self._send_queue.join()
|
velbusaio/raw_message.py
CHANGED
|
@@ -130,12 +130,12 @@ def _trim_buffer_garbage(rawmessage: bytearray) -> bytearray:
|
|
|
130
130
|
if rawmessage and rawmessage[0] != START_BYTE:
|
|
131
131
|
start_index = rawmessage.find(START_BYTE)
|
|
132
132
|
if start_index > -1:
|
|
133
|
-
logging.debug(
|
|
134
|
-
"Trimming leading garbage from buffer content: {buffer} becomes {new_buffer}".format(
|
|
135
|
-
buffer=binascii.hexlify(rawmessage),
|
|
136
|
-
new_buffer=binascii.hexlify(rawmessage[start_index:]),
|
|
137
|
-
)
|
|
138
|
-
)
|
|
133
|
+
# logging.debug(
|
|
134
|
+
# "Trimming leading garbage from buffer content: {buffer} becomes {new_buffer}".format(
|
|
135
|
+
# buffer=binascii.hexlify(rawmessage),
|
|
136
|
+
# new_buffer=binascii.hexlify(rawmessage[start_index:]),
|
|
137
|
+
# )
|
|
138
|
+
# )
|
|
139
139
|
return rawmessage[start_index:]
|
|
140
140
|
else:
|
|
141
141
|
logging.debug(
|
velbusaio/util.py
CHANGED
velbusaio/vlp_reader.py
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import importlib.resources
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from aiofile import async_open
|
|
7
|
+
from bs4 import BeautifulSoup
|
|
8
|
+
|
|
9
|
+
from velbusaio.command_registry import MODULE_DIRECTORY
|
|
10
|
+
from velbusaio.helpers import h2
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class VlpFile:
|
|
14
|
+
|
|
15
|
+
def __init__(self, file_path) -> None:
|
|
16
|
+
self._file_path = file_path
|
|
17
|
+
self._modules = []
|
|
18
|
+
self._log = logging.getLogger("velbus-vlpFile")
|
|
19
|
+
|
|
20
|
+
def get(self) -> dict:
|
|
21
|
+
return self._modules
|
|
22
|
+
|
|
23
|
+
async def read(self) -> None:
|
|
24
|
+
async with async_open(self._file_path) as file:
|
|
25
|
+
xml_content = await file.read()
|
|
26
|
+
_soup = BeautifulSoup(xml_content, "xml")
|
|
27
|
+
for module in _soup.find_all("Module"):
|
|
28
|
+
mod = vlpModule(
|
|
29
|
+
module.find("Caption").get_text(),
|
|
30
|
+
module["address"],
|
|
31
|
+
module["build"],
|
|
32
|
+
module["serial"],
|
|
33
|
+
module["type"],
|
|
34
|
+
module.find("Memory").get_text(),
|
|
35
|
+
)
|
|
36
|
+
self._modules.append(mod)
|
|
37
|
+
await mod.parse()
|
|
38
|
+
self._modules.sort(key=lambda mod: mod.get_decimal_addr())
|
|
39
|
+
|
|
40
|
+
def dump(self) -> None:
|
|
41
|
+
for m in self._modules:
|
|
42
|
+
print(f"Module {m.get_decimal_addr()}: {m._name}, type {m._type_id}")
|
|
43
|
+
for key, value in m._channels.items():
|
|
44
|
+
name = value["Name"]
|
|
45
|
+
print(f" {key} => {name}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class vlpModule:
|
|
49
|
+
|
|
50
|
+
def __init__(self, name, addresses, build, serial, type, memory) -> None:
|
|
51
|
+
self._name = name
|
|
52
|
+
self._addresses = addresses
|
|
53
|
+
self._build = build
|
|
54
|
+
self._serial = serial
|
|
55
|
+
self._type = type
|
|
56
|
+
self._memory = memory
|
|
57
|
+
self._spec = {}
|
|
58
|
+
self._channels = {}
|
|
59
|
+
self._type_id = next(
|
|
60
|
+
(key for key, value in MODULE_DIRECTORY.items() if value == self._type),
|
|
61
|
+
None,
|
|
62
|
+
)
|
|
63
|
+
self._log = logging.getLogger("velbus-vlpFile")
|
|
64
|
+
self._log.info(
|
|
65
|
+
f"=> Created vlpModule address: {self._addresses} type: {self._type} ({self._type_id})"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def get(self) -> None:
|
|
69
|
+
print(self._channels)
|
|
70
|
+
print(self)
|
|
71
|
+
|
|
72
|
+
def get_addr(self) -> str:
|
|
73
|
+
return self._addresses
|
|
74
|
+
|
|
75
|
+
def get_name(self) -> str:
|
|
76
|
+
return self._name
|
|
77
|
+
|
|
78
|
+
def get_type(self) -> int | None:
|
|
79
|
+
return self._type_id
|
|
80
|
+
|
|
81
|
+
def get_serial(self) -> str:
|
|
82
|
+
return self._serial
|
|
83
|
+
|
|
84
|
+
def get_memory(self) -> str:
|
|
85
|
+
return self._memory
|
|
86
|
+
|
|
87
|
+
def get_build(self) -> str:
|
|
88
|
+
return self._build
|
|
89
|
+
|
|
90
|
+
def get_channels(self) -> dict:
|
|
91
|
+
return self._channels
|
|
92
|
+
|
|
93
|
+
def __str__(self):
|
|
94
|
+
return f"vlpModule(name={self._name}, addresses={self._addresses}, build={self._build}, serial={self._serial}, type={self._type})"
|
|
95
|
+
|
|
96
|
+
def get_decimal_addr(
|
|
97
|
+
self,
|
|
98
|
+
) -> int: # lgor: get the numeric value of primary module address
|
|
99
|
+
addr = self._addresses.split(",")[0]
|
|
100
|
+
return int(addr, 16)
|
|
101
|
+
|
|
102
|
+
async def parse(self) -> None:
|
|
103
|
+
await self._load_module_spec()
|
|
104
|
+
|
|
105
|
+
if "Memory" not in self._spec:
|
|
106
|
+
self._log.debug(" => no Memory locations found")
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
# channel names
|
|
110
|
+
self._channels = self._spec.get("Channels", {})
|
|
111
|
+
for addr, chan in self._channels.items():
|
|
112
|
+
self._log.debug(f" => Processing channel {addr}:")
|
|
113
|
+
if ("Editable" in chan) and (chan["Editable"] == "yes"):
|
|
114
|
+
self._log.debug(f" => channel {addr} is editable, getting name")
|
|
115
|
+
name = self._get_channel_name(int(addr))
|
|
116
|
+
if name:
|
|
117
|
+
self._log.debug(f" => got name '{name}' for channel {addr}")
|
|
118
|
+
self._channels[addr]["Name"] = name
|
|
119
|
+
self._channels[addr]["_is_loaded"] = True
|
|
120
|
+
|
|
121
|
+
# extra
|
|
122
|
+
self._load_extra_data()
|
|
123
|
+
|
|
124
|
+
def _load_extra_data(self) -> None:
|
|
125
|
+
self._log.debug(" => Getting extra data")
|
|
126
|
+
if "Extras" not in self._spec["Memory"]:
|
|
127
|
+
self._log.debug(" => no Extra Memory locations found")
|
|
128
|
+
return None
|
|
129
|
+
for addr, extra in self._spec["Memory"]["Extras"].items():
|
|
130
|
+
byte_data = bytes.fromhex(self._read_from_memory(addr))
|
|
131
|
+
self._log.debug(
|
|
132
|
+
f" => got extra data {byte_data.hex().upper()} from address {addr}"
|
|
133
|
+
)
|
|
134
|
+
if "Translate" in extra:
|
|
135
|
+
translation_found = False
|
|
136
|
+
for translate_key, translate_value in extra["Translate"].items():
|
|
137
|
+
if translate_key.startswith("%"):
|
|
138
|
+
# Binary pattern matching
|
|
139
|
+
if self._match_binary_pattern(translate_key, byte_data):
|
|
140
|
+
self._log.debug(
|
|
141
|
+
f" => Binary pattern {translate_key} matched, value: {translate_value}"
|
|
142
|
+
)
|
|
143
|
+
self._channels[translate_value["Channel"]][
|
|
144
|
+
translate_value["SubName"]
|
|
145
|
+
] = translate_value["Value"]
|
|
146
|
+
translation_found = True
|
|
147
|
+
else:
|
|
148
|
+
# Direct value matching (existing behavior for integer keys)
|
|
149
|
+
try:
|
|
150
|
+
int_key = int(translate_key)
|
|
151
|
+
if len(byte_data) > 0 and byte_data[0] == int_key:
|
|
152
|
+
self._log.debug(
|
|
153
|
+
f" => Direct match for value {int_key}: {translate_value}"
|
|
154
|
+
)
|
|
155
|
+
translation_found = True
|
|
156
|
+
except ValueError:
|
|
157
|
+
# Not an integer key, skip
|
|
158
|
+
continue
|
|
159
|
+
if not translation_found:
|
|
160
|
+
self._log.error(
|
|
161
|
+
f" => No translation found for data {byte_data.hex().upper()}"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def _match_binary_pattern(self, pattern: str, byte_data: bytes) -> bool:
|
|
165
|
+
"""
|
|
166
|
+
Match a binary pattern like %......00 against byte data.
|
|
167
|
+
% indicates binary pattern
|
|
168
|
+
. means don't care bit
|
|
169
|
+
0/1 are specific bits that must match
|
|
170
|
+
"""
|
|
171
|
+
if not pattern.startswith("%"):
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
# Remove the % prefix
|
|
175
|
+
binary_pattern = pattern[1:]
|
|
176
|
+
|
|
177
|
+
# Convert byte_data to binary string (without '0b' prefix)
|
|
178
|
+
if len(byte_data) == 0:
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
# Take the first byte for pattern matching
|
|
182
|
+
byte_value = byte_data[0]
|
|
183
|
+
binary_data = format(byte_value, "08b")
|
|
184
|
+
|
|
185
|
+
# Check if pattern length matches
|
|
186
|
+
if len(binary_pattern) != len(binary_data):
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
# Check each bit position
|
|
190
|
+
for i, (pattern_bit, data_bit) in enumerate(zip(binary_pattern, binary_data)):
|
|
191
|
+
if pattern_bit == ".":
|
|
192
|
+
# Don't care bit, skip
|
|
193
|
+
continue
|
|
194
|
+
elif pattern_bit != data_bit:
|
|
195
|
+
# Specific bit must match
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
return True
|
|
199
|
+
|
|
200
|
+
def _get_channel_name(self, chan: int) -> str | None:
|
|
201
|
+
if "Channels" not in self._spec["Memory"]:
|
|
202
|
+
self._log.debug(" => no Channels Memory locations found")
|
|
203
|
+
return None
|
|
204
|
+
dchan = format(chan, "02d")
|
|
205
|
+
if dchan not in self._spec["Memory"]["Channels"]:
|
|
206
|
+
self._log.debug(f" => no chan {chan} Memory locations found")
|
|
207
|
+
return None
|
|
208
|
+
byte_data = bytes.fromhex(
|
|
209
|
+
self._read_from_memory(self._spec["Memory"]["Channels"][dchan]).replace(
|
|
210
|
+
"FF", ""
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
try:
|
|
214
|
+
name = byte_data.decode("ascii")
|
|
215
|
+
except UnicodeDecodeError as e:
|
|
216
|
+
self._log.error(f" => UnicodeDecodeError: {e}")
|
|
217
|
+
name = byte_data
|
|
218
|
+
finally:
|
|
219
|
+
return name
|
|
220
|
+
|
|
221
|
+
async def _load_module_spec(self) -> None:
|
|
222
|
+
self._log.debug(f" => Load module spec for {self._type_id}")
|
|
223
|
+
if sys.version_info >= (3, 13):
|
|
224
|
+
with importlib.resources.path(
|
|
225
|
+
__name__, f"module_spec/{h2(self._type_id)}.json"
|
|
226
|
+
) as fspath:
|
|
227
|
+
async with async_open(fspath) as protocol_file:
|
|
228
|
+
self._spec = json.loads(await protocol_file.read())
|
|
229
|
+
else:
|
|
230
|
+
async with async_open(
|
|
231
|
+
str(
|
|
232
|
+
importlib.resources.files(__name__.split(".")[0]).joinpath(
|
|
233
|
+
f"module_spec/{h2(self._type_id)}.json"
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
) as protocol_file:
|
|
237
|
+
self._spec = json.loads(await protocol_file.read())
|
|
238
|
+
|
|
239
|
+
def _read_from_memory(self, address_range) -> str | None:
|
|
240
|
+
# its a single address
|
|
241
|
+
if "-" not in address_range:
|
|
242
|
+
start = int(address_range, 16) * 2
|
|
243
|
+
end = (int(address_range, 16) + 1) * 2
|
|
244
|
+
return self._memory[start:end]
|
|
245
|
+
# its a range
|
|
246
|
+
start_str, end_str = address_range.split("-")
|
|
247
|
+
start = int(start_str, 16) * 2
|
|
248
|
+
end = (int(end_str, 16) + 1) * 2
|
|
249
|
+
return self._memory[start:end]
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
velbusaio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
velbusaio/channels.py,sha256=ezviD9AepEneuBezcWpk0tWoPZxbTit8wuMcBO44MsM,22848
|
|
3
|
-
velbusaio/command_registry.py,sha256=UJOknKYGDPb0pPuvsH42GetEVC-Sa3bMkiSExE6DfPc,4787
|
|
4
|
-
velbusaio/const.py,sha256=9JY8oMrE4galRPAv247RXP_xDOdYkyPc7lBlptJius4,1550
|
|
5
|
-
velbusaio/controller.py,sha256=h8LProppbTYJNdyyS5rQ3ZweiTjcKsdbKBzNoOS_IAk,9253
|
|
6
|
-
velbusaio/discovery.py,sha256=Px6qoZl4QhF17aMz6JxstCORBpLzZGWEK9h4Vyvg57o,1649
|
|
7
|
-
velbusaio/exceptions.py,sha256=FHkXaM3dK5Gkk-QGAf9dLE3FPlCU2FRZWUyY-4KRNnA,515
|
|
8
|
-
velbusaio/handler.py,sha256=gI0Zma9SbcVEvAzYno4IJbqwtEYQk5Cp6zxVNtmox54,8342
|
|
9
|
-
velbusaio/helpers.py,sha256=8PWyD13UA244ESk5YwPcRzvjK3OBAqmhwAwC6E9W4B4,2542
|
|
10
|
-
velbusaio/message.py,sha256=ZFy0iup_DK_so_wZsr2F-3A4AJgQGxf5C3TXjK4BMW0,5047
|
|
11
|
-
velbusaio/module.py,sha256=GmuUthbqlluI8n8HHgTjMsPbzSWWDUa5Y2XPSbJArms,35355
|
|
12
|
-
velbusaio/protocol.py,sha256=sut7jf3IJiJRx59w1EJs0AX4Aa15vrPNgIWJrtQkMjc,8220
|
|
13
|
-
velbusaio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
velbusaio/raw_message.py,sha256=5604X_0Itav6Ts89tXWvXs6sdx_KWRvPvSHjtFjXlMA,4613
|
|
15
|
-
velbusaio/util.py,sha256=eCsGQJ9nuzk_B2skBlv3MsNaMaHDXo1a1qSPjObzXH4,1540
|
|
16
|
-
velbusaio/messages/__init__.py,sha256=7L1ziMh5u5HE62MjzcDRhKVof294puZzKFCbBrqxeqw,5892
|
|
17
|
-
velbusaio/messages/blind_status.py,sha256=3K5JRRn34_N1oQRKNBCkNwWIwhaURYL1ZNZhQZWFnSU,3417
|
|
18
|
-
velbusaio/messages/bus_active.py,sha256=7aSGEASI4WxWZV9HI6P1b_p6wU1UvbJYmT90Hz_I8ls,748
|
|
19
|
-
velbusaio/messages/bus_error_counter_status.py,sha256=GYMAxyIHUZLzse-pzyUgegLMY75ptFLKNP5qADbuNFg,1177
|
|
20
|
-
velbusaio/messages/bus_error_counter_status_request.py,sha256=9Y-yW4Z8FPPbn-ky5JTxGkt6NQg7ISDQVHPaVnm7P4U,702
|
|
21
|
-
velbusaio/messages/bus_off.py,sha256=SUi9lOp8BNwl6aWacjp1TC0xcleTKSQt1sM8yWUoeGg,678
|
|
22
|
-
velbusaio/messages/channel_name_part1.py,sha256=TiziwJb7idwi7YeAxDVGMVrG1VPRrBnCswMZZXA0V4w,2577
|
|
23
|
-
velbusaio/messages/channel_name_part2.py,sha256=YUQFBvSRkft79FSRNGd3-gV2hpiDx11TgyNH65IcG1w,2577
|
|
24
|
-
velbusaio/messages/channel_name_part3.py,sha256=46LiGIwm0HKTwd7CMfKFUv5xjebAK85uQwopAlBEYjY,2578
|
|
25
|
-
velbusaio/messages/channel_name_request.py,sha256=11HK4IDjJOg_K57ssEZ5ZAV-P0y5ysjJVUX_3di3uTk,2363
|
|
26
|
-
velbusaio/messages/clear_led.py,sha256=O5InexnnPpzJcmKBfVVILTtIZ7ojljJ2xn-hRXRP8cM,911
|
|
27
|
-
velbusaio/messages/counter_status.py,sha256=FW1Vaf3U-Mp5Zg--HcBs24Oh739sZ00EocFaeF5Maf4,1239
|
|
28
|
-
velbusaio/messages/counter_status_request.py,sha256=0SeJemPRheX5k1DjZlnvaY0dWhHBVqh4d60kMe8RVxc,918
|
|
29
|
-
velbusaio/messages/cover_down.py,sha256=r1PzvwyhmMF5BiKlxQELBwsdqVqSW_9lPYO0B_4O0WU,2551
|
|
30
|
-
velbusaio/messages/cover_off.py,sha256=a8a1iRQwcEycQQvyjMjRHTeWoSHejjBAJiiQtkzf5SQ,2257
|
|
31
|
-
velbusaio/messages/cover_position.py,sha256=UsBaENP1x61iTQTx8oPTH6GS1iwI8g0xah-b5hnxbwg,1282
|
|
32
|
-
velbusaio/messages/cover_up.py,sha256=-P6Ze1OUDSs-YTqFPV0Tl3_EpCL97OJ2CeFB1ZDsLgk,2546
|
|
33
|
-
velbusaio/messages/dali_device_settings.py,sha256=JeiUGw5oPPQde7buNz0T7euZ-QJVxmtTZF2DYVJxFsM,4855
|
|
34
|
-
velbusaio/messages/dali_device_settings_request.py,sha256=3IqaXOxqidJCJ1FxFMDofaXiX75SCgFcPyOQk0p6L_Q,1461
|
|
35
|
-
velbusaio/messages/dali_dim_value_status.py,sha256=V2Fs8CZNihIi9ZxQs7Bxkt9qCInFXxMZnF6vKhIUCnQ,1044
|
|
36
|
-
velbusaio/messages/dimmer_channel_status.py,sha256=nJ_83uVe0FIiwWmGt7XNXcRYu3ikNUwxOOg5nHzaOPw,2470
|
|
37
|
-
velbusaio/messages/dimmer_status.py,sha256=QwWbVoCLndhymsOcj-jw0scqUuQRU3m6J6q1FwgNgn4,2937
|
|
38
|
-
velbusaio/messages/edge_set_color.py,sha256=enwPshhNrleAQQwc3RobPt5foZ1ynE_74SlBEuzd86w,3305
|
|
39
|
-
velbusaio/messages/edge_set_custom_color.py,sha256=fw4hQTrTxNBb63XLTY9iLFtPYrNh0mAAE28DCxG9h1A,1337
|
|
40
|
-
velbusaio/messages/fast_blinking_led.py,sha256=Q5scu34RQJZ43EDZCR1kJy63gULSWHN5bpKpqo_XOOc,908
|
|
41
|
-
velbusaio/messages/forced_off.py,sha256=QQiwMbVUW6wegyTLrf1daJiS8KggPXZvepGsBU9_FUo,590
|
|
42
|
-
velbusaio/messages/forced_on.py,sha256=tS7dUayO_0Grco5TfiOMWBUOc0CY44w_7FcpU-ZViJU,589
|
|
43
|
-
velbusaio/messages/interface_status_request.py,sha256=2vAbB2Qha3pp1ZLepM8TWJyPkX_6I_lmGBIKxJxfajU,694
|
|
44
|
-
velbusaio/messages/ir_receiver_status.py,sha256=_STATc_X7R4o-uyG5iQyyasyB_Np6ubkNi__5w_IeT0,374
|
|
45
|
-
velbusaio/messages/kwh_status.py,sha256=K4twkBx-XhdmqAhnAZ6xuHX4kiNQuhDQl5JHDnxdlnQ,1413
|
|
46
|
-
velbusaio/messages/light_value_request.py,sha256=sF7YYlbTESEBp05bygcLVpjFpejM9m-N8sbL_hDUPO0,598
|
|
47
|
-
velbusaio/messages/memo_text.py,sha256=yg7Qc4mHWoclMN9Rk6L_lCmh_NjPTdwSvkAYalUY0Vw,1030
|
|
48
|
-
velbusaio/messages/memory_data.py,sha256=ZTf8R8r8LpaBg2KOK8CnQJ8RU6gH2DEWfr7phqY5pjE,1030
|
|
49
|
-
velbusaio/messages/memory_data_block.py,sha256=VdZ3s1f3xRgaMRqbceLgByEjoRPSaDU82BLdGQXUvc8,1044
|
|
50
|
-
velbusaio/messages/memory_dump_request.py,sha256=g8jWuy3IwqG6UvTz2_eVng1e0TAPy8XF3Mt6u0l5GVU,698
|
|
51
|
-
velbusaio/messages/module_status.py,sha256=PLsmTheV-fTGHc5FhrzNXMxvGKP8EeYFjadY07ENOn0,6399
|
|
52
|
-
velbusaio/messages/module_status_request.py,sha256=Iv9Y2UXzPFyz3FiL0yH-o231S5_dOfv9z72AYgEjLjM,960
|
|
53
|
-
velbusaio/messages/module_subtype.py,sha256=aDoXFR6fnVFitPLnB9RNLnR4S_t4MZP1uoMMzg4JfB0,1463
|
|
54
|
-
velbusaio/messages/module_type.py,sha256=2VhKxtPF_8VKPsyWGUvBcwTlxVIxgNnRi5HVYdAAwgU,4115
|
|
55
|
-
velbusaio/messages/module_type_request.py,sha256=v8LBuPG_nxvh2lid9XG254M522PN-zSVezEMCOLrGis,753
|
|
56
|
-
velbusaio/messages/push_button_status.py,sha256=AkhZJ5xhMjWDKidOffLZewIi8qWDTu7q8qt4joNenIw,1553
|
|
57
|
-
velbusaio/messages/raw.py,sha256=m7xaGhDqmvHxy0UzWC4gijAbkqXkNEdfrsKRDKWX1JM,1963
|
|
58
|
-
velbusaio/messages/read_data_block_from_memory.py,sha256=Z3hacc_wHtgkkeA3ZsfJv-1xTIt73M5AKbvggZdZtew,974
|
|
59
|
-
velbusaio/messages/read_data_from_memory.py,sha256=I__kKm8xcoQz6e5DKr8G3w3NnpXfuv0a3Fn_EIg0Tw4,977
|
|
60
|
-
velbusaio/messages/realtime_clock_status_request.py,sha256=KEvcmWlgN4XbpHSA90fg3RFVU2bsNGq6sfugw9IbdTg,607
|
|
61
|
-
velbusaio/messages/receive_buffer_full.py,sha256=dgyzmZ8VxhQk8r4vUs2uWuojut2Akj4IMazkqfDa1e4,857
|
|
62
|
-
velbusaio/messages/receive_ready.py,sha256=f1LdUVcUTG7rj71wCVHgF03zbkIx4CpJKG7zgXIERf4,686
|
|
63
|
-
velbusaio/messages/relay_status.py,sha256=BBJHM54gzbOTAoQ_N8ls3IiJoOIklKFCvBU4odntXvM,2836
|
|
64
|
-
velbusaio/messages/restore_dimmer.py,sha256=Xk3GqthYwqCMc6rPXCCc5EXWoXZWpWrk1r8nevfdfso,1783
|
|
65
|
-
velbusaio/messages/select_program.py,sha256=RHvOvWXC7w8wxMhco0NUDPIMK6r6Q2LCd87Kdnl7hxI,834
|
|
66
|
-
velbusaio/messages/sensor_settings_request.py,sha256=njSYjNTxhNyRKJLP7wexaWHnYiSWbrSlOm_F7myuBdw,818
|
|
67
|
-
velbusaio/messages/sensor_temp_request.py,sha256=R-VNtqqiDbgZjPNfj5nE0_bTuGLD74Zy-FUsrCcyXFI,598
|
|
68
|
-
velbusaio/messages/sensor_temperature.py,sha256=Erh7pIrbxpmurg3x9_di6A6Os_2Sgpid7T6LgHuWQyA,1557
|
|
69
|
-
velbusaio/messages/set_date.py,sha256=aV8qO3kJsIMsLZPwVZB5PKivBVa7YHpottj6sTgUSiM,1459
|
|
70
|
-
velbusaio/messages/set_daylight_saving.py,sha256=bpP4BN-uyFIRt4Y6finaaYBIzI08A5AVsUFJfnpxzkg,1117
|
|
71
|
-
velbusaio/messages/set_dimmer.py,sha256=gS3Bm6BvKj9ZnHDOGwTzrEGukXnoPVkAQCRwLOG-E9Q,1936
|
|
72
|
-
velbusaio/messages/set_led.py,sha256=hNmbmS2hXqTBVfXMWYhyf10-BOvd5ZwimMd74Cyj5PQ,899
|
|
73
|
-
velbusaio/messages/set_realtime_clock.py,sha256=2EVdIKLB0M1syBZyIpD-vxiEJMriqElxnw3cO773Nw4,1308
|
|
74
|
-
velbusaio/messages/set_temperature.py,sha256=PMs_uXWMhggIX0s2ikediwTJ4WSpvrXzahCJ0Ak_hks,958
|
|
75
|
-
velbusaio/messages/slider_status.py,sha256=poNQY8hvdTVqD0qfLxvmp-w3js2MVozTLR657cZOI8o,1393
|
|
76
|
-
velbusaio/messages/slow_blinking_led.py,sha256=a5Kpbx_IAYzOoahkgaqcc61j3ecFjy6fT9Dox6BlUiM,908
|
|
77
|
-
velbusaio/messages/start_relay_blinking_timer.py,sha256=KdTqYxXquPn07_Con_vSfbi7KthdsUv4SwkxJj2MEGk,1297
|
|
78
|
-
velbusaio/messages/start_relay_timer.py,sha256=fhaIJrVOP2MrIpzzlswyENC4XwhS8YOgr4p6rShSYyA,1289
|
|
79
|
-
velbusaio/messages/switch_relay_off.py,sha256=nTE-l9_lpvxneq1txoTrX_C-gJ2aDQ-0czqd8JLwNts,1098
|
|
80
|
-
velbusaio/messages/switch_relay_on.py,sha256=Vj4tjTCrK7CBRJBFnF81fAp0qFWGqX_JAeeioV3x9aE,1097
|
|
81
|
-
velbusaio/messages/switch_to_comfort.py,sha256=9NuqVHwrWLsFUmx2ayra_0oubc5ovKTAawoG44w61h8,790
|
|
82
|
-
velbusaio/messages/switch_to_day.py,sha256=ZqUdZkZDtZsUsZNLj08G3PlJ8jd6H0uos8vdnivPwwg,786
|
|
83
|
-
velbusaio/messages/switch_to_night.py,sha256=FhoKB-a7TWQLm3MsLJ-5ttUNVgT9CIvS-HogrF1MZTg,788
|
|
84
|
-
velbusaio/messages/switch_to_safe.py,sha256=DD8z7QwFU0Z_f2OT9ANjPfu7jLNKktqaID6nUce3K-U,787
|
|
85
|
-
velbusaio/messages/temp_sensor_settings_part1.py,sha256=QDAQDmdpJNWNWRqigtkDzAYmy4lxo7ZtX4CuThwUOb0,604
|
|
86
|
-
velbusaio/messages/temp_sensor_settings_part2.py,sha256=kQBlvVBroETdZjxz-oB6nAJlImbwGBNPhJVArDf26-M,583
|
|
87
|
-
velbusaio/messages/temp_sensor_settings_part3.py,sha256=_HugqJ5wVj84TCBC_4Y3NcdUNVE4SevIW9uskiAKKWs,583
|
|
88
|
-
velbusaio/messages/temp_sensor_settings_part4.py,sha256=GxxW8VttzeYlSgLAdkCFkMvIm5Q_Mzgy10WLgSXKhxA,583
|
|
89
|
-
velbusaio/messages/temp_sensor_settings_request.py,sha256=YSKKGjqAbeBbtmstPg5tCaEbXIRI5i_2NustkXIm7wY,606
|
|
90
|
-
velbusaio/messages/temp_sensor_status.py,sha256=rMHQ3-I9jMWn1fE2xjmaoSRoXfBA4JxccR5XQ92J71w,3827
|
|
91
|
-
velbusaio/messages/temp_set_cooling.py,sha256=9HyGzdcr5R-EoXvem8Ajgx8_12WB7yUYUcvVxZXqT-8,723
|
|
92
|
-
velbusaio/messages/temp_set_heating.py,sha256=F0B1RsQNdq7F25z3Sure6Hobph-HJVglOhK7K-lHQFE,723
|
|
93
|
-
velbusaio/messages/update_led_status.py,sha256=1b4gWqmmtAYUAeaoJdkda_zlW02lLwsIPoXMRGD8OCg,1300
|
|
94
|
-
velbusaio/messages/very_fast_blinking_led.py,sha256=3gB3TYXFFUdna_VaGrfSWGpOpX2iOu_8i5L9qQ2YG1s,912
|
|
95
|
-
velbusaio/messages/write_data_to_memory.py,sha256=2bC-vuv_RK3hGxvKeba1zR405ffj-CkYh3hJ9LVEaME,1038
|
|
96
|
-
velbusaio/messages/write_memory_block.py,sha256=5pP4hCRV7tc5xYxHO_51t-uCYgNtVHiaPic_ZGG2Y9s,1031
|
|
97
|
-
velbusaio/messages/write_module_address_and_serial_number.py,sha256=gU-yAHxICP6LPQX7BRj-WUfiVpTk0sV1CMlXeElAwl4,1596
|
|
98
|
-
velbusaio/moduleprotocol/protocol.json,sha256=zChwegR8Ic_Msw5iVMV_W3kVmSyK5DwYof46wD7l9H8,883766
|
|
99
|
-
velbus_aio-2023.12.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
100
|
-
velbus_aio-2023.12.0.dist-info/METADATA,sha256=nl4xVLuNrlqrwm2OcgO0V1tkXSFJZ31P2QmYgeIvb0Q,3079
|
|
101
|
-
velbus_aio-2023.12.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
102
|
-
velbus_aio-2023.12.0.dist-info/top_level.txt,sha256=W0-lSOwD23mm8FqaIe9vY20fKicBMIdUVjF-zmfxRnY,15
|
|
103
|
-
velbus_aio-2023.12.0.dist-info/RECORD,,
|