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
pyvlx/connection.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Module for handling the TCP connection with Gateway."""
|
|
2
|
+
import asyncio
|
|
3
|
+
import ssl
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Callable, Coroutine, List, Optional, Set
|
|
6
|
+
|
|
7
|
+
from .api.frame_creation import frame_from_raw
|
|
8
|
+
from .api.frames import FrameBase
|
|
9
|
+
from .config import Config
|
|
10
|
+
from .exception import PyVLXException
|
|
11
|
+
from .log import PYVLXLOG
|
|
12
|
+
from .slip import get_next_slip, is_slip, slip_pack
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SlipTokenizer:
|
|
16
|
+
"""Helper class for splitting up binary stream to slip packets."""
|
|
17
|
+
|
|
18
|
+
def __init__(self) -> None:
|
|
19
|
+
"""Init Tokenizer."""
|
|
20
|
+
self.data = bytes()
|
|
21
|
+
|
|
22
|
+
def feed(self, chunk: bytes) -> None:
|
|
23
|
+
"""Feed chunk to tokenizer."""
|
|
24
|
+
if not chunk:
|
|
25
|
+
return
|
|
26
|
+
self.data += chunk
|
|
27
|
+
|
|
28
|
+
def has_tokens(self) -> bool:
|
|
29
|
+
"""Return True if Tokenizer has tokens."""
|
|
30
|
+
return is_slip(self.data)
|
|
31
|
+
|
|
32
|
+
def get_next_token(self) -> Optional[bytes]:
|
|
33
|
+
"""Get next token from Tokenizer."""
|
|
34
|
+
slip, self.data = get_next_slip(self.data)
|
|
35
|
+
return slip
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TCPTransport(asyncio.Protocol):
|
|
39
|
+
"""Class for handling asyncio connection transport."""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
frame_received_cb: Callable[[FrameBase], None],
|
|
44
|
+
connection_lost_cb: Callable[[], None],
|
|
45
|
+
):
|
|
46
|
+
"""Init TCPTransport."""
|
|
47
|
+
self.frame_received_cb = frame_received_cb
|
|
48
|
+
self.connection_lost_cb = connection_lost_cb
|
|
49
|
+
self.tokenizer = SlipTokenizer()
|
|
50
|
+
|
|
51
|
+
def connection_made(self, transport: object) -> None:
|
|
52
|
+
"""Handle sucessful connection."""
|
|
53
|
+
PYVLXLOG.debug("Socket connection to KLF 200 opened")
|
|
54
|
+
|
|
55
|
+
def data_received(self, data: bytes) -> None:
|
|
56
|
+
"""Handle data received."""
|
|
57
|
+
self.tokenizer.feed(data)
|
|
58
|
+
while self.tokenizer.has_tokens():
|
|
59
|
+
raw = self.tokenizer.get_next_token()
|
|
60
|
+
assert raw is not None
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
frame = frame_from_raw(raw)
|
|
64
|
+
if frame is not None:
|
|
65
|
+
self.frame_received_cb(frame)
|
|
66
|
+
except PyVLXException:
|
|
67
|
+
PYVLXLOG.error("Error in data_received", exc_info=sys.exc_info())
|
|
68
|
+
|
|
69
|
+
def connection_lost(self, exc: object) -> None:
|
|
70
|
+
"""Handle lost connection."""
|
|
71
|
+
PYVLXLOG.debug("Socket connection to KLF 200 has been lost")
|
|
72
|
+
self.connection_lost_cb()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
CallbackType = Callable[[FrameBase], Coroutine]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Connection:
|
|
79
|
+
"""Class for handling TCP connection."""
|
|
80
|
+
|
|
81
|
+
def __init__(self, loop: asyncio.AbstractEventLoop, config: Config):
|
|
82
|
+
"""Init TCP connection."""
|
|
83
|
+
self.loop = loop
|
|
84
|
+
self.config = config
|
|
85
|
+
self.transport: Optional[asyncio.Transport] = None
|
|
86
|
+
self.frame_received_cbs: List[CallbackType] = []
|
|
87
|
+
self.connection_closed_cbs: List[Callable[[], Coroutine]] = []
|
|
88
|
+
self.connection_opened_cbs: List[Callable[[], Coroutine]] = []
|
|
89
|
+
self.connected = False
|
|
90
|
+
self.connection_counter = 0
|
|
91
|
+
self.tasks: Set[asyncio.Task] = set()
|
|
92
|
+
|
|
93
|
+
def __del__(self) -> None:
|
|
94
|
+
"""Destruct connection."""
|
|
95
|
+
self.disconnect()
|
|
96
|
+
|
|
97
|
+
def disconnect(self) -> None:
|
|
98
|
+
"""Disconnect connection."""
|
|
99
|
+
if self.transport is not None:
|
|
100
|
+
self.transport.close()
|
|
101
|
+
self.transport = None
|
|
102
|
+
self.connected = False
|
|
103
|
+
PYVLXLOG.debug("TCP transport closed.")
|
|
104
|
+
for connection_closed_cb in self.connection_closed_cbs:
|
|
105
|
+
if asyncio.iscoroutine(connection_closed_cb()):
|
|
106
|
+
task = self.loop.create_task(connection_closed_cb())
|
|
107
|
+
self.tasks.add(task)
|
|
108
|
+
task.add_done_callback(self.tasks.remove)
|
|
109
|
+
|
|
110
|
+
async def connect(self) -> None:
|
|
111
|
+
"""Connect to gateway via SSL."""
|
|
112
|
+
tcp_client = TCPTransport(self.frame_received_cb, connection_lost_cb=self.on_connection_lost)
|
|
113
|
+
assert self.config.host is not None
|
|
114
|
+
self.transport, _ = await self.loop.create_connection(
|
|
115
|
+
lambda: tcp_client,
|
|
116
|
+
host=self.config.host,
|
|
117
|
+
port=self.config.port,
|
|
118
|
+
ssl=self.create_ssl_context(),
|
|
119
|
+
)
|
|
120
|
+
self.connected = True
|
|
121
|
+
self.connection_counter += 1
|
|
122
|
+
PYVLXLOG.debug(
|
|
123
|
+
"Amount of connections since last HA start: %s", self.connection_counter
|
|
124
|
+
)
|
|
125
|
+
for connection_opened_cb in self.connection_opened_cbs:
|
|
126
|
+
if asyncio.iscoroutine(connection_opened_cb()):
|
|
127
|
+
task = self.loop.create_task(connection_opened_cb())
|
|
128
|
+
self.tasks.add(task)
|
|
129
|
+
task.add_done_callback(self.tasks.remove)
|
|
130
|
+
|
|
131
|
+
def register_frame_received_cb(self, callback: CallbackType) -> None:
|
|
132
|
+
"""Register frame received callback."""
|
|
133
|
+
self.frame_received_cbs.append(callback)
|
|
134
|
+
|
|
135
|
+
def unregister_frame_received_cb(self, callback: CallbackType) -> None:
|
|
136
|
+
"""Unregister frame received callback."""
|
|
137
|
+
self.frame_received_cbs.remove(callback)
|
|
138
|
+
|
|
139
|
+
def register_connection_closed_cb(self, callback: Callable[[], Coroutine]) -> None:
|
|
140
|
+
"""Register connection closed callback."""
|
|
141
|
+
self.connection_closed_cbs.append(callback)
|
|
142
|
+
|
|
143
|
+
def unregister_connection_closed_cb(self, callback: Callable[[], Coroutine]) -> None:
|
|
144
|
+
"""Unregister connection closed callback."""
|
|
145
|
+
self.connection_closed_cbs.remove(callback)
|
|
146
|
+
|
|
147
|
+
def register_connection_opened_cb(self, callback: Callable[[], Coroutine]) -> None:
|
|
148
|
+
"""Register connection opened callback."""
|
|
149
|
+
self.connection_opened_cbs.append(callback)
|
|
150
|
+
|
|
151
|
+
def unregister_connection_opened_cb(self, callback: Callable[[], Coroutine]) -> None:
|
|
152
|
+
"""Unregister connection opened callback."""
|
|
153
|
+
self.connection_opened_cbs.remove(callback)
|
|
154
|
+
|
|
155
|
+
def write(self, frame: FrameBase) -> None:
|
|
156
|
+
"""Write frame to Bus."""
|
|
157
|
+
if not isinstance(frame, FrameBase):
|
|
158
|
+
raise PyVLXException("Frame not of type FrameBase", *type(frame))
|
|
159
|
+
PYVLXLOG.debug("SEND: %s", frame)
|
|
160
|
+
assert self.transport is not None
|
|
161
|
+
self.transport.write(slip_pack(bytes(frame)))
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def create_ssl_context() -> ssl.SSLContext:
|
|
165
|
+
"""Create and return SSL Context."""
|
|
166
|
+
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
|
167
|
+
ssl_context.check_hostname = False
|
|
168
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
|
169
|
+
return ssl_context
|
|
170
|
+
|
|
171
|
+
def frame_received_cb(self, frame: FrameBase) -> None:
|
|
172
|
+
"""Received message."""
|
|
173
|
+
PYVLXLOG.debug("REC: %s", frame)
|
|
174
|
+
for frame_received_cb in self.frame_received_cbs:
|
|
175
|
+
# pylint: disable=not-callable
|
|
176
|
+
task = self.loop.create_task(frame_received_cb(frame))
|
|
177
|
+
self.tasks.add(task)
|
|
178
|
+
task.add_done_callback(self.tasks.remove)
|
|
179
|
+
|
|
180
|
+
def on_connection_lost(self) -> None:
|
|
181
|
+
"""Server closed connection."""
|
|
182
|
+
self.disconnect()
|