pyvlx 0.2.27__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 (84) hide show
  1. pyvlx/__init__.py +21 -0
  2. pyvlx/api/__init__.py +23 -0
  3. pyvlx/api/activate_scene.py +63 -0
  4. pyvlx/api/api_event.py +73 -0
  5. pyvlx/api/command_send.py +85 -0
  6. pyvlx/api/factory_default.py +34 -0
  7. pyvlx/api/frame_creation.py +202 -0
  8. pyvlx/api/frames/__init__.py +76 -0
  9. pyvlx/api/frames/alias_array.py +45 -0
  10. pyvlx/api/frames/frame.py +56 -0
  11. pyvlx/api/frames/frame_activate_scene.py +92 -0
  12. pyvlx/api/frames/frame_activation_log_updated.py +14 -0
  13. pyvlx/api/frames/frame_command_send.py +280 -0
  14. pyvlx/api/frames/frame_discover_nodes.py +64 -0
  15. pyvlx/api/frames/frame_error_notification.py +42 -0
  16. pyvlx/api/frames/frame_facory_default.py +32 -0
  17. pyvlx/api/frames/frame_get_all_nodes_information.py +218 -0
  18. pyvlx/api/frames/frame_get_limitation.py +127 -0
  19. pyvlx/api/frames/frame_get_local_time.py +38 -0
  20. pyvlx/api/frames/frame_get_network_setup.py +64 -0
  21. pyvlx/api/frames/frame_get_node_information.py +223 -0
  22. pyvlx/api/frames/frame_get_protocol_version.py +53 -0
  23. pyvlx/api/frames/frame_get_scene_list.py +82 -0
  24. pyvlx/api/frames/frame_get_state.py +47 -0
  25. pyvlx/api/frames/frame_get_version.py +72 -0
  26. pyvlx/api/frames/frame_helper.py +40 -0
  27. pyvlx/api/frames/frame_house_status_monitor_disable_cfm.py +14 -0
  28. pyvlx/api/frames/frame_house_status_monitor_disable_req.py +14 -0
  29. pyvlx/api/frames/frame_house_status_monitor_enable_cfm.py +14 -0
  30. pyvlx/api/frames/frame_house_status_monitor_enable_req.py +14 -0
  31. pyvlx/api/frames/frame_leave_learn_state.py +41 -0
  32. pyvlx/api/frames/frame_node_information_changed.py +57 -0
  33. pyvlx/api/frames/frame_node_state_position_changed_notification.py +84 -0
  34. pyvlx/api/frames/frame_password_change.py +114 -0
  35. pyvlx/api/frames/frame_password_enter.py +70 -0
  36. pyvlx/api/frames/frame_reboot.py +32 -0
  37. pyvlx/api/frames/frame_set_node_name.py +73 -0
  38. pyvlx/api/frames/frame_set_utc.py +45 -0
  39. pyvlx/api/frames/frame_status_request.py +212 -0
  40. pyvlx/api/get_all_nodes_information.py +46 -0
  41. pyvlx/api/get_limitation.py +64 -0
  42. pyvlx/api/get_local_time.py +34 -0
  43. pyvlx/api/get_network_setup.py +34 -0
  44. pyvlx/api/get_node_information.py +42 -0
  45. pyvlx/api/get_protocol_version.py +40 -0
  46. pyvlx/api/get_scene_list.py +49 -0
  47. pyvlx/api/get_state.py +43 -0
  48. pyvlx/api/get_version.py +34 -0
  49. pyvlx/api/house_status_monitor.py +52 -0
  50. pyvlx/api/leave_learn_state.py +33 -0
  51. pyvlx/api/password_enter.py +39 -0
  52. pyvlx/api/reboot.py +33 -0
  53. pyvlx/api/session_id.py +20 -0
  54. pyvlx/api/set_node_name.py +32 -0
  55. pyvlx/api/set_utc.py +31 -0
  56. pyvlx/api/status_request.py +48 -0
  57. pyvlx/config.py +54 -0
  58. pyvlx/connection.py +182 -0
  59. pyvlx/const.py +685 -0
  60. pyvlx/dataobjects.py +161 -0
  61. pyvlx/discovery.py +100 -0
  62. pyvlx/exception.py +26 -0
  63. pyvlx/heartbeat.py +79 -0
  64. pyvlx/klf200gateway.py +167 -0
  65. pyvlx/lightening_device.py +102 -0
  66. pyvlx/log.py +4 -0
  67. pyvlx/node.py +74 -0
  68. pyvlx/node_helper.py +165 -0
  69. pyvlx/node_updater.py +162 -0
  70. pyvlx/nodes.py +99 -0
  71. pyvlx/on_off_switch.py +44 -0
  72. pyvlx/opening_device.py +644 -0
  73. pyvlx/parameter.py +357 -0
  74. pyvlx/py.typed +0 -0
  75. pyvlx/pyvlx.py +124 -0
  76. pyvlx/scene.py +53 -0
  77. pyvlx/scenes.py +60 -0
  78. pyvlx/slip.py +48 -0
  79. pyvlx/string_helper.py +20 -0
  80. pyvlx-0.2.27.dist-info/METADATA +122 -0
  81. pyvlx-0.2.27.dist-info/RECORD +84 -0
  82. pyvlx-0.2.27.dist-info/WHEEL +5 -0
  83. pyvlx-0.2.27.dist-info/licenses/LICENSE +165 -0
  84. pyvlx-0.2.27.dist-info/top_level.txt +1 -0
