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,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)