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,53 @@
|
|
|
1
|
+
"""Module for get version frame classes."""
|
|
2
|
+
from pyvlx.const import Command
|
|
3
|
+
|
|
4
|
+
from .frame import FrameBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FrameGetProtocolVersionRequest(FrameBase):
|
|
8
|
+
"""Frame for requesting protocol version."""
|
|
9
|
+
|
|
10
|
+
PAYLOAD_LEN = 0
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
"""Init Frame."""
|
|
14
|
+
super().__init__(Command.GW_GET_PROTOCOL_VERSION_REQ)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FrameGetProtocolVersionConfirmation(FrameBase):
|
|
18
|
+
"""Frame for response for get protocol version requests."""
|
|
19
|
+
|
|
20
|
+
PAYLOAD_LEN = 4
|
|
21
|
+
|
|
22
|
+
def __init__(self, major_version: int = 0, minor_version: int = 0):
|
|
23
|
+
"""Init Frame."""
|
|
24
|
+
super().__init__(Command.GW_GET_PROTOCOL_VERSION_CFM)
|
|
25
|
+
self.major_version = major_version
|
|
26
|
+
self.minor_version = minor_version
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def version(self) -> str:
|
|
30
|
+
"""Return formatted protocol version."""
|
|
31
|
+
return "{}.{}".format(self.major_version, self.minor_version)
|
|
32
|
+
|
|
33
|
+
def get_payload(self) -> bytes:
|
|
34
|
+
"""Return Payload."""
|
|
35
|
+
return bytes(
|
|
36
|
+
[
|
|
37
|
+
self.major_version >> 8 & 255,
|
|
38
|
+
self.major_version & 255,
|
|
39
|
+
self.minor_version >> 8 & 255,
|
|
40
|
+
self.minor_version & 255,
|
|
41
|
+
]
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def from_payload(self, payload: bytes) -> None:
|
|
45
|
+
"""Init frame from binary data."""
|
|
46
|
+
self.major_version = payload[0] * 256 + payload[1]
|
|
47
|
+
self.minor_version = payload[2] * 256 + payload[3]
|
|
48
|
+
|
|
49
|
+
def __str__(self) -> str:
|
|
50
|
+
"""Return human readable string."""
|
|
51
|
+
return '<{} version="{}"/>'.format(
|
|
52
|
+
type(self).__name__, self.version
|
|
53
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Module for get scene list frame classes."""
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
from pyvlx.const import Command
|
|
5
|
+
from pyvlx.exception import PyVLXException
|
|
6
|
+
from pyvlx.string_helper import bytes_to_string, string_to_bytes
|
|
7
|
+
|
|
8
|
+
from .frame import FrameBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class FrameGetSceneListRequest(FrameBase):
|
|
12
|
+
"""Frame for get scene list request."""
|
|
13
|
+
|
|
14
|
+
PAYLOAD_LEN = 0
|
|
15
|
+
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
"""Init Frame."""
|
|
18
|
+
super().__init__(Command.GW_GET_SCENE_LIST_REQ)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FrameGetSceneListConfirmation(FrameBase):
|
|
22
|
+
"""Frame for confirmation for scene list request."""
|
|
23
|
+
|
|
24
|
+
PAYLOAD_LEN = 1
|
|
25
|
+
|
|
26
|
+
def __init__(self, count_scenes: int = 0):
|
|
27
|
+
"""Init Frame."""
|
|
28
|
+
super().__init__(Command.GW_GET_SCENE_LIST_CFM)
|
|
29
|
+
self.count_scenes = count_scenes
|
|
30
|
+
|
|
31
|
+
def get_payload(self) -> bytes:
|
|
32
|
+
"""Return Payload."""
|
|
33
|
+
return bytes([self.count_scenes])
|
|
34
|
+
|
|
35
|
+
def from_payload(self, payload: bytes) -> None:
|
|
36
|
+
"""Init frame from binary data."""
|
|
37
|
+
self.count_scenes = payload[0]
|
|
38
|
+
|
|
39
|
+
def __str__(self) -> str:
|
|
40
|
+
"""Return human readable string."""
|
|
41
|
+
return '<{} count_scenes="{}"/>'.format(
|
|
42
|
+
type(self).__name__, self.count_scenes
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class FrameGetSceneListNotification(FrameBase):
|
|
47
|
+
"""Frame for scene list notification."""
|
|
48
|
+
|
|
49
|
+
def __init__(self) -> None:
|
|
50
|
+
"""Init Frame."""
|
|
51
|
+
super().__init__(Command.GW_GET_SCENE_LIST_NTF)
|
|
52
|
+
self.scenes: List[Tuple[int, str]] = []
|
|
53
|
+
self.remaining_scenes = 0
|
|
54
|
+
|
|
55
|
+
def get_payload(self) -> bytes:
|
|
56
|
+
"""Return Payload."""
|
|
57
|
+
ret = bytes([len(self.scenes)])
|
|
58
|
+
for number, name in self.scenes:
|
|
59
|
+
ret += bytes([number])
|
|
60
|
+
ret += string_to_bytes(name, 64)
|
|
61
|
+
ret += bytes([self.remaining_scenes])
|
|
62
|
+
return ret
|
|
63
|
+
|
|
64
|
+
def from_payload(self, payload: bytes) -> None:
|
|
65
|
+
"""Init frame from binary data."""
|
|
66
|
+
number_of_objects = payload[0]
|
|
67
|
+
self.remaining_scenes = payload[-1]
|
|
68
|
+
predicted_len = number_of_objects * 65 + 2
|
|
69
|
+
if len(payload) != predicted_len:
|
|
70
|
+
raise PyVLXException("scene_list_notification_wrong_length")
|
|
71
|
+
self.scenes = []
|
|
72
|
+
for i in range(number_of_objects):
|
|
73
|
+
scene = payload[(i * 65 + 1) : (i * 65 + 66)]
|
|
74
|
+
number = scene[0]
|
|
75
|
+
name = bytes_to_string(scene[1:])
|
|
76
|
+
self.scenes.append((number, name))
|
|
77
|
+
|
|
78
|
+
def __str__(self) -> str:
|
|
79
|
+
"""Return human readable string."""
|
|
80
|
+
return '<{} scenes="{}" remaining_scenes="{}">'.format(
|
|
81
|
+
type(self).__name__, self.scenes, self.remaining_scenes
|
|
82
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Frames for receiving state from gateway."""
|
|
2
|
+
from pyvlx.const import Command, GatewayState, GatewaySubState
|
|
3
|
+
|
|
4
|
+
from .frame import FrameBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FrameGetStateRequest(FrameBase):
|
|
8
|
+
"""Frame for requesting state."""
|
|
9
|
+
|
|
10
|
+
PAYLOAD_LEN = 0
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
"""Init Frame."""
|
|
14
|
+
super().__init__(Command.GW_GET_STATE_REQ)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FrameGetStateConfirmation(FrameBase):
|
|
18
|
+
"""Frame for confirmation for get state requests."""
|
|
19
|
+
|
|
20
|
+
PAYLOAD_LEN = 6
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
gateway_state: GatewayState = GatewayState.TEST_MODE,
|
|
25
|
+
gateway_sub_state: GatewaySubState = GatewaySubState.IDLE,
|
|
26
|
+
):
|
|
27
|
+
"""Init Frame."""
|
|
28
|
+
super().__init__(Command.GW_GET_STATE_CFM)
|
|
29
|
+
self.gateway_state = gateway_state
|
|
30
|
+
self.gateway_sub_state = gateway_sub_state
|
|
31
|
+
|
|
32
|
+
def get_payload(self) -> bytes:
|
|
33
|
+
"""Return Payload."""
|
|
34
|
+
payload = bytes([self.gateway_state.value, self.gateway_sub_state.value])
|
|
35
|
+
payload += bytes(4) # State date, reserved for future use
|
|
36
|
+
return payload
|
|
37
|
+
|
|
38
|
+
def from_payload(self, payload: bytes) -> None:
|
|
39
|
+
"""Init frame from binary data."""
|
|
40
|
+
self.gateway_state = GatewayState(payload[0])
|
|
41
|
+
self.gateway_sub_state = GatewaySubState(payload[1])
|
|
42
|
+
|
|
43
|
+
def __str__(self) -> str:
|
|
44
|
+
"""Return human readable string."""
|
|
45
|
+
return '<{} gateway_state="{}" gateway_sub_state="{}"/>'.format(
|
|
46
|
+
type(self).__name__, self.gateway_state, self.gateway_sub_state
|
|
47
|
+
)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Module for get version frame classes."""
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
from pyvlx.const import Command
|
|
5
|
+
|
|
6
|
+
from .frame import FrameBase
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FrameGetVersionRequest(FrameBase):
|
|
10
|
+
"""Frame for requesting version."""
|
|
11
|
+
|
|
12
|
+
PAYLOAD_LEN = 0
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
"""Init Frame."""
|
|
16
|
+
super().__init__(Command.GW_GET_VERSION_REQ)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FrameGetVersionConfirmation(FrameBase):
|
|
20
|
+
"""Frame for response for get version requests."""
|
|
21
|
+
|
|
22
|
+
PAYLOAD_LEN = 9
|
|
23
|
+
|
|
24
|
+
def __init__(self, software_version: Union[bytes, str] = bytes(6), hardware_version: int = 0):
|
|
25
|
+
"""Init Frame."""
|
|
26
|
+
super().__init__(Command.GW_GET_VERSION_CFM)
|
|
27
|
+
if isinstance(software_version, str):
|
|
28
|
+
software_version = bytes(int(c) for c in software_version.split("."))
|
|
29
|
+
self._software_version = software_version
|
|
30
|
+
self.hardware_version = hardware_version
|
|
31
|
+
self.product_group = 14
|
|
32
|
+
self.product_type = 3
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def version(self) -> str:
|
|
36
|
+
"""Return formatted version."""
|
|
37
|
+
return "{}: Software version: {}, hardware version: {}".format(
|
|
38
|
+
self.product, self.software_version, self.hardware_version
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def software_version(self) -> str:
|
|
43
|
+
"""Return software version as human readable string."""
|
|
44
|
+
return ".".join(str(c) for c in self._software_version)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def product(self) -> str:
|
|
48
|
+
"""Return product as human readable string."""
|
|
49
|
+
if self.product_group == 14 and self.product_type == 3:
|
|
50
|
+
return "KLF 200"
|
|
51
|
+
return "Unknown Product: {}:{}".format(self.product_group, self.product_type)
|
|
52
|
+
|
|
53
|
+
def get_payload(self) -> bytes:
|
|
54
|
+
"""Return Payload."""
|
|
55
|
+
ret = self._software_version
|
|
56
|
+
ret += bytes([self.hardware_version, self.product_group, self.product_type])
|
|
57
|
+
return ret
|
|
58
|
+
|
|
59
|
+
def from_payload(self, payload: bytes) -> None:
|
|
60
|
+
"""Init frame from binary data."""
|
|
61
|
+
self._software_version = payload[0:6]
|
|
62
|
+
self.hardware_version = payload[6]
|
|
63
|
+
self.product_group = payload[7]
|
|
64
|
+
self.product_type = payload[8]
|
|
65
|
+
|
|
66
|
+
def __str__(self) -> str:
|
|
67
|
+
"""Return human readable string."""
|
|
68
|
+
return (
|
|
69
|
+
'<{} software_version="{}" hardware_version="{}" product="{}"/>'.format(
|
|
70
|
+
type(self).__name__, self.software_version, self.hardware_version, self.product
|
|
71
|
+
)
|
|
72
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Helper module for SLIP Frames."""
|
|
2
|
+
from typing import Tuple
|
|
3
|
+
|
|
4
|
+
from pyvlx.const import Command
|
|
5
|
+
from pyvlx.exception import PyVLXException
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def calc_crc(raw: bytes) -> int:
|
|
9
|
+
"""Calculate cyclic redundancy check (CRC)."""
|
|
10
|
+
crc = 0
|
|
11
|
+
for sym in raw:
|
|
12
|
+
crc = crc ^ int(sym)
|
|
13
|
+
return crc
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def extract_from_frame(data: bytes) -> Tuple[Command, bytes]:
|
|
17
|
+
"""Extract payload and command from frame."""
|
|
18
|
+
if len(data) <= 4:
|
|
19
|
+
raise PyVLXException("could_not_extract_from_frame_too_short", data=data)
|
|
20
|
+
length = data[0] * 256 + data[1] - 1
|
|
21
|
+
if len(data) != length + 3:
|
|
22
|
+
raise PyVLXException(
|
|
23
|
+
"could_not_extract_from_frame_invalid_length",
|
|
24
|
+
data=data,
|
|
25
|
+
current_length=len(data),
|
|
26
|
+
expected_length=length + 3,
|
|
27
|
+
)
|
|
28
|
+
if calc_crc(data[:-1]) != data[-1]:
|
|
29
|
+
raise PyVLXException(
|
|
30
|
+
"could_not_extract_from_frame_invalid_crc",
|
|
31
|
+
data=data,
|
|
32
|
+
expected_crc=calc_crc(data[:-1]),
|
|
33
|
+
current_crc=data[-1],
|
|
34
|
+
)
|
|
35
|
+
payload = data[4:-1]
|
|
36
|
+
try:
|
|
37
|
+
command = Command(data[2] * 256 + data[3])
|
|
38
|
+
except ValueError as type_error:
|
|
39
|
+
raise PyVLXException("could_not_extract_from_frame_command", data=data) from type_error
|
|
40
|
+
return command, payload
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Module for confirmation for disabling the house status monitor."""
|
|
2
|
+
from pyvlx.const import Command
|
|
3
|
+
|
|
4
|
+
from .frame import FrameBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FrameHouseStatusMonitorDisableConfirmation(FrameBase):
|
|
8
|
+
"""Frame for requesting enabling the house status monitor."""
|
|
9
|
+
|
|
10
|
+
PAYLOAD_LEN = 0
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
"""Init Frame."""
|
|
14
|
+
super().__init__(Command.GW_HOUSE_STATUS_MONITOR_DISABLE_CFM)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Module for requesting disabling the house status monitor."""
|
|
2
|
+
from pyvlx.const import Command
|
|
3
|
+
|
|
4
|
+
from .frame import FrameBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FrameHouseStatusMonitorDisableRequest(FrameBase):
|
|
8
|
+
"""Frame for requesting disabling the house status monitor."""
|
|
9
|
+
|
|
10
|
+
PAYLOAD_LEN = 0
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
"""Init Frame."""
|
|
14
|
+
super().__init__(Command.GW_HOUSE_STATUS_MONITOR_DISABLE_REQ)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Module for confirmation for enabling the house status monitor."""
|
|
2
|
+
from pyvlx.const import Command
|
|
3
|
+
|
|
4
|
+
from .frame import FrameBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FrameHouseStatusMonitorEnableConfirmation(FrameBase):
|
|
8
|
+
"""Frame for confirmation for enabling the house status monitor."""
|
|
9
|
+
|
|
10
|
+
PAYLOAD_LEN = 0
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
"""Init Frame."""
|
|
14
|
+
super().__init__(Command.GW_HOUSE_STATUS_MONITOR_ENABLE_CFM)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Module for requesting enabling the house status monitor."""
|
|
2
|
+
from pyvlx.const import Command
|
|
3
|
+
|
|
4
|
+
from .frame import FrameBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FrameHouseStatusMonitorEnableRequest(FrameBase):
|
|
8
|
+
"""Frame for requesting enabling the house status monitor."""
|
|
9
|
+
|
|
10
|
+
PAYLOAD_LEN = 0
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
"""Init Frame."""
|
|
14
|
+
super().__init__(Command.GW_HOUSE_STATUS_MONITOR_ENABLE_REQ)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Module for leave learn state frame classes."""
|
|
2
|
+
from pyvlx.const import Command, LeaveLearnStateConfirmationStatus
|
|
3
|
+
|
|
4
|
+
from .frame import FrameBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FrameLeaveLearnStateRequest(FrameBase):
|
|
8
|
+
"""Frame for leaving learn state request."""
|
|
9
|
+
|
|
10
|
+
PAYLOAD_LEN = 0
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
"""Init Frame."""
|
|
14
|
+
super().__init__(Command.GW_LEAVE_LEARN_STATE_REQ)
|
|
15
|
+
|
|
16
|
+
def __str__(self) -> str:
|
|
17
|
+
"""Return human readable string."""
|
|
18
|
+
return '<{}/>'.format(type(self).__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FrameLeaveLearnStateConfirmation(FrameBase):
|
|
22
|
+
"""Frame for confirmation for leaving learn State."""
|
|
23
|
+
|
|
24
|
+
PAYLOAD_LEN = 1
|
|
25
|
+
|
|
26
|
+
def __init__(self, status: int = 0):
|
|
27
|
+
"""Init Frame."""
|
|
28
|
+
super().__init__(Command.GW_LEAVE_LEARN_STATE_CFM)
|
|
29
|
+
self.status = LeaveLearnStateConfirmationStatus(status)
|
|
30
|
+
|
|
31
|
+
def get_payload(self) -> bytes:
|
|
32
|
+
"""Return Payload."""
|
|
33
|
+
return bytes([self.status.value])
|
|
34
|
+
|
|
35
|
+
def from_payload(self, payload: bytes) -> None:
|
|
36
|
+
"""Init frame from binary data."""
|
|
37
|
+
self.status = LeaveLearnStateConfirmationStatus(payload[0])
|
|
38
|
+
|
|
39
|
+
def __str__(self) -> str:
|
|
40
|
+
"""Return human readable string."""
|
|
41
|
+
return '<{} status="{}"/>'.format(type(self).__name__, self.status)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Module for requesting change of node name."""
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from pyvlx.const import Command, NodeVariation
|
|
5
|
+
from pyvlx.string_helper import bytes_to_string, string_to_bytes
|
|
6
|
+
|
|
7
|
+
from .frame import FrameBase
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FrameNodeInformationChangedNotification(FrameBase):
|
|
11
|
+
"""Frame for notification for set node name."""
|
|
12
|
+
|
|
13
|
+
PAYLOAD_LEN = 69
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
node_id: int = 0,
|
|
18
|
+
name: Optional[str] = None,
|
|
19
|
+
order: int = 0,
|
|
20
|
+
placement: int = 0,
|
|
21
|
+
node_variation: NodeVariation = NodeVariation.NOT_SET,
|
|
22
|
+
):
|
|
23
|
+
"""Init Frame."""
|
|
24
|
+
super().__init__(Command.GW_NODE_INFORMATION_CHANGED_NTF)
|
|
25
|
+
self.node_id = node_id
|
|
26
|
+
self.name = name
|
|
27
|
+
self.order = order
|
|
28
|
+
self.placement = placement
|
|
29
|
+
self.node_variation = node_variation
|
|
30
|
+
|
|
31
|
+
def get_payload(self) -> bytes:
|
|
32
|
+
"""Return Payload."""
|
|
33
|
+
payload = bytes([self.node_id])
|
|
34
|
+
assert self.name is not None
|
|
35
|
+
payload += string_to_bytes(self.name, 64)
|
|
36
|
+
payload += bytes([self.order >> 8 & 255, self.order & 255])
|
|
37
|
+
payload += bytes([self.placement])
|
|
38
|
+
payload += bytes([self.node_variation.value])
|
|
39
|
+
return payload
|
|
40
|
+
|
|
41
|
+
def from_payload(self, payload: bytes) -> None:
|
|
42
|
+
"""Init frame from binary data."""
|
|
43
|
+
self.node_id = payload[0]
|
|
44
|
+
self.name = bytes_to_string(payload[1:65])
|
|
45
|
+
self.order = payload[65] * 256 + payload[66]
|
|
46
|
+
self.placement = payload[67]
|
|
47
|
+
self.node_variation = NodeVariation(payload[68])
|
|
48
|
+
|
|
49
|
+
def __str__(self) -> str:
|
|
50
|
+
"""Return human readable string."""
|
|
51
|
+
return (
|
|
52
|
+
'<{} node_id="{}" name="{}" order="{}" '
|
|
53
|
+
'placement="{}" node_variation="{}"/>'.format(
|
|
54
|
+
type(self).__name__, self.node_id, self.name,
|
|
55
|
+
self.order, self.placement, self.node_variation
|
|
56
|
+
)
|
|
57
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Module for get node information from gateway."""
|
|
2
|
+
import struct
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from pyvlx.const import Command, OperatingState
|
|
6
|
+
from pyvlx.parameter import Parameter
|
|
7
|
+
|
|
8
|
+
from .frame import FrameBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class FrameNodeStatePositionChangedNotification(FrameBase):
|
|
12
|
+
"""Frame for notification of note information request."""
|
|
13
|
+
|
|
14
|
+
PAYLOAD_LEN = 20
|
|
15
|
+
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
"""Init Frame."""
|
|
18
|
+
super().__init__(Command.GW_NODE_STATE_POSITION_CHANGED_NTF)
|
|
19
|
+
self.node_id = 0
|
|
20
|
+
self.state: OperatingState = OperatingState.NON_EXECUTING
|
|
21
|
+
self.current_position = Parameter()
|
|
22
|
+
self.target = Parameter()
|
|
23
|
+
self.current_position_fp1 = Parameter()
|
|
24
|
+
self.current_position_fp2 = Parameter()
|
|
25
|
+
self.current_position_fp3 = Parameter()
|
|
26
|
+
self.current_position_fp4 = Parameter()
|
|
27
|
+
self.remaining_time = 0
|
|
28
|
+
self.timestamp = 0
|
|
29
|
+
|
|
30
|
+
def get_payload(self) -> bytes:
|
|
31
|
+
"""Return Payload."""
|
|
32
|
+
payload = bytes([self.node_id])
|
|
33
|
+
payload += bytes([self.state.value])
|
|
34
|
+
payload += bytes(self.current_position.raw)
|
|
35
|
+
payload += bytes(self.target.raw)
|
|
36
|
+
payload += bytes(self.current_position_fp1.raw)
|
|
37
|
+
payload += bytes(self.current_position_fp2.raw)
|
|
38
|
+
payload += bytes(self.current_position_fp3.raw)
|
|
39
|
+
payload += bytes(self.current_position_fp4.raw)
|
|
40
|
+
payload += bytes([self.remaining_time >> 8 & 255, self.remaining_time & 255])
|
|
41
|
+
payload += struct.pack(">I", self.timestamp)
|
|
42
|
+
return payload
|
|
43
|
+
|
|
44
|
+
def from_payload(self, payload: bytes) -> None:
|
|
45
|
+
"""Init frame from binary data."""
|
|
46
|
+
self.node_id = payload[0]
|
|
47
|
+
self.state = OperatingState(payload[1])
|
|
48
|
+
self.current_position = Parameter(payload[2:4])
|
|
49
|
+
self.target = Parameter(payload[4:6])
|
|
50
|
+
self.current_position_fp1 = Parameter(payload[6:8])
|
|
51
|
+
self.current_position_fp2 = Parameter(payload[8:10])
|
|
52
|
+
self.current_position_fp3 = Parameter(payload[10:12])
|
|
53
|
+
self.current_position_fp4 = Parameter(payload[12:14])
|
|
54
|
+
self.remaining_time = payload[14] * 256 + payload[15]
|
|
55
|
+
# @VELUX: looks like your timestamp is wrong. Looks like
|
|
56
|
+
# you are only transmitting the two lower bytes.
|
|
57
|
+
self.timestamp = struct.unpack(">I", payload[16:20])[0]
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def timestamp_formatted(self) -> str:
|
|
61
|
+
"""Return time as human readable string."""
|
|
62
|
+
return datetime.fromtimestamp(self.timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
|
63
|
+
|
|
64
|
+
def __str__(self) -> str:
|
|
65
|
+
"""Return human readable string."""
|
|
66
|
+
return (
|
|
67
|
+
'<{} node_id="{}" '
|
|
68
|
+
'state="{}" current_position="{}" '
|
|
69
|
+
'target="{}" current_position_fp1="{}" current_position_fp2="{}" '
|
|
70
|
+
'current_position_fp3="{}" current_position_fp4="{}" '
|
|
71
|
+
'remaining_time="{}" time="{}"/>'.format(
|
|
72
|
+
type(self).__name__,
|
|
73
|
+
self.node_id,
|
|
74
|
+
self.state.name,
|
|
75
|
+
self.current_position,
|
|
76
|
+
self.target,
|
|
77
|
+
self.current_position_fp1,
|
|
78
|
+
self.current_position_fp2,
|
|
79
|
+
self.current_position_fp3,
|
|
80
|
+
self.current_position_fp4,
|
|
81
|
+
self.remaining_time,
|
|
82
|
+
self.timestamp_formatted,
|
|
83
|
+
)
|
|
84
|
+
)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Module for password enter frame classes."""
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from pyvlx.const import Command
|
|
6
|
+
from pyvlx.exception import PyVLXException
|
|
7
|
+
from pyvlx.string_helper import bytes_to_string, string_to_bytes
|
|
8
|
+
|
|
9
|
+
from .frame import FrameBase
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FramePasswordChangeRequest(FrameBase):
|
|
13
|
+
"""Frame for sending password enter request."""
|
|
14
|
+
|
|
15
|
+
MAX_SIZE = 32
|
|
16
|
+
PAYLOAD_LEN = 64
|
|
17
|
+
|
|
18
|
+
def __init__(self, currentpassword: Optional[str] = None, newpassword: Optional[str] = None):
|
|
19
|
+
"""Init Frame."""
|
|
20
|
+
super().__init__(Command.GW_PASSWORD_CHANGE_REQ)
|
|
21
|
+
self.currentpassword = currentpassword
|
|
22
|
+
self.newpassword = newpassword
|
|
23
|
+
|
|
24
|
+
def get_payload(self) -> bytes:
|
|
25
|
+
"""Return Payload."""
|
|
26
|
+
if self.currentpassword is None:
|
|
27
|
+
raise PyVLXException("currentpassword is none")
|
|
28
|
+
if self.newpassword is None:
|
|
29
|
+
raise PyVLXException("newpassword is none")
|
|
30
|
+
if len(self.currentpassword) > self.MAX_SIZE:
|
|
31
|
+
raise PyVLXException("currentpassword is too long")
|
|
32
|
+
if len(self.newpassword) > self.MAX_SIZE:
|
|
33
|
+
raise PyVLXException("newpassword is too long")
|
|
34
|
+
|
|
35
|
+
return string_to_bytes(self.currentpassword,
|
|
36
|
+
self.MAX_SIZE)+string_to_bytes(self.newpassword, self.MAX_SIZE)
|
|
37
|
+
|
|
38
|
+
def from_payload(self, payload: bytes) -> None:
|
|
39
|
+
"""Init frame from binary data."""
|
|
40
|
+
self.currentpassword = bytes_to_string(payload[0:32])
|
|
41
|
+
self.newpassword = bytes_to_string(payload[32:])
|
|
42
|
+
|
|
43
|
+
def __str__(self) -> str:
|
|
44
|
+
"""Return human readable string."""
|
|
45
|
+
currentpassword_esc = (
|
|
46
|
+
None if self.currentpassword is None else "{}****".format(self.currentpassword[:2])
|
|
47
|
+
)
|
|
48
|
+
newpassword_esc = (
|
|
49
|
+
None if self.newpassword is None else "{}****".format(self.newpassword[:2])
|
|
50
|
+
)
|
|
51
|
+
return ('<{} currentpassword="{}" newpassword="{}"/>'
|
|
52
|
+
.format(type(self).__name__, currentpassword_esc, newpassword_esc))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class PasswordChangeConfirmationStatus(Enum):
|
|
56
|
+
"""Enum class for status of password change confirmation."""
|
|
57
|
+
|
|
58
|
+
SUCCESSFUL = 0
|
|
59
|
+
FAILED = 1
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class FramePasswordChangeConfirmation(FrameBase):
|
|
63
|
+
"""Frame for confirmation for sent password."""
|
|
64
|
+
|
|
65
|
+
PAYLOAD_LEN = 1
|
|
66
|
+
|
|
67
|
+
def __init__(self, status: PasswordChangeConfirmationStatus = PasswordChangeConfirmationStatus.SUCCESSFUL):
|
|
68
|
+
"""Init Frame."""
|
|
69
|
+
super().__init__(Command.GW_PASSWORD_CHANGE_CFM)
|
|
70
|
+
self.status = status
|
|
71
|
+
|
|
72
|
+
def get_payload(self) -> bytes:
|
|
73
|
+
"""Return Payload."""
|
|
74
|
+
return bytes([self.status.value])
|
|
75
|
+
|
|
76
|
+
def from_payload(self, payload: bytes) -> None:
|
|
77
|
+
"""Init frame from binary data."""
|
|
78
|
+
self.status = PasswordChangeConfirmationStatus(payload[0])
|
|
79
|
+
|
|
80
|
+
def __str__(self) -> str:
|
|
81
|
+
"""Return human readable string."""
|
|
82
|
+
return '<{} status="{}"/>'.format(type(self).__name__, self.status)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class FramePasswordChangeNotification(FrameBase):
|
|
86
|
+
"""Frame for sending password changed notification request."""
|
|
87
|
+
|
|
88
|
+
MAX_SIZE = 32
|
|
89
|
+
PAYLOAD_LEN = 32
|
|
90
|
+
|
|
91
|
+
def __init__(self, newpassword: Optional[str] = None):
|
|
92
|
+
"""Init Frame."""
|
|
93
|
+
super().__init__(Command.GW_PASSWORD_CHANGE_NTF)
|
|
94
|
+
self.newpassword = newpassword
|
|
95
|
+
|
|
96
|
+
def get_payload(self) -> bytes:
|
|
97
|
+
"""Return Payload."""
|
|
98
|
+
if self.newpassword is None:
|
|
99
|
+
raise PyVLXException("newpassword is none")
|
|
100
|
+
if len(self.newpassword) > self.MAX_SIZE:
|
|
101
|
+
raise PyVLXException("newpassword is too long")
|
|
102
|
+
|
|
103
|
+
return string_to_bytes(self.newpassword, self.MAX_SIZE)
|
|
104
|
+
|
|
105
|
+
def from_payload(self, payload: bytes) -> None:
|
|
106
|
+
"""Init frame from binary data."""
|
|
107
|
+
self.newpassword = bytes_to_string(payload)
|
|
108
|
+
|
|
109
|
+
def __str__(self) -> str:
|
|
110
|
+
"""Return human readable string."""
|
|
111
|
+
newpassword_esc = (
|
|
112
|
+
None if self.newpassword is None else "{}****".format(self.newpassword[:2])
|
|
113
|
+
)
|
|
114
|
+
return '<{} newpassword="{}"/>'.format(type(self).__name__, newpassword_esc)
|