@@ -0,0 +1,218 @@
1
+ """Module for get all node information from gateway."""
2
+ import struct
3
+ from datetime import datetime
4
+ from enum import Enum
5
+ from typing import Optional
6
+
7
+ from pyvlx.const import (
8
+ Command, NodeTypeWithSubtype, NodeVariation, OperatingState, Velocity)
9
+ from pyvlx.exception import PyVLXException
10
+ from pyvlx.parameter import Parameter
11
+ from pyvlx.string_helper import bytes_to_string, string_to_bytes
12
+
13
+ from .alias_array import AliasArray
14
+ from .frame import FrameBase
15
+
16
+
17
+ class FrameGetAllNodesInformationRequest(FrameBase):
18
+ """Frame for get node information request."""
19
+
20
+ PAYLOAD_LEN = 0
21
+
22
+ def __init__(self) -> None:
23
+ """Init Frame."""
24
+ super().__init__(Command.GW_GET_ALL_NODES_INFORMATION_REQ)
25
+
26
+
27
+ class AllNodesInformationStatus(Enum):
28
+ # pylint: disable=invalid-name
29
+ """Enum for node information status."""
30
+
31
+ OK = 0
32
+ Error_System_Table_Empty = 1
33
+
34
+
35
+ class FrameGetAllNodesInformationConfirmation(FrameBase):
36
+ """Frame for confirmation for node information request."""
37
+
38
+ PAYLOAD_LEN = 2
39
+
40
+ def __init__(
41
+ self,
42
+ status: AllNodesInformationStatus = AllNodesInformationStatus.OK,
43
+ number_of_nodes: int = 0,
44
+ ):
45
+ """Init Frame."""
46
+ super().__init__(Command.GW_GET_ALL_NODES_INFORMATION_CFM)
47
+ self.status = status
48
+ self.number_of_nodes = number_of_nodes
49
+
50
+ def get_payload(self) -> bytes:
51
+ """Return Payload."""
52
+ return bytes([self.status.value, self.number_of_nodes])
53
+
54
+ def from_payload(self, payload: bytes) -> None:
55
+ """Init frame from binary data."""
56
+ self.status = AllNodesInformationStatus(payload[0])
57
+ self.number_of_nodes = payload[1]
58
+
59
+ def __str__(self) -> str:
60
+ """Return human readable string."""
61
+ return '<{} status="{}" number_of_nodes="{}"/>'.format(
62
+ type(self).__name__, self.status, self.number_of_nodes
63
+ )
64
+
65
+
66
+ class FrameGetAllNodesInformationNotification(FrameBase):
67
+ """Frame for notification of all nodes information request."""
68
+
69
+ PAYLOAD_LEN = 124
70
+
71
+ def __init__(self) -> None:
72
+ """Init Frame."""
73
+ super().__init__(Command.GW_GET_ALL_NODES_INFORMATION_NTF)
74
+ self.node_id = 0
75
+ self.order = 0
76
+ self.placement = 0
77
+ self.name = ""
78
+ self.velocity = Velocity.DEFAULT
79
+ self.node_type = NodeTypeWithSubtype.NO_TYPE
80
+ self.product_group = 0
81
+ self.product_type = 0
82
+ self.node_variation = NodeVariation.NOT_SET
83
+ self.power_mode = 0
84
+ self.build_number = 0
85
+ self._serial_number = bytes(8)
86
+ self.state = OperatingState.UNKNOWN
87
+ self.current_position = Parameter()
88
+ self.target = Parameter()
89
+ self.current_position_fp1 = Parameter()
90
+ self.current_position_fp2 = Parameter()
91
+ self.current_position_fp3 = Parameter()
92
+ self.current_position_fp4 = Parameter()
93
+ self.remaining_time = 0
94
+ self.timestamp = 0
95
+ self.alias_array = AliasArray()
96
+
97
+ @property
98
+ def serial_number(self) -> Optional[str]:
99
+ """Property for serial number in a human readable way."""
100
+ if self._serial_number == bytes(8):
101
+ return None
102
+ return ":".join("{:02x}".format(c) for c in self._serial_number)
103
+
104
+ @serial_number.setter
105
+ def serial_number(self, serial_number: str) -> None:
106
+ """Set serial number."""
107
+ if serial_number is None:
108
+ self._serial_number = bytes(8)
109
+ return
110
+ self._serial_number = b""
111
+ for elem in serial_number.split(":"):
112
+ self._serial_number += bytes.fromhex(elem)
113
+ if len(self._serial_number) != 8:
114
+ raise PyVLXException("could_not_parse_serial_number")
115
+
116
+ def get_payload(self) -> bytes:
117
+ """Return Payload."""
118
+ payload = bytes()
119
+ payload += bytes([self.node_id])
120
+ payload += bytes([self.order >> 8 & 255, self.order & 255])
121
+ payload += bytes([self.placement])
122
+ payload += bytes(string_to_bytes(self.name, 64))
123
+ payload += bytes([self.velocity.value])
124
+ payload += bytes([self.node_type.value >> 8 & 255, self.node_type.value & 255])
125
+ payload += bytes([self.product_group])
126
+ payload += bytes([self.product_type])
127
+ payload += bytes([self.node_variation.value])
128
+ payload += bytes([self.power_mode])
129
+ payload += bytes([self.build_number])
130
+ payload += bytes(self._serial_number)
131
+ payload += bytes([self.state.value])
132
+ payload += bytes(self.current_position.raw)
133
+ payload += bytes(self.target.raw)
134
+ payload += bytes(self.current_position_fp1.raw)
135
+ payload += bytes(self.current_position_fp2.raw)
136
+ payload += bytes(self.current_position_fp3.raw)
137
+ payload += bytes(self.current_position_fp4.raw)
138
+ payload += bytes([self.remaining_time >> 8 & 255, self.remaining_time & 255])
139
+ payload += struct.pack(">I", self.timestamp)
140
+ payload += bytes(self.alias_array)
141
+
142
+ return payload
143
+
144
+ def from_payload(self, payload: bytes) -> None:
145
+ """Init frame from binary data."""
146
+ self.node_id = payload[0]
147
+ self.order = payload[1] * 256 + payload[2]
148
+ self.placement = payload[3]
149
+ self.name = bytes_to_string(payload[4:68])
150
+ self.velocity = Velocity(payload[68])
151
+ self.node_type = NodeTypeWithSubtype(payload[69] * 256 + payload[70])
152
+ self.product_group = payload[71]
153
+ self.product_type = payload[72]
154
+ self.node_variation = NodeVariation(payload[73])
155
+ self.power_mode = payload[74]
156
+ self.build_number = payload[75]
157
+ self._serial_number = payload[76:84]
158
+ self.state = OperatingState(payload[84])
159
+ self.current_position = Parameter(payload[85:87])
160
+ self.target = Parameter(payload[87:89])
161
+ self.current_position_fp1 = Parameter(payload[89:91])
162
+ self.current_position_fp2 = Parameter(payload[91:93])
163
+ self.current_position_fp3 = Parameter(payload[93:95])
164
+ self.current_position_fp4 = Parameter(payload[95:97])
165
+ self.remaining_time = payload[97] * 256 + payload[98]
166
+ self.timestamp = struct.unpack(">I", payload[99:103])[0]
167
+ self.alias_array = AliasArray(payload[103:125])
168
+
169
+ @property
170
+ def timestamp_formatted(self) -> str:
171
+ """Return time as human readable string."""
172
+ return datetime.fromtimestamp(self.timestamp).strftime("%Y-%m-%d %H:%M:%S")
173
+
174
+ def __str__(self) -> str:
175
+ """Return human readable string."""
176
+ return (
177
+ '<{} node_id="{}" order="{}" '
178
+ 'placement="{}" name="{}" velocity="{}" node_type="{}" product_group="{}" '
179
+ 'product_type="{}" node_variation="{}" power_mode="{}" build_number="{}" '
180
+ 'serial_number="{}" state="{}" current_position="{}" '
181
+ 'target="{}" current_position_fp1="{}" current_position_fp2="{}" '
182
+ 'current_position_fp3="{}" current_position_fp4="{}" '
183
+ 'remaining_time="{}" time="{}" alias_array="{}"/>'.format(
184
+ type(self).__name__,
185
+ self.node_id,
186
+ self.order,
187
+ self.placement,
188
+ self.name,
189
+ self.velocity,
190
+ self.node_type,
191
+ self.product_group,
192
+ self.product_type,
193
+ self.node_variation,
194
+ self.power_mode,
195
+ self.build_number,
196
+ self.serial_number,
197
+ self.state.name,
198
+ self.current_position,
199
+ self.target,
200
+ self.current_position_fp1,
201
+ self.current_position_fp2,
202
+ self.current_position_fp3,
203
+ self.current_position_fp4,
204
+ self.remaining_time,
205
+ self.timestamp_formatted,
206
+ self.alias_array,
207
+ )
208
+ )
209
+
210
+
211
+ class FrameGetAllNodesInformationFinishedNotification(FrameBase):
212
+ """Frame for notification of all nodes information finished notification."""
213
+
214
+ PAYLOAD_LEN = 0
215
+
216
+ def __init__(self) -> None:
217
+ """Init Frame."""
218
+ super().__init__(Command.GW_GET_ALL_NODES_INFORMATION_FINISHED_NTF)
@@ -0,0 +1,127 @@
1
+
2
+ """Module for get local time classes."""
3
+ from typing import List, Optional
4
+
5
+ from pyvlx.const import Command, LimitationType, Originator, Priority
6
+
7
+ from .frame import FrameBase
8
+
9
+
10
+ class FrameGetLimitationStatus(FrameBase):
11
+ """Frame for requesting limitation status."""
12
+
13
+ PAYLOAD_LEN = 25
14
+
15
+ def __init__(self,
16
+ node_ids: Optional[List[int]] = None,
17
+ session_id: Optional[int] = None,
18
+ limitation_type: LimitationType = LimitationType.MIN_LIMITATION):
19
+ """Init Frame."""
20
+ super().__init__(Command.GW_GET_LIMITATION_STATUS_REQ)
21
+ self.session_id = session_id
22
+ self.originator = Originator.USER
23
+ self.priority = Priority.USER_LEVEL_2
24
+ self.node_ids = node_ids if node_ids is not None else []
25
+
26
+ self.parameter_id = 0 # Main Parameter
27
+ self.limitations_type = limitation_type
28
+
29
+ def get_payload(self) -> bytes:
30
+ """Return Payload."""
31
+ assert self.session_id is not None
32
+ ret = bytes([self.session_id >> 8 & 255, self.session_id & 255])
33
+ ret += bytes([len(self.node_ids)]) # index array count
34
+ ret += bytes(self.node_ids) + bytes(20 - len(self.node_ids))
35
+ ret += bytes([self.parameter_id])
36
+ ret += bytes([self.limitations_type.value])
37
+ return ret
38
+
39
+ def __str__(self) -> str:
40
+ """Return human readable string."""
41
+ return f'<{type(self).__name__} node_ids="{self.node_ids}" ' \
42
+ f'session_id="{self.session_id}" originator="{self.originator}" />'
43
+
44
+
45
+ class FrameGetLimitationStatusConfirmation(FrameBase):
46
+ """Frame for response for get limitation requests."""
47
+
48
+ PAYLOAD_LEN = 3
49
+
50
+ def __init__(self, session_id: Optional[int] = None, data: Optional[int] = None):
51
+ """Init Frame."""
52
+ super().__init__(Command.GW_GET_LIMITATION_STATUS_CFM)
53
+ self.session_id = session_id
54
+ self.data = data
55
+
56
+ def get_payload(self) -> bytes:
57
+ """Return Payload."""
58
+ assert self.session_id is not None
59
+ ret = bytes([self.session_id >> 8 & 255, self.session_id & 255])
60
+ assert self.data is not None
61
+ ret += bytes([self.data])
62
+ return ret
63
+
64
+ def from_payload(self, payload: bytes) -> None:
65
+ """Init frame from binary data."""
66
+ self.session_id = payload[0] * 256 + payload[1]
67
+ self.data = payload[2]
68
+
69
+ def __str__(self) -> str:
70
+ """Return human readable string."""
71
+ return '<{} session_id="{}" status="{}"/>'.format(
72
+ type(self).__name__, self.session_id, self.data
73
+ )
74
+
75
+
76
+ class FrameGetLimitationStatusNotification(FrameBase):
77
+ """Frame for notification of note information request."""
78
+
79
+ PAYLOAD_LEN = 10
80
+
81
+ def __init__(self) -> None:
82
+ """Init Frame."""
83
+ super().__init__(Command.GW_LIMITATION_STATUS_NTF)
84
+ self.session_id: Optional[int] = None
85
+ self.node_id = 0
86
+ self.parameter_id = 0
87
+ self.min_value: Optional[bytes] = None
88
+ self.max_value: Optional[bytes] = None
89
+ self.limit_originator: Optional[Originator] = None
90
+ self.limit_time: Optional[int] = None
91
+
92
+ def get_payload(self) -> bytes:
93
+ """Return Payload."""
94
+ assert self.session_id is not None
95
+ assert self.min_value is not None
96
+ assert self.max_value is not None
97
+ assert self.limit_originator is not None
98
+ assert self.limit_time is not None
99
+ payload = bytes([self.session_id >> 8 & 255, self.session_id & 255])
100
+ payload += bytes([self.node_id])
101
+ payload += bytes([self.parameter_id])
102
+ payload += self.min_value
103
+ payload += self.max_value
104
+ payload += bytes([self.limit_originator.value])
105
+ payload += bytes([self.limit_time])
106
+ return payload
107
+
108
+ def from_payload(self, payload: bytes) -> None:
109
+ """Init frame from binary data."""
110
+ self.session_id = payload[0] * 256 + payload[1]
111
+ self.node_id = payload[2]
112
+ self.parameter_id = payload[3]
113
+ self.min_value = payload[4:5]
114
+ self.max_value = payload[6:7]
115
+ self.limit_originator = Originator(payload[8])
116
+ self.limit_time = payload[9]
117
+
118
+ def __str__(self) -> str:
119
+ """Return human readable string."""
120
+ return (
121
+ '<{} node_id="{}" session_id="{}" min_value="{!r}" '
122
+ 'max_value="{!r}" originator="{}" limit_time="{}"/>'.format(
123
+ type(self).__name__, self.node_id, self.session_id,
124
+ self.min_value, self.max_value, self.limit_originator,
125
+ self.limit_time
126
+ )
127
+ )
@@ -0,0 +1,38 @@
1
+ """Module for get local time classes."""
2
+ from pyvlx.const import Command
3
+ from pyvlx.dataobjects import DtoLocalTime
4
+
5
+ from .frame import FrameBase
6
+
7
+
8
+ class FrameGetLocalTimeRequest(FrameBase):
9
+ """Frame for requesting local time."""
10
+
11
+ PAYLOAD_LEN = 0
12
+
13
+ def __init__(self) -> None:
14
+ """Init Frame."""
15
+ super().__init__(Command.GW_GET_LOCAL_TIME_REQ)
16
+
17
+
18
+ class FrameGetLocalTimeConfirmation(FrameBase):
19
+ """Frame for response for get local time requests."""
20
+
21
+ PAYLOAD_LEN = 15
22
+
23
+ def __init__(self) -> None:
24
+ """Init Frame."""
25
+ super().__init__(Command.GW_GET_LOCAL_TIME_CFM)
26
+ self.time = DtoLocalTime()
27
+
28
+ def get_payload(self) -> bytes:
29
+ """Return Payload."""
30
+ return self.time.to_payload()
31
+
32
+ def from_payload(self, payload: bytes) -> None:
33
+ """Init frame from binary data."""
34
+ self.time.from_payload(payload)
35
+
36
+ def __str__(self) -> str:
37
+ """Return human readable string."""
38
+ return '<{0}>{1}</{0}>'.format(type(self).__name__, self.time)
@@ -0,0 +1,64 @@
1
+ """Frames for receiving network setup from gateway."""
2
+ from pyvlx.const import Command, DHCPParameter
3
+
4
+ from .frame import FrameBase
5
+
6
+
7
+ class FrameGetNetworkSetupRequest(FrameBase):
8
+ """Frame for requesting network setup."""
9
+
10
+ PAYLOAD_LEN = 0
11
+
12
+ def __init__(self) -> None:
13
+ """Init Frame."""
14
+ super().__init__(Command.GW_GET_NETWORK_SETUP_REQ)
15
+
16
+
17
+ class FrameGetNetworkSetupConfirmation(FrameBase):
18
+ """Frame for confirmation for get network setup requests."""
19
+
20
+ PAYLOAD_LEN = 13
21
+
22
+ def __init__(self, ipaddress: bytes = bytes(4), netmask: bytes = bytes(4), gateway: bytes = bytes(4),
23
+ dhcp: DHCPParameter = DHCPParameter.DISABLE):
24
+ """Init Frame."""
25
+ super().__init__(Command.GW_GET_NETWORK_SETUP_CFM)
26
+ self._ipaddress = ipaddress
27
+ self._netmask = netmask
28
+ self._gateway = gateway
29
+ self.dhcp = dhcp
30
+
31
+ @property
32
+ def ipaddress(self) -> str:
33
+ """Return ipaddress as human readable string."""
34
+ return ".".join(str(c) for c in self._ipaddress)
35
+
36
+ @property
37
+ def netmask(self) -> str:
38
+ """Return ipaddress as human readable string."""
39
+ return ".".join(str(c) for c in self._netmask)
40
+
41
+ @property
42
+ def gateway(self) -> str:
43
+ """Return ipaddress as human readable string."""
44
+ return ".".join(str(c) for c in self._gateway)
45
+
46
+ def get_payload(self) -> bytes:
47
+ """Return Payload."""
48
+ payload = self._ipaddress
49
+ payload += self._netmask
50
+ payload += self._gateway
51
+ payload += bytes(self.dhcp.value)
52
+ return payload
53
+
54
+ def from_payload(self, payload: bytes) -> None:
55
+ """Init frame from binary data."""
56
+ self._ipaddress = payload[0:4]
57
+ self._netmask = payload[4:8]
58
+ self._gateway = payload[8:12]
59
+ self.dhcp = DHCPParameter(payload[12])
60
+
61
+ def __str__(self) -> str:
62
+ """Return human readable string."""
63
+ return '<{} ipaddress="{}" netmask="{}" gateway="{}" dhcp="{}"/>'.format(
64
+ type(self).__name__, self.ipaddress, self.netmask, self.gateway, self.dhcp)
@@ -0,0 +1,223 @@
1
+ """Module for get node information from gateway."""
2
+ import struct
3
+ from datetime import datetime
4
+ from enum import Enum
5
+ from typing import Optional
6
+
7
+ from pyvlx.const import (
8
+ Command, NodeTypeWithSubtype, NodeVariation, OperatingState, Velocity)
9
+ from pyvlx.exception import PyVLXException
10
+ from pyvlx.parameter import Parameter
11
+ from pyvlx.string_helper import bytes_to_string, string_to_bytes
12
+
13
+ from .alias_array import AliasArray
14
+ from .frame import FrameBase
15
+
16
+
17
+ class FrameGetNodeInformationRequest(FrameBase):
18
+ """Frame for get node information request."""
19
+
20
+ PAYLOAD_LEN = 1
21
+
22
+ def __init__(self, node_id: Optional[int] = None):
23
+ """Init Frame."""
24
+ super().__init__(Command.GW_GET_NODE_INFORMATION_REQ)
25
+ self.node_id = node_id
26
+
27
+ def get_payload(self) -> bytes:
28
+ """Return Payload."""
29
+ assert self.node_id is not None
30
+ return bytes([self.node_id])
31
+
32
+ def from_payload(self, payload: bytes) -> None:
33
+ """Init frame from binary data."""
34
+ self.node_id = payload[0]
35
+
36
+ def __str__(self) -> str:
37
+ """Return human readable string."""
38
+ return '<{} node_id="{}"/>'.format(type(self).__name__, self.node_id)
39
+
40
+
41
+ class NodeInformationStatus(Enum):
42
+ # pylint: disable=invalid-name
43
+ """Enum for node information status."""
44
+
45
+ OK = 0
46
+ Error_Request_Rejected = 1
47
+ Error_Invalid_Node_Index = 2
48
+
49
+
50
+ class FrameGetNodeInformationConfirmation(FrameBase):
51
+ """Frame for confirmation for node information request."""
52
+
53
+ PAYLOAD_LEN = 2
54
+
55
+ def __init__(self, status: NodeInformationStatus = NodeInformationStatus.OK, node_id: Optional[int] = None):
56
+ """Init Frame."""
57
+ super().__init__(Command.GW_GET_NODE_INFORMATION_CFM)
58
+ self.status = status
59
+ self.node_id = node_id
60
+
61
+ def get_payload(self) -> bytes:
62
+ """Return Payload."""
63
+ assert self.node_id is not None
64
+ return bytes([self.status.value, self.node_id])
65
+
66
+ def from_payload(self, payload: bytes) -> None:
67
+ """Init frame from binary data."""
68
+ self.status = NodeInformationStatus(payload[0])
69
+ self.node_id = payload[1]
70
+
71
+ def __str__(self) -> str:
72
+ """Return human readable string."""
73
+ return '<{} node_id="{}" status="{}"/>'.format(
74
+ type(self).__name__, self.node_id, self.status
75
+ )
76
+
77
+
78
+ class FrameGetNodeInformationNotification(FrameBase):
79
+ """Frame for notification of node information request."""
80
+
81
+ PAYLOAD_LEN = 124
82
+
83
+ def __init__(self) -> None:
84
+ """Init Frame."""
85
+ super().__init__(Command.GW_GET_NODE_INFORMATION_NTF)
86
+ self.node_id = 0
87
+ self.order = 0
88
+ self.placement = 0
89
+ self.name = ""
90
+ self.velocity = Velocity.DEFAULT
91
+ self.node_type = NodeTypeWithSubtype.NO_TYPE
92
+ self.product_group = 0
93
+ self.product_type = 0
94
+ self.node_variation = NodeVariation.NOT_SET
95
+ self.power_mode = 0
96
+ self.build_number = 0
97
+ self._serial_number = bytes(8)
98
+ self.state = OperatingState.UNKNOWN
99
+ self.current_position = Parameter()
100
+ self.target = Parameter()
101
+ self.current_position_fp1 = Parameter()
102
+ self.current_position_fp2 = Parameter()
103
+ self.current_position_fp3 = Parameter()
104
+ self.current_position_fp4 = Parameter()
105
+ self.remaining_time = 0
106
+ self.timestamp = 0
107
+ self.alias_array = AliasArray()
108
+
109
+ @property
110
+ def serial_number(self) -> Optional[str]:
111
+ """Property for serial number in a human readable way."""
112
+ if self._serial_number == bytes(8):
113
+ return None
114
+ return ":".join("{:02x}".format(c) for c in self._serial_number)
115
+
116
+ @serial_number.setter
117
+ def serial_number(self, serial_number: Optional[str]) -> None:
118
+ """Set serial number."""
119
+ if serial_number is None:
120
+ self._serial_number = bytes(8)
121
+ return
122
+ self._serial_number = b""
123
+ for elem in serial_number.split(":"):
124
+ self._serial_number += bytes.fromhex(elem)
125
+ if len(self._serial_number) != 8:
126
+ raise PyVLXException("could_not_parse_serial_number")
127
+
128
+ def get_payload(self) -> bytes:
129
+ """Return Payload."""
130
+ payload = bytes()
131
+ payload += bytes([self.node_id])
132
+ payload += bytes([self.order >> 8 & 255, self.order & 255])
133
+ payload += bytes([self.placement])
134
+ payload += bytes(string_to_bytes(self.name, 64))
135
+ payload += bytes([self.velocity.value])
136
+ payload += bytes([self.node_type.value >> 8 & 255, self.node_type.value & 255])
137
+ payload += bytes([self.product_group])
138
+ payload += bytes([self.product_type])
139
+ payload += bytes([self.node_variation.value])
140
+ payload += bytes([self.power_mode])
141
+ payload += bytes(
142
+ [self.build_number]
143
+ ) # <-- hey @VELUX: your documentation is wrong here
144
+ payload += bytes(self._serial_number)
145
+ payload += bytes([self.state.value])
146
+ payload += bytes(self.current_position.raw)
147
+ payload += bytes(self.target.raw)
148
+ payload += bytes(self.current_position_fp1.raw)
149
+ payload += bytes(self.current_position_fp2.raw)
150
+ payload += bytes(self.current_position_fp3.raw)
151
+ payload += bytes(self.current_position_fp4.raw)
152
+ payload += bytes([self.remaining_time >> 8 & 255, self.remaining_time & 255])
153
+ payload += struct.pack(">I", self.timestamp)
154
+ payload += bytes(self.alias_array)
155
+ return payload
156
+
157
+ def from_payload(self, payload: bytes) -> None:
158
+ """Init frame from binary data."""
159
+ self.node_id = payload[0]
160
+ self.order = payload[1] * 256 + payload[2]
161
+ self.placement = payload[3]
162
+ self.name = bytes_to_string(payload[4:68])
163
+ self.velocity = Velocity(payload[68])
164
+ self.node_type = NodeTypeWithSubtype(payload[69] * 256 + payload[70])
165
+ self.product_group = payload[71]
166
+ self.product_type = payload[72]
167
+ self.node_variation = NodeVariation(payload[73])
168
+ self.power_mode = payload[74]
169
+ self.build_number = payload[
170
+ 75
171
+ ] # <-- hey @VELUX: your documentation is wrong here
172
+ self._serial_number = payload[76:84]
173
+ self.state = OperatingState(payload[84])
174
+ self.current_position = Parameter(payload[85:87])
175
+ self.target = Parameter(payload[87:89])
176
+ self.current_position_fp1 = Parameter(payload[89:91])
177
+ self.current_position_fp2 = Parameter(payload[91:93])
178
+ self.current_position_fp3 = Parameter(payload[93:95])
179
+ self.current_position_fp4 = Parameter(payload[95:97])
180
+ self.remaining_time = payload[97] * 256 + payload[98]
181
+ self.timestamp = struct.unpack(">I", payload[99:103])[0]
182
+ self.alias_array = AliasArray(payload[103:125])
183
+
184
+ @property
185
+ def timestamp_formatted(self) -> str:
186
+ """Return time as human readable string."""
187
+ return datetime.fromtimestamp(self.timestamp).strftime("%Y-%m-%d %H:%M:%S")
188
+
189
+ def __str__(self) -> str:
190
+ """Return human readable string."""
191
+ return (
192
+ '<{} node_id="{}" order="{}" '
193
+ 'placement="{}" name="{}" velocity="{}" node_type="{}" product_group="{}" '
194
+ 'product_type="{}" node_variation="{}" power_mode="{}" build_number="{}" '
195
+ 'serial_number="{}" state="{}" current_position="{}" '
196
+ 'target="{}" current_position_fp1="{}" current_position_fp2="{}" '
197
+ 'current_position_fp3="{}" current_position_fp4="{}" '
198
+ 'remaining_time="{}" time="{}" alias_array="{}"/>'.format(
199
+ type(self).__name__,
200
+ self.node_id,
201
+ self.order,
202
+ self.placement,
203
+ self.name,
204
+ self.velocity,
205
+ self.node_type,
206
+ self.product_group,
207
+ self.product_type,
208
+ self.node_variation,
209
+ self.power_mode,
210
+ self.build_number,
211
+ self.serial_number,
212
+ self.state.name,
213
+ self.current_position,
214
+ self.target,
215
+ self.current_position_fp1,
216
+ self.current_position_fp2,
217
+ self.current_position_fp3,
218
+ self.current_position_fp4,
219
+ self.remaining_time,
220
+ self.timestamp_formatted,
221
+ self.alias_array,
222
+ )
223
+ )