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/opening_device.py
ADDED
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
"""Module for Opening devices."""
|
|
2
|
+
import asyncio
|
|
3
|
+
import datetime
|
|
4
|
+
from asyncio import Task
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
6
|
+
|
|
7
|
+
from .api.command_send import CommandSend
|
|
8
|
+
from .api.get_limitation import GetLimitation
|
|
9
|
+
from .const import Velocity
|
|
10
|
+
from .exception import PyVLXException
|
|
11
|
+
from .node import Node
|
|
12
|
+
from .parameter import (
|
|
13
|
+
CurrentPosition, DualRollerShutterPosition, IgnorePosition, Parameter,
|
|
14
|
+
Position, TargetPosition)
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from pyvlx import PyVLX
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class OpeningDevice(Node):
|
|
21
|
+
"""Meta class for opening device with one main parameter for position."""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
pyvlx: "PyVLX",
|
|
26
|
+
node_id: int,
|
|
27
|
+
name: str,
|
|
28
|
+
serial_number: Optional[str] = None,
|
|
29
|
+
position_parameter: Parameter = Parameter(),
|
|
30
|
+
):
|
|
31
|
+
"""Initialize opening device.
|
|
32
|
+
|
|
33
|
+
Parameters:
|
|
34
|
+
* pyvlx: PyVLX object
|
|
35
|
+
* node_id: internal id for addressing nodes.
|
|
36
|
+
Provided by KLF 200 device
|
|
37
|
+
* name: node name
|
|
38
|
+
* serial_number: serial number of the node.
|
|
39
|
+
* position_parameter: initial position of the opening device.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
super().__init__(
|
|
43
|
+
pyvlx=pyvlx, node_id=node_id, name=name, serial_number=serial_number
|
|
44
|
+
)
|
|
45
|
+
self.position: Position = Position(parameter=position_parameter)
|
|
46
|
+
self.target: Position = Position(parameter=position_parameter)
|
|
47
|
+
self.is_opening: bool = False
|
|
48
|
+
self.is_closing: bool = False
|
|
49
|
+
self.state_received_at: Optional[datetime.datetime] = None
|
|
50
|
+
self.estimated_completion: Optional[datetime.datetime] = None
|
|
51
|
+
self.use_default_velocity: bool = False
|
|
52
|
+
self.default_velocity: Velocity = Velocity.DEFAULT
|
|
53
|
+
self.open_position_target: int = 0
|
|
54
|
+
self.close_position_target: int = 100
|
|
55
|
+
self._update_task: Task | None = None
|
|
56
|
+
|
|
57
|
+
async def _update_calls(self) -> None:
|
|
58
|
+
"""While cover are moving, perform periodically update calls."""
|
|
59
|
+
while self.is_moving():
|
|
60
|
+
await asyncio.sleep(1)
|
|
61
|
+
await self.after_update()
|
|
62
|
+
if self._update_task:
|
|
63
|
+
self._update_task.cancel()
|
|
64
|
+
self._update_task = None
|
|
65
|
+
|
|
66
|
+
async def set_position(
|
|
67
|
+
self,
|
|
68
|
+
position: Position,
|
|
69
|
+
velocity: Velocity | int | None = Velocity.DEFAULT,
|
|
70
|
+
wait_for_completion: bool = True,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Set opening device to desired position.
|
|
73
|
+
|
|
74
|
+
Parameters:
|
|
75
|
+
* position: Position object containing the target position.
|
|
76
|
+
* velocity: Velocity to be used during transition.
|
|
77
|
+
* wait_for_completion: If set, function will return
|
|
78
|
+
after device has reached target position.
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
kwargs: Any = {}
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
velocity is None or velocity is Velocity.DEFAULT
|
|
85
|
+
) and self.use_default_velocity:
|
|
86
|
+
velocity = self.default_velocity
|
|
87
|
+
|
|
88
|
+
if isinstance(velocity, Velocity):
|
|
89
|
+
if velocity is not Velocity.DEFAULT:
|
|
90
|
+
if velocity is Velocity.SILENT:
|
|
91
|
+
kwargs["fp1"] = Parameter(raw=b"\x00\x00")
|
|
92
|
+
else:
|
|
93
|
+
kwargs["fp1"] = Parameter(raw=b"\xC8\x00")
|
|
94
|
+
elif isinstance(velocity, int):
|
|
95
|
+
kwargs["fp1"] = Position.from_percent(velocity)
|
|
96
|
+
|
|
97
|
+
command = CommandSend(
|
|
98
|
+
pyvlx=self.pyvlx,
|
|
99
|
+
wait_for_completion=wait_for_completion,
|
|
100
|
+
node_id=self.node_id,
|
|
101
|
+
parameter=position,
|
|
102
|
+
functional_parameter=kwargs,
|
|
103
|
+
)
|
|
104
|
+
await command.send()
|
|
105
|
+
await self.after_update()
|
|
106
|
+
|
|
107
|
+
async def open(
|
|
108
|
+
self,
|
|
109
|
+
velocity: Velocity | int | None = Velocity.DEFAULT,
|
|
110
|
+
wait_for_completion: bool = True,
|
|
111
|
+
) -> None:
|
|
112
|
+
"""Open opening device.
|
|
113
|
+
|
|
114
|
+
Parameters:
|
|
115
|
+
* velocity: Velocity to be used during transition.
|
|
116
|
+
* wait_for_completion: If set, function will return
|
|
117
|
+
after device has reached target position.
|
|
118
|
+
|
|
119
|
+
"""
|
|
120
|
+
await self.set_position(
|
|
121
|
+
position=Position(position_percent=self.open_position_target),
|
|
122
|
+
velocity=velocity,
|
|
123
|
+
wait_for_completion=wait_for_completion,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
async def close(
|
|
127
|
+
self,
|
|
128
|
+
velocity: Velocity | int | None = Velocity.DEFAULT,
|
|
129
|
+
wait_for_completion: bool = True,
|
|
130
|
+
) -> None:
|
|
131
|
+
"""Close opening device.
|
|
132
|
+
|
|
133
|
+
Parameters:
|
|
134
|
+
* velocity: Velocity to be used during transition.
|
|
135
|
+
* wait_for_completion: If set, function will return
|
|
136
|
+
after device has reached target position.
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
await self.set_position(
|
|
140
|
+
position=Position(position_percent=self.close_position_target),
|
|
141
|
+
velocity=velocity,
|
|
142
|
+
wait_for_completion=wait_for_completion,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
async def stop(self, wait_for_completion: bool = True) -> None:
|
|
146
|
+
"""Stop opening device.
|
|
147
|
+
|
|
148
|
+
Parameters:
|
|
149
|
+
* wait_for_completion: If set, function will return
|
|
150
|
+
after device has reached target position.
|
|
151
|
+
|
|
152
|
+
"""
|
|
153
|
+
await self.set_position(
|
|
154
|
+
position=CurrentPosition(), wait_for_completion=wait_for_completion
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def is_moving(self) -> bool:
|
|
158
|
+
"""Return moving state of the cover."""
|
|
159
|
+
return self.is_opening or self.is_closing
|
|
160
|
+
|
|
161
|
+
def movement_percent(self) -> int:
|
|
162
|
+
"""Return movement percentage of the cover."""
|
|
163
|
+
if (
|
|
164
|
+
self.estimated_completion is None
|
|
165
|
+
or self.state_received_at is None
|
|
166
|
+
or self.estimated_completion < datetime.datetime.now()
|
|
167
|
+
):
|
|
168
|
+
return 100
|
|
169
|
+
|
|
170
|
+
movement_duration_s: float = (
|
|
171
|
+
self.estimated_completion - self.state_received_at
|
|
172
|
+
).total_seconds()
|
|
173
|
+
time_passed_s: float = (
|
|
174
|
+
datetime.datetime.now() - self.state_received_at
|
|
175
|
+
).total_seconds()
|
|
176
|
+
|
|
177
|
+
percent: int = int(time_passed_s / movement_duration_s * 100)
|
|
178
|
+
percent = max(percent, 0)
|
|
179
|
+
percent = min(percent, 100)
|
|
180
|
+
return percent
|
|
181
|
+
|
|
182
|
+
def get_position(self) -> Position:
|
|
183
|
+
"""Return position of the cover."""
|
|
184
|
+
if self.is_moving():
|
|
185
|
+
percent = self.movement_percent()
|
|
186
|
+
movement_origin = self.position.position_percent
|
|
187
|
+
movement_target = self.target.position_percent
|
|
188
|
+
current_position = (
|
|
189
|
+
movement_origin + (movement_target - movement_origin) / 100 * percent
|
|
190
|
+
)
|
|
191
|
+
if not self._update_task:
|
|
192
|
+
self._update_task = self.pyvlx.loop.create_task(self._update_calls())
|
|
193
|
+
return Position(position_percent=int(current_position))
|
|
194
|
+
return self.position
|
|
195
|
+
|
|
196
|
+
def __str__(self) -> str:
|
|
197
|
+
"""Return object as readable string."""
|
|
198
|
+
return '<{} name="{}" node_id="{}" serial_number="{}" position="{}"/>'.format(
|
|
199
|
+
type(self).__name__,
|
|
200
|
+
self.name,
|
|
201
|
+
self.node_id,
|
|
202
|
+
self.serial_number,
|
|
203
|
+
self.position,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class Window(OpeningDevice):
|
|
208
|
+
"""Window object."""
|
|
209
|
+
|
|
210
|
+
def __init__(
|
|
211
|
+
self,
|
|
212
|
+
pyvlx: "PyVLX",
|
|
213
|
+
node_id: int,
|
|
214
|
+
name: str,
|
|
215
|
+
serial_number: Optional[str],
|
|
216
|
+
position_parameter: Parameter = Parameter(),
|
|
217
|
+
rain_sensor: bool = False,
|
|
218
|
+
):
|
|
219
|
+
"""Initialize Window class.
|
|
220
|
+
|
|
221
|
+
Parameters:
|
|
222
|
+
* pyvlx: PyVLX object
|
|
223
|
+
* node_id: internal id for addressing nodes.
|
|
224
|
+
Provided by KLF 200 device
|
|
225
|
+
* name: node name
|
|
226
|
+
* serial_number: serial number of the node.
|
|
227
|
+
* position_parameter: initial position of the opening device.
|
|
228
|
+
* rain_sensor: set if device is equipped with a
|
|
229
|
+
rain sensor.
|
|
230
|
+
|
|
231
|
+
"""
|
|
232
|
+
super().__init__(
|
|
233
|
+
pyvlx=pyvlx,
|
|
234
|
+
node_id=node_id,
|
|
235
|
+
name=name,
|
|
236
|
+
serial_number=serial_number,
|
|
237
|
+
position_parameter=position_parameter,
|
|
238
|
+
)
|
|
239
|
+
self.rain_sensor = rain_sensor
|
|
240
|
+
|
|
241
|
+
def __str__(self) -> str:
|
|
242
|
+
"""Return object as readable string."""
|
|
243
|
+
return '<{} name="{}" node_id="{}" rain_sensor={} serial_number="{}" position="{}"/>'.format(
|
|
244
|
+
type(self).__name__,
|
|
245
|
+
self.name,
|
|
246
|
+
self.node_id,
|
|
247
|
+
self.rain_sensor,
|
|
248
|
+
self.serial_number,
|
|
249
|
+
self.position,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
async def get_limitation(self) -> GetLimitation:
|
|
253
|
+
"""Return limitation."""
|
|
254
|
+
get_limitation = GetLimitation(pyvlx=self.pyvlx, node_id=self.node_id)
|
|
255
|
+
await get_limitation.do_api_call()
|
|
256
|
+
if not get_limitation.success:
|
|
257
|
+
raise PyVLXException("Unable to send command")
|
|
258
|
+
return get_limitation
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class Blind(OpeningDevice):
|
|
262
|
+
"""Blind objects."""
|
|
263
|
+
|
|
264
|
+
def __init__(
|
|
265
|
+
self,
|
|
266
|
+
pyvlx: "PyVLX",
|
|
267
|
+
node_id: int,
|
|
268
|
+
name: str,
|
|
269
|
+
serial_number: Optional[str],
|
|
270
|
+
position_parameter: Parameter = Parameter(),
|
|
271
|
+
):
|
|
272
|
+
"""Initialize Blind class.
|
|
273
|
+
|
|
274
|
+
Parameters:
|
|
275
|
+
* pyvlx: PyVLX object
|
|
276
|
+
* node_id: internal id for addressing nodes.
|
|
277
|
+
Provided by KLF 200 device
|
|
278
|
+
* name: node name
|
|
279
|
+
|
|
280
|
+
"""
|
|
281
|
+
super().__init__(
|
|
282
|
+
pyvlx=pyvlx,
|
|
283
|
+
node_id=node_id,
|
|
284
|
+
name=name,
|
|
285
|
+
serial_number=serial_number,
|
|
286
|
+
position_parameter=position_parameter,
|
|
287
|
+
)
|
|
288
|
+
self.orientation: Position = Position(position_percent=0)
|
|
289
|
+
self.target_orientation: Position = TargetPosition()
|
|
290
|
+
self.target_position: Position = TargetPosition()
|
|
291
|
+
self.open_orientation_target: int = 50
|
|
292
|
+
self.close_orientation_target: int = 100
|
|
293
|
+
|
|
294
|
+
async def set_position_and_orientation(
|
|
295
|
+
self,
|
|
296
|
+
position: Position,
|
|
297
|
+
wait_for_completion: bool = True,
|
|
298
|
+
velocity: Velocity | int | None = None,
|
|
299
|
+
orientation: Optional[Position] = None,
|
|
300
|
+
) -> None:
|
|
301
|
+
"""Set window to desired position.
|
|
302
|
+
|
|
303
|
+
Parameters:
|
|
304
|
+
* position: Position object containing the current position.
|
|
305
|
+
* velocity: Velocity to be used during transition.
|
|
306
|
+
* target_position: Position object holding the target position
|
|
307
|
+
which allows to adjust the position while the blind is in movement
|
|
308
|
+
without stopping the blind (if orientation position has been changed.)
|
|
309
|
+
* wait_for_completion: If set, function will return
|
|
310
|
+
after device has reached target position.
|
|
311
|
+
* orientation: If set, the orientation of the device will be set in the same request.
|
|
312
|
+
Note, that, if the position is set to 0, the orientation will be set to 0 too.
|
|
313
|
+
|
|
314
|
+
"""
|
|
315
|
+
self.target_position = position
|
|
316
|
+
kwargs: Any = {}
|
|
317
|
+
|
|
318
|
+
if orientation is not None:
|
|
319
|
+
kwargs["fp3"] = orientation
|
|
320
|
+
elif self.target_position == Position(position_percent=0):
|
|
321
|
+
kwargs["fp3"] = Position(position_percent=0)
|
|
322
|
+
else:
|
|
323
|
+
kwargs["fp3"] = IgnorePosition()
|
|
324
|
+
|
|
325
|
+
if (
|
|
326
|
+
velocity is None or velocity is Velocity.DEFAULT
|
|
327
|
+
) and self.use_default_velocity:
|
|
328
|
+
velocity = self.default_velocity
|
|
329
|
+
|
|
330
|
+
if isinstance(velocity, Velocity):
|
|
331
|
+
if velocity is not Velocity.DEFAULT:
|
|
332
|
+
if velocity is Velocity.SILENT:
|
|
333
|
+
# The above code is declaring a variable called `kwargs`.
|
|
334
|
+
kwargs["fp1"] = Parameter(raw=b"\x00\x00")
|
|
335
|
+
else:
|
|
336
|
+
kwargs["fp1"] = Parameter(raw=b"\xC8\x00")
|
|
337
|
+
elif isinstance(velocity, int):
|
|
338
|
+
kwargs["fp1"] = Position.from_percent(velocity)
|
|
339
|
+
|
|
340
|
+
command = CommandSend(
|
|
341
|
+
pyvlx=self.pyvlx,
|
|
342
|
+
node_id=self.node_id,
|
|
343
|
+
parameter=position,
|
|
344
|
+
wait_for_completion=wait_for_completion,
|
|
345
|
+
**kwargs
|
|
346
|
+
)
|
|
347
|
+
await command.send()
|
|
348
|
+
await self.after_update()
|
|
349
|
+
|
|
350
|
+
async def set_position(
|
|
351
|
+
self,
|
|
352
|
+
position: Position,
|
|
353
|
+
velocity: Velocity | int | None = Velocity.DEFAULT,
|
|
354
|
+
wait_for_completion: bool = True,
|
|
355
|
+
) -> None:
|
|
356
|
+
"""Set window to desired position.
|
|
357
|
+
|
|
358
|
+
Parameters:
|
|
359
|
+
* position: Position object containing the current position.
|
|
360
|
+
* velocity: Velocity to be used during transition.
|
|
361
|
+
* target_position: Position object holding the target position
|
|
362
|
+
which allows to adjust the position while the blind is in movement
|
|
363
|
+
without stopping the blind (if orientation position has been changed.)
|
|
364
|
+
* wait_for_completion: If set, function will return
|
|
365
|
+
after device has reached target position.
|
|
366
|
+
"""
|
|
367
|
+
await self.set_position_and_orientation(
|
|
368
|
+
position=position,
|
|
369
|
+
wait_for_completion=wait_for_completion,
|
|
370
|
+
velocity=velocity,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
async def open(
|
|
374
|
+
self,
|
|
375
|
+
velocity: Velocity | int | None = Velocity.DEFAULT,
|
|
376
|
+
wait_for_completion: bool = True,
|
|
377
|
+
) -> None:
|
|
378
|
+
"""Open window.
|
|
379
|
+
|
|
380
|
+
Parameters:
|
|
381
|
+
* velocity: Velocity to be used during transition.
|
|
382
|
+
* wait_for_completion: If set, function will return
|
|
383
|
+
after device has reached target position.
|
|
384
|
+
"""
|
|
385
|
+
await self.set_position(
|
|
386
|
+
position=Position(position_percent=self.open_position_target),
|
|
387
|
+
velocity=velocity,
|
|
388
|
+
wait_for_completion=wait_for_completion,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
async def close(
|
|
392
|
+
self,
|
|
393
|
+
velocity: Velocity | int | None = Velocity.DEFAULT,
|
|
394
|
+
wait_for_completion: bool = True,
|
|
395
|
+
) -> None:
|
|
396
|
+
"""Close window.
|
|
397
|
+
|
|
398
|
+
Parameters:
|
|
399
|
+
* velocity: Velocity to be used during transition.
|
|
400
|
+
* wait_for_completion: If set, function will return
|
|
401
|
+
after device has reached target position.
|
|
402
|
+
"""
|
|
403
|
+
await self.set_position(
|
|
404
|
+
position=Position(position_percent=self.close_position_target),
|
|
405
|
+
velocity=velocity,
|
|
406
|
+
wait_for_completion=wait_for_completion,
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
async def stop(self, wait_for_completion: bool = True) -> None:
|
|
410
|
+
"""Stop Blind position."""
|
|
411
|
+
await self.set_position_and_orientation(
|
|
412
|
+
position=CurrentPosition(),
|
|
413
|
+
wait_for_completion=wait_for_completion,
|
|
414
|
+
orientation=self.target_orientation,
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
async def set_orientation(
|
|
418
|
+
self, orientation: Position, wait_for_completion: bool = True
|
|
419
|
+
) -> None:
|
|
420
|
+
"""Set Blind shades to desired orientation.
|
|
421
|
+
|
|
422
|
+
Parameters:
|
|
423
|
+
* orientation: Position object containing the target orientation.
|
|
424
|
+
+ target_orientation: Position object holding the target orientation
|
|
425
|
+
which allows to adjust the orientation while the blind is in movement
|
|
426
|
+
without stopping the blind (if the position has been changed.)
|
|
427
|
+
* wait_for_completion: If set, function will return
|
|
428
|
+
after device has reached target position.
|
|
429
|
+
|
|
430
|
+
"""
|
|
431
|
+
self.target_orientation = orientation
|
|
432
|
+
self.orientation = orientation
|
|
433
|
+
|
|
434
|
+
fp3 = (
|
|
435
|
+
Position(position_percent=0)
|
|
436
|
+
if self.target_position == Position(position_percent=0)
|
|
437
|
+
else self.target_orientation
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
print("Orientation in device: %s " % (orientation))
|
|
441
|
+
command = CommandSend(
|
|
442
|
+
pyvlx=self.pyvlx,
|
|
443
|
+
wait_for_completion=wait_for_completion,
|
|
444
|
+
node_id=self.node_id,
|
|
445
|
+
parameter=self.target_position,
|
|
446
|
+
fp3=fp3,
|
|
447
|
+
)
|
|
448
|
+
await command.send()
|
|
449
|
+
await self.after_update()
|
|
450
|
+
# KLF200 always send UNKNOWN position for functional parameter,
|
|
451
|
+
# so orientation is set directly and not via GW_NODE_STATE_POSITION_CHANGED_NTF
|
|
452
|
+
|
|
453
|
+
async def open_orientation(self, wait_for_completion: bool = True) -> None:
|
|
454
|
+
"""Open Blind slats orientation.
|
|
455
|
+
|
|
456
|
+
Blind slats with ±90° orientation are open at 50%
|
|
457
|
+
"""
|
|
458
|
+
await self.set_orientation(
|
|
459
|
+
orientation=Position(position_percent=self.open_orientation_target),
|
|
460
|
+
wait_for_completion=wait_for_completion,
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
async def close_orientation(self, wait_for_completion: bool = True) -> None:
|
|
464
|
+
"""Close Blind slats."""
|
|
465
|
+
await self.set_orientation(
|
|
466
|
+
orientation=Position(position_percent=self.close_orientation_target),
|
|
467
|
+
wait_for_completion=wait_for_completion,
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
async def stop_orientation(self, wait_for_completion: bool = True) -> None:
|
|
471
|
+
"""Stop Blind slats."""
|
|
472
|
+
await self.set_orientation(
|
|
473
|
+
orientation=CurrentPosition(), wait_for_completion=wait_for_completion
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
class Awning(OpeningDevice):
|
|
478
|
+
"""Awning objects."""
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
class DualRollerShutter(OpeningDevice):
|
|
482
|
+
"""DualRollerShutter object."""
|
|
483
|
+
|
|
484
|
+
def __init__(
|
|
485
|
+
self,
|
|
486
|
+
pyvlx: "PyVLX",
|
|
487
|
+
node_id: int,
|
|
488
|
+
name: str,
|
|
489
|
+
serial_number: Optional[str],
|
|
490
|
+
position_parameter: Parameter = Parameter(),
|
|
491
|
+
):
|
|
492
|
+
"""Initialize Blind class.
|
|
493
|
+
|
|
494
|
+
Parameters:
|
|
495
|
+
* pyvlx: PyVLX object
|
|
496
|
+
* node_id: internal id for addressing nodes.
|
|
497
|
+
Provided by KLF 200 device
|
|
498
|
+
* name: node name
|
|
499
|
+
|
|
500
|
+
"""
|
|
501
|
+
super().__init__(
|
|
502
|
+
pyvlx=pyvlx,
|
|
503
|
+
node_id=node_id,
|
|
504
|
+
name=name,
|
|
505
|
+
serial_number=serial_number,
|
|
506
|
+
position_parameter=position_parameter,
|
|
507
|
+
)
|
|
508
|
+
self.position_upper_curtain: Position = Position(position_percent=0)
|
|
509
|
+
self.position_lower_curtain: Position = Position(position_percent=0)
|
|
510
|
+
self.target_position: Any = Position()
|
|
511
|
+
self.active_parameter: int = 0
|
|
512
|
+
|
|
513
|
+
async def set_position(
|
|
514
|
+
self,
|
|
515
|
+
position: Position,
|
|
516
|
+
velocity: Velocity | int | None = Velocity.DEFAULT,
|
|
517
|
+
wait_for_completion: bool = True,
|
|
518
|
+
curtain: str = "dual",
|
|
519
|
+
) -> None:
|
|
520
|
+
"""Set DualRollerShutter to desired position.
|
|
521
|
+
|
|
522
|
+
Parameters:
|
|
523
|
+
* position: Position object containing the current position.
|
|
524
|
+
* target_position: Position object holding the target position
|
|
525
|
+
which allows to adjust the position while the blind is in movement
|
|
526
|
+
* wait_for_completion: If set, function will return
|
|
527
|
+
after device has reached target position.
|
|
528
|
+
"""
|
|
529
|
+
kwargs: Any = {}
|
|
530
|
+
|
|
531
|
+
if curtain == "upper":
|
|
532
|
+
self.target_position = DualRollerShutterPosition()
|
|
533
|
+
self.active_parameter = 1
|
|
534
|
+
kwargs["fp1"] = position
|
|
535
|
+
kwargs["fp2"] = TargetPosition()
|
|
536
|
+
elif curtain == "lower":
|
|
537
|
+
self.target_position = DualRollerShutterPosition()
|
|
538
|
+
self.active_parameter = 2
|
|
539
|
+
kwargs["fp1"] = TargetPosition()
|
|
540
|
+
kwargs["fp2"] = position
|
|
541
|
+
else:
|
|
542
|
+
self.target_position = position
|
|
543
|
+
self.active_parameter = 0
|
|
544
|
+
|
|
545
|
+
if (
|
|
546
|
+
velocity is None or velocity is Velocity.DEFAULT
|
|
547
|
+
) and self.use_default_velocity:
|
|
548
|
+
velocity = self.default_velocity
|
|
549
|
+
|
|
550
|
+
if isinstance(velocity, Velocity):
|
|
551
|
+
if velocity is not Velocity.DEFAULT:
|
|
552
|
+
if velocity is Velocity.SILENT:
|
|
553
|
+
kwargs["fp3"] = Parameter(raw=b"\x00\x00")
|
|
554
|
+
else:
|
|
555
|
+
kwargs["fp3"] = Parameter(raw=b"\xC8\x00")
|
|
556
|
+
elif isinstance(velocity, int):
|
|
557
|
+
kwargs["fp3"] = Position.from_percent(velocity)
|
|
558
|
+
|
|
559
|
+
command = CommandSend(
|
|
560
|
+
pyvlx=self.pyvlx,
|
|
561
|
+
wait_for_completion=wait_for_completion,
|
|
562
|
+
node_id=self.node_id,
|
|
563
|
+
parameter=self.target_position,
|
|
564
|
+
active_parameter=self.active_parameter,
|
|
565
|
+
**kwargs
|
|
566
|
+
)
|
|
567
|
+
await command.send()
|
|
568
|
+
if position.position <= Position.MAX:
|
|
569
|
+
if curtain == "upper":
|
|
570
|
+
self.position_upper_curtain = position
|
|
571
|
+
elif curtain == "lower":
|
|
572
|
+
self.position_lower_curtain = position
|
|
573
|
+
else:
|
|
574
|
+
self.position = position
|
|
575
|
+
await self.after_update()
|
|
576
|
+
|
|
577
|
+
async def open(
|
|
578
|
+
self,
|
|
579
|
+
velocity: Velocity | int | None = Velocity.DEFAULT,
|
|
580
|
+
wait_for_completion: bool = True,
|
|
581
|
+
curtain: str = "dual",
|
|
582
|
+
) -> None:
|
|
583
|
+
"""Open DualRollerShutter.
|
|
584
|
+
|
|
585
|
+
Parameters:
|
|
586
|
+
* wait_for_completion: If set, function will return
|
|
587
|
+
after device has reached target position.
|
|
588
|
+
|
|
589
|
+
"""
|
|
590
|
+
await self.set_position(
|
|
591
|
+
position=Position(position_percent=self.open_position_target),
|
|
592
|
+
velocity=velocity,
|
|
593
|
+
wait_for_completion=wait_for_completion,
|
|
594
|
+
curtain=curtain,
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
async def close(
|
|
598
|
+
self,
|
|
599
|
+
velocity: Velocity | int | None = Velocity.DEFAULT,
|
|
600
|
+
wait_for_completion: bool = True,
|
|
601
|
+
curtain: str = "dual",
|
|
602
|
+
) -> None:
|
|
603
|
+
"""Close DualRollerShutter.
|
|
604
|
+
|
|
605
|
+
Parameters:
|
|
606
|
+
* wait_for_completion: If set, function will return
|
|
607
|
+
after device has reached target position.
|
|
608
|
+
"""
|
|
609
|
+
await self.set_position(
|
|
610
|
+
position=Position(position_percent=self.close_position_target),
|
|
611
|
+
velocity=velocity,
|
|
612
|
+
wait_for_completion=wait_for_completion,
|
|
613
|
+
curtain=curtain,
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
async def stop(
|
|
617
|
+
self,
|
|
618
|
+
wait_for_completion: bool = True,
|
|
619
|
+
velocity: Velocity | int | None = Velocity.DEFAULT,
|
|
620
|
+
curtain: str = "dual",
|
|
621
|
+
) -> None:
|
|
622
|
+
"""Stop Blind position."""
|
|
623
|
+
await self.set_position(
|
|
624
|
+
position=CurrentPosition(),
|
|
625
|
+
velocity=velocity,
|
|
626
|
+
wait_for_completion=wait_for_completion,
|
|
627
|
+
curtain=curtain,
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
class RollerShutter(OpeningDevice):
|
|
632
|
+
"""RollerShutter object."""
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
class GarageDoor(OpeningDevice):
|
|
636
|
+
"""GarageDoor object."""
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
class Gate(OpeningDevice):
|
|
640
|
+
"""Gate object."""
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
class Blade(OpeningDevice):
|
|
644
|
+
"""Blade object."""
|