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.
Files changed (200) hide show
  1. scripts/parse_specs.py +156 -0
  2. velbus_aio-2025.11.0.dist-info/METADATA +71 -0
  3. velbus_aio-2025.11.0.dist-info/RECORD +194 -0
  4. {velbus_aio-2021.8.7.dist-info → velbus_aio-2025.11.0.dist-info}/WHEEL +1 -1
  5. velbus_aio-2025.11.0.dist-info/top_level.txt +3 -0
  6. velbusaio/channels.py +443 -109
  7. velbusaio/command_registry.py +126 -13
  8. velbusaio/const.py +36 -12
  9. velbusaio/controller.py +252 -177
  10. velbusaio/discovery.py +2 -2
  11. velbusaio/exceptions.py +22 -0
  12. velbusaio/handler.py +311 -145
  13. velbusaio/helpers.py +6 -18
  14. velbusaio/message.py +46 -132
  15. velbusaio/messages/__init__.py +12 -2
  16. velbusaio/messages/blind_status.py +16 -25
  17. velbusaio/messages/bus_active.py +3 -9
  18. velbusaio/messages/bus_error_counter_status.py +3 -4
  19. velbusaio/messages/bus_error_counter_status_request.py +3 -4
  20. velbusaio/messages/bus_off.py +3 -4
  21. velbusaio/messages/channel_name_part1.py +49 -33
  22. velbusaio/messages/channel_name_part2.py +49 -33
  23. velbusaio/messages/channel_name_part3.py +49 -33
  24. velbusaio/messages/channel_name_request.py +26 -12
  25. velbusaio/messages/clear_led.py +3 -4
  26. velbusaio/messages/counter_status.py +3 -17
  27. velbusaio/messages/counter_status_request.py +6 -6
  28. velbusaio/messages/counter_value.py +44 -0
  29. velbusaio/messages/cover_down.py +4 -29
  30. velbusaio/messages/cover_off.py +5 -29
  31. velbusaio/messages/cover_position.py +4 -19
  32. velbusaio/messages/cover_up.py +4 -27
  33. velbusaio/messages/dali_device_settings.py +178 -0
  34. velbusaio/messages/dali_device_settings_request.py +53 -0
  35. velbusaio/messages/dali_dim_value_status.py +44 -0
  36. velbusaio/messages/dimmer_channel_status.py +6 -19
  37. velbusaio/messages/dimmer_status.py +14 -31
  38. velbusaio/messages/edge_set_color.py +114 -0
  39. velbusaio/messages/edge_set_custom_color.py +56 -0
  40. velbusaio/messages/fast_blinking_led.py +3 -4
  41. velbusaio/messages/forced_off.py +3 -4
  42. velbusaio/messages/forced_on.py +3 -4
  43. velbusaio/messages/interface_status_request.py +3 -4
  44. velbusaio/messages/ir_receiver_status.py +18 -0
  45. velbusaio/messages/kwh_status.py +3 -19
  46. velbusaio/messages/light_value_request.py +3 -4
  47. velbusaio/messages/memo_text.py +3 -5
  48. velbusaio/messages/memory_data.py +3 -16
  49. velbusaio/messages/memory_data_block.py +3 -4
  50. velbusaio/messages/memory_dump_request.py +3 -4
  51. velbusaio/messages/module_status.py +107 -55
  52. velbusaio/messages/module_status_request.py +7 -6
  53. velbusaio/messages/module_subtype.py +11 -19
  54. velbusaio/messages/module_type.py +132 -21
  55. velbusaio/messages/module_type_request.py +1 -0
  56. velbusaio/messages/psu_load.py +56 -0
  57. velbusaio/messages/psu_values.py +53 -0
  58. velbusaio/messages/push_button_status.py +3 -16
  59. velbusaio/messages/raw.py +74 -0
  60. velbusaio/messages/read_data_block_from_memory.py +3 -4
  61. velbusaio/messages/read_data_from_memory.py +3 -4
  62. velbusaio/messages/realtime_clock_status_request.py +3 -4
  63. velbusaio/messages/receive_buffer_full.py +3 -4
  64. velbusaio/messages/receive_ready.py +3 -4
  65. velbusaio/messages/relay_status.py +13 -42
  66. velbusaio/messages/restore_dimmer.py +33 -24
  67. velbusaio/messages/select_program.py +35 -0
  68. velbusaio/messages/sensor_settings_request.py +3 -4
  69. velbusaio/messages/sensor_temp_request.py +3 -4
  70. velbusaio/messages/sensor_temperature.py +15 -19
  71. velbusaio/messages/set_date.py +10 -30
  72. velbusaio/messages/set_daylight_saving.py +8 -24
  73. velbusaio/messages/set_dimmer.py +43 -41
  74. velbusaio/messages/set_led.py +3 -4
  75. velbusaio/messages/set_realtime_clock.py +10 -30
  76. velbusaio/messages/set_temperature.py +3 -4
  77. velbusaio/messages/slider_status.py +16 -20
  78. velbusaio/messages/slow_blinking_led.py +3 -4
  79. velbusaio/messages/start_relay_blinking_timer.py +3 -4
  80. velbusaio/messages/start_relay_timer.py +3 -4
  81. velbusaio/messages/switch_relay_off.py +3 -16
  82. velbusaio/messages/switch_relay_on.py +3 -16
  83. velbusaio/messages/switch_to_comfort.py +4 -15
  84. velbusaio/messages/switch_to_day.py +4 -15
  85. velbusaio/messages/switch_to_night.py +4 -15
  86. velbusaio/messages/switch_to_safe.py +4 -15
  87. velbusaio/messages/temp_sensor_settings_part1.py +3 -4
  88. velbusaio/messages/temp_sensor_settings_part2.py +27 -0
  89. velbusaio/messages/temp_sensor_settings_part3.py +27 -0
  90. velbusaio/messages/temp_sensor_settings_part4.py +27 -0
  91. velbusaio/messages/temp_sensor_settings_request.py +3 -4
  92. velbusaio/messages/temp_sensor_status.py +34 -35
  93. velbusaio/messages/temp_set_cooling.py +3 -13
  94. velbusaio/messages/temp_set_heating.py +3 -13
  95. velbusaio/messages/update_led_status.py +3 -4
  96. velbusaio/messages/very_fast_blinking_led.py +3 -4
  97. velbusaio/messages/write_data_to_memory.py +3 -4
  98. velbusaio/messages/write_memory_block.py +3 -4
  99. velbusaio/messages/write_module_address_and_serial_number.py +3 -4
  100. velbusaio/module.py +680 -158
  101. velbusaio/module_spec/01.json +62 -0
  102. velbusaio/module_spec/02.json +16 -0
  103. velbusaio/module_spec/03.json +23 -0
  104. velbusaio/module_spec/04.json +283 -0
  105. velbusaio/module_spec/05.json +54 -0
  106. velbusaio/module_spec/06.json +110 -0
  107. velbusaio/module_spec/07.json +16 -0
  108. velbusaio/module_spec/08.json +38 -0
  109. velbusaio/module_spec/09.json +30 -0
  110. velbusaio/module_spec/0A.json +58 -0
  111. velbusaio/module_spec/0B.json +58 -0
  112. velbusaio/module_spec/0C.json +18 -0
  113. velbusaio/module_spec/0E.json +25 -0
  114. velbusaio/module_spec/0F.json +16 -0
  115. velbusaio/module_spec/10.json +111 -0
  116. velbusaio/module_spec/11.json +111 -0
  117. velbusaio/module_spec/12.json +73 -0
  118. velbusaio/module_spec/13.json +4 -0
  119. velbusaio/module_spec/14.json +16 -0
  120. velbusaio/module_spec/15.json +83 -0
  121. velbusaio/module_spec/16.json +129 -0
  122. velbusaio/module_spec/17.json +129 -0
  123. velbusaio/module_spec/18.json +129 -0
  124. velbusaio/module_spec/1A.json +79 -0
  125. velbusaio/module_spec/1B.json +107 -0
  126. velbusaio/module_spec/1D.json +89 -0
  127. velbusaio/module_spec/1E.json +306 -0
  128. velbusaio/module_spec/1F.json +178 -0
  129. velbusaio/module_spec/20.json +178 -0
  130. velbusaio/module_spec/21.json +326 -0
  131. velbusaio/module_spec/22.json +426 -0
  132. velbusaio/module_spec/23.json +129 -0
  133. velbusaio/module_spec/24.json +30 -0
  134. velbusaio/module_spec/25.json +3 -0
  135. velbusaio/module_spec/28.json +454 -0
  136. velbusaio/module_spec/29.json +235 -0
  137. velbusaio/module_spec/2A.json +239 -0
  138. velbusaio/module_spec/2B.json +239 -0
  139. velbusaio/module_spec/2C.json +257 -0
  140. velbusaio/module_spec/2D.json +270 -0
  141. velbusaio/module_spec/2E.json +215 -0
  142. velbusaio/module_spec/2F.json +211 -0
  143. velbusaio/module_spec/30.json +58 -0
  144. velbusaio/module_spec/31.json +465 -0
  145. velbusaio/module_spec/32.json +385 -0
  146. velbusaio/module_spec/33.json +249 -0
  147. velbusaio/module_spec/34.json +313 -0
  148. velbusaio/module_spec/35.json +313 -0
  149. velbusaio/module_spec/36.json +313 -0
  150. velbusaio/module_spec/37.json +333 -0
  151. velbusaio/module_spec/38.json +111 -0
  152. velbusaio/module_spec/39.json +4 -0
  153. velbusaio/module_spec/3A.json +306 -0
  154. velbusaio/module_spec/3B.json +306 -0
  155. velbusaio/module_spec/3C.json +306 -0
  156. velbusaio/module_spec/3D.json +454 -0
  157. velbusaio/module_spec/3E.json +302 -0
  158. velbusaio/module_spec/3F.json +4 -0
  159. velbusaio/module_spec/40.json +4 -0
  160. velbusaio/module_spec/41.json +241 -0
  161. velbusaio/module_spec/42.json +4 -0
  162. velbusaio/module_spec/43.json +23 -0
  163. velbusaio/module_spec/44.json +38 -0
  164. velbusaio/module_spec/45.json +4 -0
  165. velbusaio/module_spec/48.json +111 -0
  166. velbusaio/module_spec/49.json +111 -0
  167. velbusaio/module_spec/4A.json +89 -0
  168. velbusaio/module_spec/4B.json +138 -0
  169. velbusaio/module_spec/4C.json +129 -0
  170. velbusaio/module_spec/4D.json +108 -0
  171. velbusaio/module_spec/4E.json +787 -0
  172. velbusaio/module_spec/4F.json +114 -0
  173. velbusaio/module_spec/50.json +114 -0
  174. velbusaio/module_spec/51.json +114 -0
  175. velbusaio/module_spec/52.json +456 -0
  176. velbusaio/module_spec/54.json +270 -0
  177. velbusaio/module_spec/55.json +270 -0
  178. velbusaio/module_spec/56.json +270 -0
  179. velbusaio/module_spec/57.json +260 -0
  180. velbusaio/module_spec/5A.json +4 -0
  181. velbusaio/module_spec/5B.json +4 -0
  182. velbusaio/module_spec/5C.json +90 -0
  183. velbusaio/module_spec/5F.json +78 -0
  184. velbusaio/module_spec/60.json +4 -0
  185. velbusaio/module_spec/61.json +89 -0
  186. velbusaio/module_spec/broadcast.json +67 -0
  187. velbusaio/module_spec/ignore.json +22 -0
  188. velbusaio/protocol.py +243 -0
  189. velbusaio/py.typed +0 -0
  190. velbusaio/raw_message.py +149 -0
  191. velbusaio/util.py +55 -0
  192. velbusaio/vlp_reader.py +249 -0
  193. velbus_aio-2021.8.7.dist-info/METADATA +0 -66
  194. velbus_aio-2021.8.7.dist-info/RECORD +0 -90
  195. velbus_aio-2021.8.7.dist-info/top_level.txt +0 -1
  196. velbusaio/messages/meteo_raw.py +0 -52
  197. velbusaio/module_registry.py +0 -64
  198. velbusaio/moduleprotocol/protocol.json +0 -25540
  199. velbusaio/parser.py +0 -142
  200. {velbus_aio-2021.8.7.dist-info → velbus_aio-2025.11.0.dist-info/licenses}/LICENSE +0 -0
@@ -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 register_command
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 register_command
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 register_command
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 register_command
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)
@@ -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 register_command
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)