velbus-aio 2021.8.7__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-2025.11.0.dist-info/METADATA +71 -0
- velbus_aio-2025.11.0.dist-info/RECORD +194 -0
- {velbus_aio-2021.8.7.dist-info → velbus_aio-2025.11.0.dist-info}/WHEEL +1 -1
- velbus_aio-2025.11.0.dist-info/top_level.txt +3 -0
- velbusaio/channels.py +443 -109
- velbusaio/command_registry.py +126 -13
- velbusaio/const.py +36 -12
- velbusaio/controller.py +252 -177
- velbusaio/discovery.py +2 -2
- velbusaio/exceptions.py +22 -0
- velbusaio/handler.py +311 -145
- velbusaio/helpers.py +6 -18
- velbusaio/message.py +46 -132
- velbusaio/messages/__init__.py +12 -2
- velbusaio/messages/blind_status.py +16 -25
- velbusaio/messages/bus_active.py +3 -9
- velbusaio/messages/bus_error_counter_status.py +3 -4
- velbusaio/messages/bus_error_counter_status_request.py +3 -4
- velbusaio/messages/bus_off.py +3 -4
- velbusaio/messages/channel_name_part1.py +49 -33
- velbusaio/messages/channel_name_part2.py +49 -33
- velbusaio/messages/channel_name_part3.py +49 -33
- velbusaio/messages/channel_name_request.py +26 -12
- velbusaio/messages/clear_led.py +3 -4
- velbusaio/messages/counter_status.py +3 -17
- velbusaio/messages/counter_status_request.py +6 -6
- velbusaio/messages/counter_value.py +44 -0
- velbusaio/messages/cover_down.py +4 -29
- velbusaio/messages/cover_off.py +5 -29
- velbusaio/messages/cover_position.py +4 -19
- velbusaio/messages/cover_up.py +4 -27
- velbusaio/messages/dali_device_settings.py +178 -0
- velbusaio/messages/dali_device_settings_request.py +53 -0
- velbusaio/messages/dali_dim_value_status.py +44 -0
- velbusaio/messages/dimmer_channel_status.py +6 -19
- velbusaio/messages/dimmer_status.py +14 -31
- velbusaio/messages/edge_set_color.py +114 -0
- velbusaio/messages/edge_set_custom_color.py +56 -0
- velbusaio/messages/fast_blinking_led.py +3 -4
- velbusaio/messages/forced_off.py +3 -4
- velbusaio/messages/forced_on.py +3 -4
- velbusaio/messages/interface_status_request.py +3 -4
- velbusaio/messages/ir_receiver_status.py +18 -0
- velbusaio/messages/kwh_status.py +3 -19
- velbusaio/messages/light_value_request.py +3 -4
- velbusaio/messages/memo_text.py +3 -5
- velbusaio/messages/memory_data.py +3 -16
- velbusaio/messages/memory_data_block.py +3 -4
- velbusaio/messages/memory_dump_request.py +3 -4
- velbusaio/messages/module_status.py +107 -55
- velbusaio/messages/module_status_request.py +7 -6
- velbusaio/messages/module_subtype.py +11 -19
- velbusaio/messages/module_type.py +132 -21
- 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 +3 -16
- velbusaio/messages/raw.py +74 -0
- velbusaio/messages/read_data_block_from_memory.py +3 -4
- velbusaio/messages/read_data_from_memory.py +3 -4
- velbusaio/messages/realtime_clock_status_request.py +3 -4
- velbusaio/messages/receive_buffer_full.py +3 -4
- velbusaio/messages/receive_ready.py +3 -4
- velbusaio/messages/relay_status.py +13 -42
- velbusaio/messages/restore_dimmer.py +33 -24
- velbusaio/messages/select_program.py +35 -0
- velbusaio/messages/sensor_settings_request.py +3 -4
- velbusaio/messages/sensor_temp_request.py +3 -4
- velbusaio/messages/sensor_temperature.py +15 -19
- velbusaio/messages/set_date.py +10 -30
- velbusaio/messages/set_daylight_saving.py +8 -24
- velbusaio/messages/set_dimmer.py +43 -41
- velbusaio/messages/set_led.py +3 -4
- velbusaio/messages/set_realtime_clock.py +10 -30
- velbusaio/messages/set_temperature.py +3 -4
- velbusaio/messages/slider_status.py +16 -20
- velbusaio/messages/slow_blinking_led.py +3 -4
- velbusaio/messages/start_relay_blinking_timer.py +3 -4
- velbusaio/messages/start_relay_timer.py +3 -4
- velbusaio/messages/switch_relay_off.py +3 -16
- velbusaio/messages/switch_relay_on.py +3 -16
- velbusaio/messages/switch_to_comfort.py +4 -15
- velbusaio/messages/switch_to_day.py +4 -15
- velbusaio/messages/switch_to_night.py +4 -15
- velbusaio/messages/switch_to_safe.py +4 -15
- velbusaio/messages/temp_sensor_settings_part1.py +3 -4
- velbusaio/messages/temp_sensor_settings_part2.py +27 -0
- velbusaio/messages/temp_sensor_settings_part3.py +27 -0
- velbusaio/messages/temp_sensor_settings_part4.py +27 -0
- velbusaio/messages/temp_sensor_settings_request.py +3 -4
- velbusaio/messages/temp_sensor_status.py +34 -35
- velbusaio/messages/temp_set_cooling.py +3 -13
- velbusaio/messages/temp_set_heating.py +3 -13
- velbusaio/messages/update_led_status.py +3 -4
- velbusaio/messages/very_fast_blinking_led.py +3 -4
- velbusaio/messages/write_data_to_memory.py +3 -4
- velbusaio/messages/write_memory_block.py +3 -4
- velbusaio/messages/write_module_address_and_serial_number.py +3 -4
- velbusaio/module.py +680 -158
- 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 +243 -0
- velbusaio/py.typed +0 -0
- velbusaio/raw_message.py +149 -0
- velbusaio/util.py +55 -0
- velbusaio/vlp_reader.py +249 -0
- velbus_aio-2021.8.7.dist-info/METADATA +0 -66
- velbus_aio-2021.8.7.dist-info/RECORD +0 -90
- velbus_aio-2021.8.7.dist-info/top_level.txt +0 -1
- velbusaio/messages/meteo_raw.py +0 -52
- velbusaio/module_registry.py +0 -64
- velbusaio/moduleprotocol/protocol.json +0 -25540
- velbusaio/parser.py +0 -142
- {velbus_aio-2021.8.7.dist-info → velbus_aio-2025.11.0.dist-info/licenses}/LICENSE +0 -0
velbusaio/messages/cover_up.py
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
"""
|
|
2
2
|
:author: Tom Dupré <gitd8400@gmail.com>
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from __future__ import annotations
|
|
5
6
|
|
|
6
|
-
import json
|
|
7
7
|
import struct
|
|
8
8
|
|
|
9
|
-
from velbusaio.command_registry import
|
|
9
|
+
from velbusaio.command_registry import register
|
|
10
10
|
from velbusaio.message import Message
|
|
11
11
|
|
|
12
12
|
COMMAND_CODE = 0x05
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
@register(COMMAND_CODE, ["VMB1BLE", "VMB2BLE", "VMB1BLS", "VMB2BLE-10"])
|
|
15
16
|
class CoverUpMessage(Message):
|
|
16
17
|
"""
|
|
17
18
|
sent by:
|
|
@@ -35,15 +36,6 @@ class CoverUpMessage(Message):
|
|
|
35
36
|
self.channel = self.byte_to_channel(data[0])
|
|
36
37
|
(self.delay_time,) = struct.unpack(">L", bytes([0]) + data[1:])
|
|
37
38
|
|
|
38
|
-
def to_json(self):
|
|
39
|
-
"""
|
|
40
|
-
:return: str
|
|
41
|
-
"""
|
|
42
|
-
json_dict = self.to_json_basic()
|
|
43
|
-
json_dict["channel"] = self.channel
|
|
44
|
-
json_dict["delay_time"] = self.delay_time
|
|
45
|
-
return json.dumps(json_dict)
|
|
46
|
-
|
|
47
39
|
def set_defaults(self, address):
|
|
48
40
|
if address is not None:
|
|
49
41
|
self.set_address(address)
|
|
@@ -60,6 +52,7 @@ class CoverUpMessage(Message):
|
|
|
60
52
|
)
|
|
61
53
|
|
|
62
54
|
|
|
55
|
+
@register(COMMAND_CODE, ["VMB1BL", "VMB2BL"])
|
|
63
56
|
class CoverUpMessage2(Message):
|
|
64
57
|
"""
|
|
65
58
|
sent by:
|
|
@@ -87,15 +80,6 @@ class CoverUpMessage2(Message):
|
|
|
87
80
|
self.channel = self.byte_to_channel(tmp)
|
|
88
81
|
(self.delay_time,) = struct.unpack(">L", bytes([0]) + data[1:])
|
|
89
82
|
|
|
90
|
-
def to_json(self):
|
|
91
|
-
"""
|
|
92
|
-
:return: str
|
|
93
|
-
"""
|
|
94
|
-
json_dict = self.to_json_basic()
|
|
95
|
-
json_dict["channel"] = self.channel
|
|
96
|
-
json_dict["delay_time"] = self.delay_time
|
|
97
|
-
return json.dumps(json_dict)
|
|
98
|
-
|
|
99
83
|
def set_defaults(self, address):
|
|
100
84
|
if address is not None:
|
|
101
85
|
self.set_address(address)
|
|
@@ -111,10 +95,3 @@ class CoverUpMessage2(Message):
|
|
|
111
95
|
else:
|
|
112
96
|
tmp = 0x0C
|
|
113
97
|
return bytes([COMMAND_CODE, tmp]) + struct.pack(">L", self.delay_time)[-3:]
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
register_command(COMMAND_CODE, CoverUpMessage2, "VMB1BL")
|
|
117
|
-
register_command(COMMAND_CODE, CoverUpMessage2, "VMB2BL")
|
|
118
|
-
register_command(COMMAND_CODE, CoverUpMessage, "VMB1BLE")
|
|
119
|
-
register_command(COMMAND_CODE, CoverUpMessage, "VMB2BLE")
|
|
120
|
-
register_command(COMMAND_CODE, CoverUpMessage, "VMB1BLS")
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:author: Niels Laukens
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import dataclasses
|
|
8
|
+
import enum
|
|
9
|
+
|
|
10
|
+
from velbusaio.command_registry import register
|
|
11
|
+
from velbusaio.message import Message
|
|
12
|
+
|
|
13
|
+
COMMAND_CODE = 0xE8
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@register(COMMAND_CODE, ["VMBDALI", "VMBDALI-20"])
|
|
17
|
+
class DaliDeviceSettingMsg(Message):
|
|
18
|
+
"""
|
|
19
|
+
send by: VMBDALI
|
|
20
|
+
received by:
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, address: int | None = None):
|
|
24
|
+
super().__init__()
|
|
25
|
+
self.set_defaults(address)
|
|
26
|
+
self.channel: int = 0
|
|
27
|
+
|
|
28
|
+
def populate(self, priority, address: int, rtr: int, data: bytes) -> None:
|
|
29
|
+
self.needs_low_priority(priority)
|
|
30
|
+
self.needs_no_rtr(rtr)
|
|
31
|
+
self.set_attributes(priority, address, rtr)
|
|
32
|
+
|
|
33
|
+
self.needs_data(data, 2)
|
|
34
|
+
self.channel = data[0]
|
|
35
|
+
message_subtype = data[1]
|
|
36
|
+
self.data: bytes | DaliDeviceSettingSubMessage = data[2:]
|
|
37
|
+
try:
|
|
38
|
+
decode_class = DaliDeviceSetting(message_subtype).decode_class
|
|
39
|
+
if decode_class is not None:
|
|
40
|
+
self.data = decode_class.from_data(self.data)
|
|
41
|
+
except Exception:
|
|
42
|
+
# Unknown subtype or error while decoding; leave as bytes
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
def to_json_basic(self):
|
|
46
|
+
me = {
|
|
47
|
+
"name": str(self.__class__.__name__),
|
|
48
|
+
"priority": self.priority,
|
|
49
|
+
"rtr": self.rtr,
|
|
50
|
+
"channel": self.channel,
|
|
51
|
+
}
|
|
52
|
+
if isinstance(self.data, DaliDeviceSettingSubMessage):
|
|
53
|
+
me["data"] = self.data.to_json_basic()
|
|
54
|
+
else:
|
|
55
|
+
me["data"] = self.data.hex()
|
|
56
|
+
return me
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class DaliDeviceSettingSubMessage:
|
|
60
|
+
"""Abstract base class."""
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def from_data(cls, data: bytes) -> DaliDeviceSettingSubMessage:
|
|
64
|
+
raise NotImplementedError()
|
|
65
|
+
|
|
66
|
+
def to_data(self) -> bytes:
|
|
67
|
+
raise NotImplementedError()
|
|
68
|
+
|
|
69
|
+
def to_json_basic(self):
|
|
70
|
+
raise NotImplementedError()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class DeviceType(enum.Enum):
|
|
74
|
+
FluorecentLamp = 0
|
|
75
|
+
EmergencyLamp = 1
|
|
76
|
+
DischargeLamp = 2
|
|
77
|
+
LowVoltageLamp = 3
|
|
78
|
+
Dimmer = 4
|
|
79
|
+
ConversionToDc = 5
|
|
80
|
+
LedModule = 6
|
|
81
|
+
Relay = 7
|
|
82
|
+
ColorControl = 8
|
|
83
|
+
Sequencer = 9
|
|
84
|
+
DevicePresent = 254
|
|
85
|
+
NoDevicePresent = 255
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclasses.dataclass
|
|
89
|
+
class DeviceTypeMsg(DaliDeviceSettingSubMessage):
|
|
90
|
+
device_type: DeviceType
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def from_data(cls, data: bytes) -> DeviceTypeMsg:
|
|
94
|
+
if len(data) != 1:
|
|
95
|
+
raise ValueError("Expected 1 byte of data")
|
|
96
|
+
return DeviceTypeMsg(device_type=DeviceType(data[0]))
|
|
97
|
+
|
|
98
|
+
def to_data(self) -> bytes:
|
|
99
|
+
return bytes([self.device_type.value])
|
|
100
|
+
|
|
101
|
+
def to_json_basic(self):
|
|
102
|
+
return {
|
|
103
|
+
"submsg_type": self.__class__.__name__,
|
|
104
|
+
"device_type": self.device_type.name,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclasses.dataclass
|
|
109
|
+
class MemberOfGroupMsg(DaliDeviceSettingSubMessage):
|
|
110
|
+
member_of_group: list[bool]
|
|
111
|
+
|
|
112
|
+
@classmethod
|
|
113
|
+
def from_data(cls, data: bytes) -> DaliDeviceSettingSubMessage:
|
|
114
|
+
if len(data) != 2:
|
|
115
|
+
raise ValueError("Expected 2 bytes")
|
|
116
|
+
i = int.from_bytes(data, "little")
|
|
117
|
+
member_of_groups = []
|
|
118
|
+
for group_num in range(0, 16):
|
|
119
|
+
member_of_groups.append(bool(i & (1 << group_num)))
|
|
120
|
+
return MemberOfGroupMsg(member_of_groups)
|
|
121
|
+
|
|
122
|
+
def to_data(self) -> bytes:
|
|
123
|
+
i = 0
|
|
124
|
+
for group_num, member in enumerate(self.member_of_group):
|
|
125
|
+
if member:
|
|
126
|
+
i += 1 << group_num
|
|
127
|
+
return i.to_bytes(2, "little")
|
|
128
|
+
|
|
129
|
+
def to_json_basic(self):
|
|
130
|
+
return {
|
|
131
|
+
"member_of_groups": self.member_of_groups,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def member_of_groups(self) -> list[int]:
|
|
136
|
+
groups = []
|
|
137
|
+
for num, member in enumerate(self.member_of_group):
|
|
138
|
+
if member:
|
|
139
|
+
groups.append(num)
|
|
140
|
+
return groups
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class DaliDeviceSetting(enum.Enum):
|
|
144
|
+
def __new__(
|
|
145
|
+
cls, value: int, decode_class: type[DaliDeviceSettingSubMessage]
|
|
146
|
+
) -> DaliDeviceSetting:
|
|
147
|
+
obj = object.__new__(cls)
|
|
148
|
+
obj._value_ = value
|
|
149
|
+
obj.decode_class = decode_class
|
|
150
|
+
return obj
|
|
151
|
+
|
|
152
|
+
Scene0Level = (0, None)
|
|
153
|
+
Scene1Level = (1, None)
|
|
154
|
+
Scene2Level = (2, None)
|
|
155
|
+
Scene3Level = (3, None)
|
|
156
|
+
Scene4Level = (4, None)
|
|
157
|
+
Scene5Level = (5, None)
|
|
158
|
+
Scene6Level = (6, None)
|
|
159
|
+
Scene7Level = (7, None)
|
|
160
|
+
Scene8Level = (8, None)
|
|
161
|
+
Scene9Level = (9, None)
|
|
162
|
+
Scene10Level = (10, None)
|
|
163
|
+
Scene11Level = (11, None)
|
|
164
|
+
Scene12Level = (12, None)
|
|
165
|
+
Scene13Level = (13, None)
|
|
166
|
+
Scene14Level = (14, None)
|
|
167
|
+
Scene15Level = (15, None)
|
|
168
|
+
PowerOnLevel = (16, None)
|
|
169
|
+
SystemFailureLevel = (17, None)
|
|
170
|
+
MinimumLevel = (18, None)
|
|
171
|
+
MaximumLevel = (19, None)
|
|
172
|
+
FadeTimeAndRate = (20, None)
|
|
173
|
+
MemberOfGroup = (21, MemberOfGroupMsg)
|
|
174
|
+
GroupMembers0 = (22, None)
|
|
175
|
+
GroupMembers32 = (22, None)
|
|
176
|
+
# currently undefined 24
|
|
177
|
+
DeviceType = (25, DeviceTypeMsg)
|
|
178
|
+
ActualLevel = (26, None)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:author: Niels Laukens
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import enum
|
|
8
|
+
|
|
9
|
+
from velbusaio.command_registry import register
|
|
10
|
+
from velbusaio.message import Message
|
|
11
|
+
from velbusaio.messages.dali_device_settings import DaliDeviceSetting
|
|
12
|
+
|
|
13
|
+
COMMAND_CODE = 0xE7
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DataSource(enum.Enum):
|
|
17
|
+
FromMemory = 0
|
|
18
|
+
FromDaliDevice = 1
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@register(COMMAND_CODE, ["VMBDALI", "VMBDALI-20"])
|
|
22
|
+
class DaliDeviceSettingsRequest(Message):
|
|
23
|
+
"""
|
|
24
|
+
send by:
|
|
25
|
+
received by: VMBDALI
|
|
26
|
+
|
|
27
|
+
Note: requesting a single setting for all (81) channels does not work (no response)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, address: int | None = None):
|
|
31
|
+
super().__init__()
|
|
32
|
+
self.channel: int = None
|
|
33
|
+
self.data_source: DataSource = None
|
|
34
|
+
self.settings: DaliDeviceSetting = None
|
|
35
|
+
self.set_defaults(address)
|
|
36
|
+
|
|
37
|
+
def populate(self, priority, address: int, rtr: int, data: bytes) -> None:
|
|
38
|
+
self.needs_low_priority(priority)
|
|
39
|
+
self.needs_no_rtr(rtr)
|
|
40
|
+
self.needs_data(data, 2)
|
|
41
|
+
self.set_attributes(priority, address, rtr)
|
|
42
|
+
self.channel = data[0]
|
|
43
|
+
self.data_source = DataSource(data[1])
|
|
44
|
+
if len(data) >= 3:
|
|
45
|
+
self.settings = data[2]
|
|
46
|
+
else:
|
|
47
|
+
self.settings = None # all
|
|
48
|
+
|
|
49
|
+
def data_to_binary(self) -> bytes:
|
|
50
|
+
data = bytearray([COMMAND_CODE, self.channel, DataSource.FromMemory.value])
|
|
51
|
+
if self.settings is not None:
|
|
52
|
+
data.append(self.settings.value)
|
|
53
|
+
return bytes(data)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:author: Niels Laukens
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from velbusaio.command_registry import register
|
|
8
|
+
from velbusaio.message import Message
|
|
9
|
+
|
|
10
|
+
COMMAND_CODE = 0xA5
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@register(
|
|
14
|
+
COMMAND_CODE, ["VMBDALI", "VMBDALI-20", "VMB8DC-20", "VMB4LEDPWM-20", "VMB2DC-20"]
|
|
15
|
+
)
|
|
16
|
+
class DimValueStatus(Message):
|
|
17
|
+
"""
|
|
18
|
+
send by: VMBDALI
|
|
19
|
+
received by:
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, address: int = None) -> None:
|
|
23
|
+
super().__init__()
|
|
24
|
+
self.set_defaults(address)
|
|
25
|
+
self.channel: int = 0
|
|
26
|
+
self.dim_values: list[int] = []
|
|
27
|
+
# dim_values contain dim value of channel, channel+1, ...
|
|
28
|
+
|
|
29
|
+
def populate(self, priority, address: int, rtr: int, data: int) -> None:
|
|
30
|
+
self.needs_low_priority(priority)
|
|
31
|
+
self.needs_no_rtr(rtr)
|
|
32
|
+
self.set_attributes(priority, address, rtr)
|
|
33
|
+
|
|
34
|
+
self.needs_data(data, 2)
|
|
35
|
+
self.channel = data[0]
|
|
36
|
+
self.dim_values = list(data[1:])
|
|
37
|
+
|
|
38
|
+
def data_to_binary(self) -> bytes:
|
|
39
|
+
return bytes(
|
|
40
|
+
[
|
|
41
|
+
COMMAND_CODE,
|
|
42
|
+
self.channel,
|
|
43
|
+
]
|
|
44
|
+
) + bytes(self.dim_values)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
2
|
:author: Frank van Breugel
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from __future__ import annotations
|
|
5
6
|
|
|
6
|
-
import json
|
|
7
7
|
import struct
|
|
8
8
|
|
|
9
|
-
from velbusaio.command_registry import
|
|
9
|
+
from velbusaio.command_registry import register
|
|
10
10
|
from velbusaio.message import Message
|
|
11
11
|
|
|
12
12
|
COMMAND_CODE = 0xB8
|
|
@@ -22,6 +22,10 @@ LED_FAST_BLINKING = 1 << 5
|
|
|
22
22
|
LED_VERY_FAST_BLINKING = 1 << 4
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
@register(
|
|
26
|
+
COMMAND_CODE,
|
|
27
|
+
["VMB4DC", "VMBDMI", "VMBDMI-R", "VMB8DC-20", "VMB4LEDPWM-20", "VMB2DC-20"],
|
|
28
|
+
)
|
|
25
29
|
class DimmerChannelStatusMessage(Message):
|
|
26
30
|
"""
|
|
27
31
|
sent by: VMB4DC
|
|
@@ -51,18 +55,6 @@ class DimmerChannelStatusMessage(Message):
|
|
|
51
55
|
self.led_status = data[3]
|
|
52
56
|
(self.delay_time,) = struct.unpack(">L", bytes([0]) + data[4:])
|
|
53
57
|
|
|
54
|
-
def to_json(self):
|
|
55
|
-
"""
|
|
56
|
-
:return: str
|
|
57
|
-
"""
|
|
58
|
-
json_dict = self.to_json_basic()
|
|
59
|
-
json_dict["channel"] = self.channel
|
|
60
|
-
json_dict["disable_inhibit_forced"] = self.disable_inhibit_forced
|
|
61
|
-
json_dict["dimmer_state"] = self.dimmer_state
|
|
62
|
-
json_dict["led_status"] = self.led_status
|
|
63
|
-
json_dict["delay_time"] = self.delay_time
|
|
64
|
-
return json.dumps(json_dict)
|
|
65
|
-
|
|
66
58
|
def is_normal(self):
|
|
67
59
|
"""
|
|
68
60
|
:return: bool
|
|
@@ -109,8 +101,3 @@ class DimmerChannelStatusMessage(Message):
|
|
|
109
101
|
)
|
|
110
102
|
+ struct.pack(">L", self.delay_time)[-3:]
|
|
111
103
|
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
register_command(COMMAND_CODE, DimmerChannelStatusMessage, "VMB4DC")
|
|
115
|
-
register_command(COMMAND_CODE, DimmerChannelStatusMessage, "VMBDMI")
|
|
116
|
-
register_command(COMMAND_CODE, DimmerChannelStatusMessage, "VMBDMI-R")
|
|
@@ -1,43 +1,42 @@
|
|
|
1
1
|
"""
|
|
2
2
|
:author: Frank van Breugel
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from __future__ import annotations
|
|
5
6
|
|
|
6
|
-
import json
|
|
7
7
|
import struct
|
|
8
8
|
|
|
9
|
-
from velbusaio.command_registry import
|
|
9
|
+
from velbusaio.command_registry import register
|
|
10
10
|
from velbusaio.message import Message
|
|
11
11
|
|
|
12
12
|
COMMAND_CODE = 0xEE
|
|
13
|
-
|
|
14
13
|
MODE_START_STOP = 0x00
|
|
15
|
-
|
|
16
14
|
MODE_STAIRCASE = 0x01
|
|
17
|
-
|
|
18
15
|
MODE_DIMMER = 0x02
|
|
19
|
-
|
|
20
16
|
MODE_MEMORY = 0x03
|
|
21
|
-
|
|
22
17
|
MODE_MULTI = 0x04
|
|
23
|
-
|
|
24
18
|
MODE_SLOW_ON = 0x05
|
|
25
|
-
|
|
26
19
|
MODE_SLOW_OFF = 0x06
|
|
27
|
-
|
|
28
20
|
MODE_SLOW = 0x06
|
|
29
|
-
|
|
30
21
|
LED_OFF = 0
|
|
31
|
-
|
|
32
22
|
LED_ON = 1 << 7
|
|
33
|
-
|
|
34
23
|
LED_SLOW_BLINKING = 1 << 6
|
|
35
|
-
|
|
36
24
|
LED_FAST_BLINKING = 1 << 5
|
|
37
|
-
|
|
38
25
|
LED_VERY_FAST_BLINKING = 1 << 4
|
|
39
26
|
|
|
40
27
|
|
|
28
|
+
@register(
|
|
29
|
+
COMMAND_CODE,
|
|
30
|
+
[
|
|
31
|
+
"VMB1DM",
|
|
32
|
+
"VMBDME",
|
|
33
|
+
"VMB1LED",
|
|
34
|
+
"VMBDALI",
|
|
35
|
+
"VMBDALI-20",
|
|
36
|
+
"VMB8DC-20",
|
|
37
|
+
"VMB4LEDPWM-20",
|
|
38
|
+
],
|
|
39
|
+
)
|
|
41
40
|
class DimmerStatusMessage(Message):
|
|
42
41
|
"""
|
|
43
42
|
sent by: VMBDME
|
|
@@ -70,18 +69,6 @@ class DimmerStatusMessage(Message):
|
|
|
70
69
|
(self.delay_time,) = struct.unpack(">L", bytes([0]) + data[4:])
|
|
71
70
|
self.dimmer_config = data[6]
|
|
72
71
|
|
|
73
|
-
def to_json(self):
|
|
74
|
-
"""
|
|
75
|
-
:return: str
|
|
76
|
-
"""
|
|
77
|
-
json_dict = self.to_json_basic()
|
|
78
|
-
json_dict["channel"] = self.channel
|
|
79
|
-
json_dict["dimmer_mode"] = self.dimmer_mode
|
|
80
|
-
json_dict["dimmer_state"] = self.dimmer_state
|
|
81
|
-
json_dict["led_status"] = self.led_status
|
|
82
|
-
json_dict["delay_time"] = self.delay_time
|
|
83
|
-
return json.dumps(json_dict)
|
|
84
|
-
|
|
85
72
|
def is_start_stop(self):
|
|
86
73
|
"""
|
|
87
74
|
:return: bool
|
|
@@ -151,7 +138,3 @@ class DimmerStatusMessage(Message):
|
|
|
151
138
|
)
|
|
152
139
|
+ struct.pack(">L", self.delay_time)[-3:]
|
|
153
140
|
)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
register_command(COMMAND_CODE, DimmerStatusMessage, "VMBDME")
|
|
157
|
-
register_command(COMMAND_CODE, DimmerStatusMessage, "VMB1LED")
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import IntEnum
|
|
4
|
+
|
|
5
|
+
from velbusaio.command_registry import register
|
|
6
|
+
from velbusaio.message import Message
|
|
7
|
+
|
|
8
|
+
COMMAND_CODE = 0xD4
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CustomColorPriority(IntEnum):
|
|
12
|
+
LOW_PRIORITY = 1
|
|
13
|
+
MID_PRIORITY = 2
|
|
14
|
+
HIGH_PRIORITY = 3
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@register(COMMAND_CODE, ["VMBEL1", "VMBEL2", "VMBEL4", "VMBELO"])
|
|
18
|
+
class SetEdgeColorMessage(Message):
|
|
19
|
+
"""
|
|
20
|
+
send by:
|
|
21
|
+
received by: VMBEL1, VMBEL2, VMBEL4, VMBELO
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
apply_background_color = False
|
|
25
|
+
apply_continuous_feedback_color = False
|
|
26
|
+
apply_slow_blinking_feedback_color = False
|
|
27
|
+
apply_fast_blinking_feedback_color = False
|
|
28
|
+
custom_color_palette = False
|
|
29
|
+
|
|
30
|
+
apply_to_left_edge = False
|
|
31
|
+
apply_to_top_edge = False
|
|
32
|
+
apply_to_right_edge = False
|
|
33
|
+
apply_to_bottom_edge = False
|
|
34
|
+
|
|
35
|
+
apply_to_page: int | None = None
|
|
36
|
+
apply_to_all_pages = False
|
|
37
|
+
|
|
38
|
+
background_blinking = False
|
|
39
|
+
custom_color_priority: CustomColorPriority | None = None
|
|
40
|
+
|
|
41
|
+
color_idx: int = 0
|
|
42
|
+
|
|
43
|
+
def populate(self, priority, address, rtr, data):
|
|
44
|
+
"""
|
|
45
|
+
:return: None
|
|
46
|
+
"""
|
|
47
|
+
self.needs_no_rtr(rtr)
|
|
48
|
+
self.set_attributes(priority, address, rtr)
|
|
49
|
+
|
|
50
|
+
self.apply_background_color = bool(data[0] & 0x01)
|
|
51
|
+
self.apply_continuous_feedback_color = bool(data[0] & 0x02)
|
|
52
|
+
self.apply_slow_blinking_feedback_color = bool(data[0] & 0x04)
|
|
53
|
+
self.apply_fast_blinking_feedback_color = bool(data[0] & 0x08)
|
|
54
|
+
self.custom_color_palette = bool(data[0] & 0x80)
|
|
55
|
+
|
|
56
|
+
self.apply_to_left_edge = bool(data[1] & 0x01)
|
|
57
|
+
self.apply_to_top_edge = bool(data[1] & 0x02)
|
|
58
|
+
self.apply_to_right_edge = bool(data[1] & 0x04)
|
|
59
|
+
self.apply_to_bottom_edge = bool(data[1] & 0x08)
|
|
60
|
+
|
|
61
|
+
page = (data[1] & 0b0111_0000) >> 4
|
|
62
|
+
if page > 0:
|
|
63
|
+
self.apply_to_page = page
|
|
64
|
+
self.apply_to_all_pages = bool(data[1] & 0x80)
|
|
65
|
+
|
|
66
|
+
self.background_blinking = bool(data[2] & 0x80)
|
|
67
|
+
|
|
68
|
+
custom_color_priority_value = data[2] & 0b0110_0000 >> 5
|
|
69
|
+
if custom_color_priority_value:
|
|
70
|
+
self.custom_color_priority = CustomColorPriority(
|
|
71
|
+
custom_color_priority_value
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
self.color_idx = data[2] & 0b0001_1111
|
|
75
|
+
|
|
76
|
+
def data_to_binary(self):
|
|
77
|
+
"""
|
|
78
|
+
:return: bytes
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
byte_2 = (
|
|
82
|
+
self.apply_background_color * 0x01
|
|
83
|
+
+ self.apply_continuous_feedback_color * 0x02
|
|
84
|
+
+ self.apply_slow_blinking_feedback_color * 0x04
|
|
85
|
+
+ self.apply_fast_blinking_feedback_color * 0x08
|
|
86
|
+
+ self.custom_color_palette * 0x80
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
byte_3 = (
|
|
90
|
+
self.apply_to_left_edge * 0x01
|
|
91
|
+
+ self.apply_to_top_edge * 0x02
|
|
92
|
+
+ self.apply_to_right_edge * 0x04
|
|
93
|
+
+ self.apply_to_bottom_edge * 0x08
|
|
94
|
+
+ (((self.apply_to_page or 0) & 0xFFF) << 4)
|
|
95
|
+
+ self.apply_to_all_pages * 0x80
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
byte_4 = (
|
|
99
|
+
(self.color_idx & 0xFFFF)
|
|
100
|
+
+ (
|
|
101
|
+
(int(self.custom_color_priority) if self.custom_color_priority else 0)
|
|
102
|
+
<< 5
|
|
103
|
+
)
|
|
104
|
+
+ self.background_blinking * 0x80
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return bytes(
|
|
108
|
+
[
|
|
109
|
+
COMMAND_CODE,
|
|
110
|
+
byte_2,
|
|
111
|
+
byte_3,
|
|
112
|
+
byte_4,
|
|
113
|
+
]
|
|
114
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:author: Maikel Punie <maikel.punie@gmail.com>
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from velbusaio.command_registry import register
|
|
8
|
+
from velbusaio.message import Message
|
|
9
|
+
|
|
10
|
+
COMMAND_CODE = 0xD4
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@register(COMMAND_CODE, ["VMBEL1", "VMBEL2", "VMBEL4", "VMBELO"])
|
|
14
|
+
class EdgeSetCustomColor(Message):
|
|
15
|
+
"""
|
|
16
|
+
send by:
|
|
17
|
+
received by: VMBEL1, VMBEL2, VMBEL4, VMBELO
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, address=None):
|
|
21
|
+
Message.__init__(self)
|
|
22
|
+
self.set_defaults(address)
|
|
23
|
+
self.pallet = 31
|
|
24
|
+
self.rgb = False
|
|
25
|
+
self.saturation = 0
|
|
26
|
+
self.red = 0
|
|
27
|
+
self.green = 0
|
|
28
|
+
self.blue = 0
|
|
29
|
+
|
|
30
|
+
def populate(self, priority, address, rtr, data):
|
|
31
|
+
"""
|
|
32
|
+
:return: None
|
|
33
|
+
"""
|
|
34
|
+
self.needs_no_rtr(rtr)
|
|
35
|
+
self.set_attributes(priority, address, rtr)
|
|
36
|
+
self.pallet = data[0]
|
|
37
|
+
self.rgb = bool(data[1] & 0x80)
|
|
38
|
+
self.saturation = data[1] & 0x7F
|
|
39
|
+
self.red = data[2]
|
|
40
|
+
self.green = data[3]
|
|
41
|
+
self.blue = data[4]
|
|
42
|
+
|
|
43
|
+
def data_to_binary(self):
|
|
44
|
+
"""
|
|
45
|
+
:return: bytes
|
|
46
|
+
"""
|
|
47
|
+
return bytes(
|
|
48
|
+
[
|
|
49
|
+
COMMAND_CODE,
|
|
50
|
+
self.pallet,
|
|
51
|
+
((self.rgb << 7) + self.saturation),
|
|
52
|
+
self.red,
|
|
53
|
+
self.green,
|
|
54
|
+
self.blue,
|
|
55
|
+
]
|
|
56
|
+
)
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
"""
|
|
2
2
|
:author: Thomas Delaet <thomas@delaet.org>
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from __future__ import annotations
|
|
5
6
|
|
|
6
|
-
from velbusaio.command_registry import
|
|
7
|
+
from velbusaio.command_registry import register
|
|
7
8
|
from velbusaio.message import Message
|
|
8
9
|
|
|
9
10
|
COMMAND_CODE = 0xF8
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
@register(COMMAND_CODE)
|
|
12
14
|
class FastBlinkingLedMessage(Message):
|
|
13
15
|
"""
|
|
14
16
|
send by: VMB4RYLD
|
|
@@ -35,6 +37,3 @@ class FastBlinkingLedMessage(Message):
|
|
|
35
37
|
:return: bytes
|
|
36
38
|
"""
|
|
37
39
|
return bytes([COMMAND_CODE, self.channels_to_byte(self.leds)])
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
register_command(COMMAND_CODE, FastBlinkingLedMessage)
|
velbusaio/messages/forced_off.py
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
"""
|
|
2
2
|
:author: Maikel Punie <maikel.punie@gmail.com>
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from __future__ import annotations
|
|
5
6
|
|
|
6
|
-
from velbusaio.command_registry import
|
|
7
|
+
from velbusaio.command_registry import register
|
|
7
8
|
from velbusaio.message import Message
|
|
8
9
|
|
|
9
10
|
COMMAND_CODE = 0x12
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
@register(COMMAND_CODE)
|
|
12
14
|
class ForcedOff(Message):
|
|
13
15
|
def populate(self, priority, address, rtr, data):
|
|
14
16
|
"""
|
|
@@ -23,6 +25,3 @@ class ForcedOff(Message):
|
|
|
23
25
|
:return: bytes
|
|
24
26
|
"""
|
|
25
27
|
return bytes([COMMAND_CODE])
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
register_command(COMMAND_CODE, ForcedOff)
|