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.
- pyvlx/__init__.py +21 -0
- pyvlx/api/__init__.py +23 -0
- pyvlx/api/activate_scene.py +63 -0
- pyvlx/api/api_event.py +73 -0
- pyvlx/api/command_send.py +85 -0
- pyvlx/api/factory_default.py +34 -0
- pyvlx/api/frame_creation.py +202 -0
- pyvlx/api/frames/__init__.py +76 -0
- pyvlx/api/frames/alias_array.py +45 -0
- pyvlx/api/frames/frame.py +56 -0
- pyvlx/api/frames/frame_activate_scene.py +92 -0
- pyvlx/api/frames/frame_activation_log_updated.py +14 -0
- pyvlx/api/frames/frame_command_send.py +280 -0
- pyvlx/api/frames/frame_discover_nodes.py +64 -0
- pyvlx/api/frames/frame_error_notification.py +42 -0
- pyvlx/api/frames/frame_facory_default.py +32 -0
- pyvlx/api/frames/frame_get_all_nodes_information.py +218 -0
- pyvlx/api/frames/frame_get_limitation.py +127 -0
- pyvlx/api/frames/frame_get_local_time.py +38 -0
- pyvlx/api/frames/frame_get_network_setup.py +64 -0
- pyvlx/api/frames/frame_get_node_information.py +223 -0
- pyvlx/api/frames/frame_get_protocol_version.py +53 -0
- pyvlx/api/frames/frame_get_scene_list.py +82 -0
- pyvlx/api/frames/frame_get_state.py +47 -0
- pyvlx/api/frames/frame_get_version.py +72 -0
- pyvlx/api/frames/frame_helper.py +40 -0
- pyvlx/api/frames/frame_house_status_monitor_disable_cfm.py +14 -0
- pyvlx/api/frames/frame_house_status_monitor_disable_req.py +14 -0
- pyvlx/api/frames/frame_house_status_monitor_enable_cfm.py +14 -0
- pyvlx/api/frames/frame_house_status_monitor_enable_req.py +14 -0
- pyvlx/api/frames/frame_leave_learn_state.py +41 -0
- pyvlx/api/frames/frame_node_information_changed.py +57 -0
- pyvlx/api/frames/frame_node_state_position_changed_notification.py +84 -0
- pyvlx/api/frames/frame_password_change.py +114 -0
- pyvlx/api/frames/frame_password_enter.py +70 -0
- pyvlx/api/frames/frame_reboot.py +32 -0
- pyvlx/api/frames/frame_set_node_name.py +73 -0
- pyvlx/api/frames/frame_set_utc.py +45 -0
- pyvlx/api/frames/frame_status_request.py +212 -0
- pyvlx/api/get_all_nodes_information.py +46 -0
- pyvlx/api/get_limitation.py +64 -0
- pyvlx/api/get_local_time.py +34 -0
- pyvlx/api/get_network_setup.py +34 -0
- pyvlx/api/get_node_information.py +42 -0
- pyvlx/api/get_protocol_version.py +40 -0
- pyvlx/api/get_scene_list.py +49 -0
- pyvlx/api/get_state.py +43 -0
- pyvlx/api/get_version.py +34 -0
- pyvlx/api/house_status_monitor.py +52 -0
- pyvlx/api/leave_learn_state.py +33 -0
- pyvlx/api/password_enter.py +39 -0
- pyvlx/api/reboot.py +33 -0
- pyvlx/api/session_id.py +20 -0
- pyvlx/api/set_node_name.py +32 -0
- pyvlx/api/set_utc.py +31 -0
- pyvlx/api/status_request.py +48 -0
- pyvlx/config.py +54 -0
- pyvlx/connection.py +182 -0
- pyvlx/const.py +685 -0
- pyvlx/dataobjects.py +161 -0
- pyvlx/discovery.py +100 -0
- pyvlx/exception.py +26 -0
- pyvlx/heartbeat.py +79 -0
- pyvlx/klf200gateway.py +167 -0
- pyvlx/lightening_device.py +102 -0
- pyvlx/log.py +4 -0
- pyvlx/node.py +74 -0
- pyvlx/node_helper.py +165 -0
- pyvlx/node_updater.py +162 -0
- pyvlx/nodes.py +99 -0
- pyvlx/on_off_switch.py +44 -0
- pyvlx/opening_device.py +644 -0
- pyvlx/parameter.py +357 -0
- pyvlx/py.typed +0 -0
- pyvlx/pyvlx.py +124 -0
- pyvlx/scene.py +53 -0
- pyvlx/scenes.py +60 -0
- pyvlx/slip.py +48 -0
- pyvlx/string_helper.py +20 -0
- pyvlx-0.2.27.dist-info/METADATA +122 -0
- pyvlx-0.2.27.dist-info/RECORD +84 -0
- pyvlx-0.2.27.dist-info/WHEEL +5 -0
- pyvlx-0.2.27.dist-info/licenses/LICENSE +165 -0
- 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
|
+
)
|