pypck 0.8.5__tar.gz → 0.8.6__tar.gz
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.
- {pypck-0.8.5/pypck.egg-info → pypck-0.8.6}/PKG-INFO +3 -2
- pypck-0.8.6/VERSION +1 -0
- {pypck-0.8.5 → pypck-0.8.6}/pypck/inputs.py +156 -5
- {pypck-0.8.5 → pypck-0.8.6}/pypck/lcn_defs.py +48 -7
- {pypck-0.8.5 → pypck-0.8.6}/pypck/module.py +38 -10
- {pypck-0.8.5 → pypck-0.8.6}/pypck/pck_commands.py +121 -38
- {pypck-0.8.5 → pypck-0.8.6}/pypck/request_handlers.py +24 -1
- {pypck-0.8.5 → pypck-0.8.6/pypck.egg-info}/PKG-INFO +3 -2
- {pypck-0.8.5 → pypck-0.8.6}/tests/test_commands.py +84 -53
- {pypck-0.8.5 → pypck-0.8.6}/tests/test_messages.py +29 -0
- pypck-0.8.5/VERSION +0 -1
- {pypck-0.8.5 → pypck-0.8.6}/LICENSE +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/README.md +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/pypck/__init__.py +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/pypck/connection.py +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/pypck/helpers.py +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/pypck/lcn_addr.py +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/pypck/timeout_retry.py +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/pypck.egg-info/SOURCES.txt +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/pypck.egg-info/dependency_links.txt +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/pypck.egg-info/not-zip-safe +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/pypck.egg-info/top_level.txt +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/pyproject.toml +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/setup.cfg +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/tests/test_connection.py +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/tests/test_dyn_text.py +0 -0
- {pypck-0.8.5 → pypck-0.8.6}/tests/test_vars.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pypck
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.6
|
|
4
4
|
Summary: LCN-PCK library
|
|
5
5
|
Home-page: https://github.com/alengwenus/pypck
|
|
6
6
|
Author-email: Andre Lengwenus <alengwenus@gmail.com>
|
|
@@ -18,6 +18,7 @@ Classifier: Topic :: Home Automation
|
|
|
18
18
|
Requires-Python: >=3.11
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
|
+
Dynamic: license-file
|
|
21
22
|
|
|
22
23
|
# pypck - Asynchronous LCN-PCK library written in Python
|
|
23
24
|
|
pypck-0.8.6/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.8.6
|
|
@@ -398,8 +398,10 @@ class ModSn(ModInput):
|
|
|
398
398
|
ValueError
|
|
399
399
|
): # unconventional manufacturer code (e.g., due to LinHK VM)
|
|
400
400
|
manu = 0xFF
|
|
401
|
-
_LOGGER.
|
|
402
|
-
"Unconventional manufacturer code: %s. Defaulting to 0x%02X",
|
|
401
|
+
_LOGGER.warning(
|
|
402
|
+
"Unconventional manufacturer code for module (S%d, M%d): %s. Defaulting to 0x%02X",
|
|
403
|
+
addr.seg_id,
|
|
404
|
+
addr.addr_id,
|
|
403
405
|
matcher.group("manu"),
|
|
404
406
|
manu,
|
|
405
407
|
)
|
|
@@ -596,7 +598,10 @@ class ModStatusOutputNative(ModInput):
|
|
|
596
598
|
|
|
597
599
|
|
|
598
600
|
class ModStatusRelays(ModInput):
|
|
599
|
-
"""Status of 8 relays received from an LCN module.
|
|
601
|
+
"""Status of 8 relays received from an LCN module.
|
|
602
|
+
|
|
603
|
+
Includes helper functions for motor states based on LCN wiring.
|
|
604
|
+
"""
|
|
600
605
|
|
|
601
606
|
def __init__(self, physical_source_addr: LcnAddr, states: list[bool]):
|
|
602
607
|
"""Construct ModInput object."""
|
|
@@ -614,6 +619,43 @@ class ModStatusRelays(ModInput):
|
|
|
614
619
|
"""
|
|
615
620
|
return self.states[relay_id]
|
|
616
621
|
|
|
622
|
+
def get_motor_onoff_relay(self, motor_id: int) -> int:
|
|
623
|
+
"""Get the motor on/off relay id."""
|
|
624
|
+
if 0 > motor_id > 3:
|
|
625
|
+
raise ValueError("Motor id must be in range 0..3")
|
|
626
|
+
return motor_id * 2
|
|
627
|
+
|
|
628
|
+
def get_motor_updown_relay(self, motor_id: int) -> int:
|
|
629
|
+
"""Get the motor up/down relay id."""
|
|
630
|
+
if 0 > motor_id > 3:
|
|
631
|
+
raise ValueError("Motor id must be in range 0..3")
|
|
632
|
+
return motor_id * 2 + 1
|
|
633
|
+
|
|
634
|
+
def motor_is_on(self, motor_id: int) -> bool:
|
|
635
|
+
"""Check if a motor is on."""
|
|
636
|
+
return self.states[self.get_motor_onoff_relay(motor_id)]
|
|
637
|
+
|
|
638
|
+
def is_opening(self, motor_id: int) -> bool:
|
|
639
|
+
"""Check if a motor is opening."""
|
|
640
|
+
if self.motor_is_on(motor_id):
|
|
641
|
+
return not self.states[self.get_motor_updown_relay(motor_id)]
|
|
642
|
+
return False
|
|
643
|
+
|
|
644
|
+
def is_closing(self, motor_id: int) -> bool:
|
|
645
|
+
"""Check if a motor is closing."""
|
|
646
|
+
if self.motor_is_on(motor_id):
|
|
647
|
+
return self.states[self.get_motor_updown_relay(motor_id)]
|
|
648
|
+
return False
|
|
649
|
+
|
|
650
|
+
def is_assumed_closed(self, motor_id: int) -> bool:
|
|
651
|
+
"""Check if a motor is closed.
|
|
652
|
+
|
|
653
|
+
The closed state is assumed if the motor direction is down and the motor is switched off."
|
|
654
|
+
"""
|
|
655
|
+
if not self.motor_is_on(motor_id):
|
|
656
|
+
return self.states[self.get_motor_updown_relay(motor_id)]
|
|
657
|
+
return False
|
|
658
|
+
|
|
617
659
|
@staticmethod
|
|
618
660
|
def try_parse(data: str) -> list[Input] | None:
|
|
619
661
|
"""Try to parse the given input text.
|
|
@@ -1024,13 +1066,120 @@ class ModStatusSceneOutputs(ModInput):
|
|
|
1024
1066
|
if matcher:
|
|
1025
1067
|
addr = LcnAddr(int(matcher.group("seg_id")), int(matcher.group("mod_id")))
|
|
1026
1068
|
scene_id = int(matcher.group("scene_id"))
|
|
1027
|
-
values = [int(matcher.group(f"output{i+1:d}")) for i in range(4)]
|
|
1028
|
-
ramps = [int(matcher.group(f"ramp{i+1:d}")) for i in range(4)]
|
|
1069
|
+
values = [int(matcher.group(f"output{i + 1:d}")) for i in range(4)]
|
|
1070
|
+
ramps = [int(matcher.group(f"ramp{i + 1:d}")) for i in range(4)]
|
|
1029
1071
|
return [ModStatusSceneOutputs(addr, scene_id, values, ramps)]
|
|
1030
1072
|
|
|
1031
1073
|
return None
|
|
1032
1074
|
|
|
1033
1075
|
|
|
1076
|
+
class ModStatusMotorPositionBS4(ModInput):
|
|
1077
|
+
"""Status of motor positions (if BS4 connected) received from an LCN module.
|
|
1078
|
+
|
|
1079
|
+
Position and limit is in percent. 0%: cover closed, 100%: cover open.
|
|
1080
|
+
"""
|
|
1081
|
+
|
|
1082
|
+
def __init__(
|
|
1083
|
+
self,
|
|
1084
|
+
physical_source_addr: LcnAddr,
|
|
1085
|
+
motor: int,
|
|
1086
|
+
position: float,
|
|
1087
|
+
limit: float | None = None,
|
|
1088
|
+
time_down: int | None = None,
|
|
1089
|
+
time_up: int | None = None,
|
|
1090
|
+
):
|
|
1091
|
+
"""Construct ModInput object."""
|
|
1092
|
+
super().__init__(physical_source_addr)
|
|
1093
|
+
self.motor = motor
|
|
1094
|
+
self.position = position
|
|
1095
|
+
self.limit = limit
|
|
1096
|
+
self.time_down = time_down
|
|
1097
|
+
self.time_up = time_up
|
|
1098
|
+
|
|
1099
|
+
@staticmethod
|
|
1100
|
+
def try_parse(data: str) -> list[Input] | None:
|
|
1101
|
+
"""Try to parse the given input text.
|
|
1102
|
+
|
|
1103
|
+
Will return a list of parsed Inputs. The list might be empty (but not
|
|
1104
|
+
null).
|
|
1105
|
+
|
|
1106
|
+
:param data str: The input data received from LCN-PCHK
|
|
1107
|
+
|
|
1108
|
+
:return: The parsed Inputs (never null)
|
|
1109
|
+
:rtype: List with instances of :class:`~pypck.input.Input`
|
|
1110
|
+
"""
|
|
1111
|
+
matcher = PckParser.PATTERN_STATUS_MOTOR_POSITION_BS4.match(data)
|
|
1112
|
+
if matcher:
|
|
1113
|
+
motor_status_inputs: list[Input] = []
|
|
1114
|
+
addr = LcnAddr(int(matcher.group("seg_id")), int(matcher.group("mod_id")))
|
|
1115
|
+
for idx in (1, 2):
|
|
1116
|
+
motor = matcher.group(f"motor{idx}_id")
|
|
1117
|
+
position = matcher.group(f"position{idx}")
|
|
1118
|
+
limit = matcher.group(f"limit{idx}")
|
|
1119
|
+
time_down = matcher.group(f"time_down{idx}")
|
|
1120
|
+
time_up = matcher.group(f"time_up{idx}")
|
|
1121
|
+
|
|
1122
|
+
motor_status_inputs.append(
|
|
1123
|
+
ModStatusMotorPositionBS4(
|
|
1124
|
+
addr,
|
|
1125
|
+
int(motor) - 1,
|
|
1126
|
+
(200 - int(position)) / 2,
|
|
1127
|
+
None if limit == "?" else (200 - int(limit)) / 2,
|
|
1128
|
+
None if time_down == "?" else int(time_down),
|
|
1129
|
+
None if time_up == "?" else int(time_up),
|
|
1130
|
+
)
|
|
1131
|
+
)
|
|
1132
|
+
return motor_status_inputs
|
|
1133
|
+
|
|
1134
|
+
return None
|
|
1135
|
+
|
|
1136
|
+
|
|
1137
|
+
class ModStatusMotorPositionModule(ModInput):
|
|
1138
|
+
"""Status of motor positions received from an LCN module.
|
|
1139
|
+
|
|
1140
|
+
Position is in percent. 0%: cover closed, 100%: cover open.
|
|
1141
|
+
"""
|
|
1142
|
+
|
|
1143
|
+
def __init__(
|
|
1144
|
+
self,
|
|
1145
|
+
physical_source_addr: LcnAddr,
|
|
1146
|
+
motor: int,
|
|
1147
|
+
position: float,
|
|
1148
|
+
):
|
|
1149
|
+
"""Construct ModInput object."""
|
|
1150
|
+
super().__init__(physical_source_addr)
|
|
1151
|
+
self.motor = motor
|
|
1152
|
+
self.position = position
|
|
1153
|
+
|
|
1154
|
+
@staticmethod
|
|
1155
|
+
def try_parse(data: str) -> list[Input] | None:
|
|
1156
|
+
"""Try to parse the given input text.
|
|
1157
|
+
|
|
1158
|
+
Will return a list of parsed Inputs. The list might be empty (but not
|
|
1159
|
+
null).
|
|
1160
|
+
|
|
1161
|
+
:param data str: The input data received from LCN-PCHK
|
|
1162
|
+
|
|
1163
|
+
:return: The parsed Inputs (never null)
|
|
1164
|
+
:rtype: List with instances of :class:`~pypck.input.Input`
|
|
1165
|
+
"""
|
|
1166
|
+
matcher = PckParser.PATTERN_STATUS_MOTOR_POSITION_MODULE.match(data)
|
|
1167
|
+
if matcher:
|
|
1168
|
+
addr = LcnAddr(int(matcher.group("seg_id")), int(matcher.group("mod_id")))
|
|
1169
|
+
motor = matcher.group("motor_id")
|
|
1170
|
+
position = matcher.group("position")
|
|
1171
|
+
|
|
1172
|
+
return [
|
|
1173
|
+
ModStatusMotorPositionModule(
|
|
1174
|
+
addr,
|
|
1175
|
+
int(motor) - 1,
|
|
1176
|
+
float(position),
|
|
1177
|
+
)
|
|
1178
|
+
]
|
|
1179
|
+
|
|
1180
|
+
return None
|
|
1181
|
+
|
|
1182
|
+
|
|
1034
1183
|
class ModSendCommandHost(ModInput):
|
|
1035
1184
|
"""Send command to host message from module."""
|
|
1036
1185
|
|
|
@@ -1200,6 +1349,8 @@ class InputParser:
|
|
|
1200
1349
|
ModStatusKeyLocks,
|
|
1201
1350
|
ModStatusAccessControl,
|
|
1202
1351
|
ModStatusSceneOutputs,
|
|
1352
|
+
ModStatusMotorPositionBS4,
|
|
1353
|
+
ModStatusMotorPositionModule,
|
|
1203
1354
|
ModSendCommandHost,
|
|
1204
1355
|
ModSendKeysHost,
|
|
1205
1356
|
Unknown,
|
|
@@ -228,7 +228,7 @@ def ramp_value_to_time(ramp_value: int) -> int:
|
|
|
228
228
|
:rtype: int
|
|
229
229
|
"""
|
|
230
230
|
if not 0 <= ramp_value <= 250:
|
|
231
|
-
raise ValueError("Ramp value has to be in range 0..250
|
|
231
|
+
raise ValueError("Ramp value has to be in range 0..250")
|
|
232
232
|
|
|
233
233
|
if ramp_value < 10:
|
|
234
234
|
times = [0, 250, 500, 660, 1000, 1400, 2000, 3000, 4000, 5000]
|
|
@@ -250,7 +250,7 @@ def time_to_native_value(time_msec: int) -> int:
|
|
|
250
250
|
:rtype: int
|
|
251
251
|
"""
|
|
252
252
|
if not 0 <= time_msec <= 240960:
|
|
253
|
-
raise ValueError("Time has to be in range 0..240960ms
|
|
253
|
+
raise ValueError("Time has to be in range 0..240960ms")
|
|
254
254
|
time_scaled = time_msec / (1000 * 0.03 * 32.0) + 1.0
|
|
255
255
|
|
|
256
256
|
pre_decimal = int(time_scaled).bit_length() - 1
|
|
@@ -264,7 +264,6 @@ def native_value_to_time(value: int) -> int:
|
|
|
264
264
|
"""Convert native LCN value to time.
|
|
265
265
|
|
|
266
266
|
Scales the given byte value (0..255) to a time value in milliseconds.
|
|
267
|
-
Inverse to time_to_native_value().
|
|
268
267
|
|
|
269
268
|
:param int value: Duration of timer in native LCN units
|
|
270
269
|
|
|
@@ -272,7 +271,7 @@ def native_value_to_time(value: int) -> int:
|
|
|
272
271
|
:rtype: int
|
|
273
272
|
"""
|
|
274
273
|
if not 0 <= value <= 255:
|
|
275
|
-
raise ValueError("Value has to be in range 0..255
|
|
274
|
+
raise ValueError("Value has to be in range 0..255")
|
|
276
275
|
pre_decimal = value // 32
|
|
277
276
|
decimal = value / 32 - pre_decimal
|
|
278
277
|
|
|
@@ -282,6 +281,39 @@ def native_value_to_time(value: int) -> int:
|
|
|
282
281
|
return int(time_msec)
|
|
283
282
|
|
|
284
283
|
|
|
284
|
+
def motor_position_time_to_native_value(time_msec: int) -> int:
|
|
285
|
+
"""Convert time to native LCN time value.
|
|
286
|
+
|
|
287
|
+
Scales the given time value in milliseconds to a two-byte value.
|
|
288
|
+
|
|
289
|
+
:param int time_msec: Duration of timer in milliseconds (1001..65535000)
|
|
290
|
+
|
|
291
|
+
:returns: The duration in native LCN units
|
|
292
|
+
:rtype: int
|
|
293
|
+
"""
|
|
294
|
+
if not 1001 <= time_msec <= 65535000:
|
|
295
|
+
raise ValueError("Time has to be in range 1001..65535000ms")
|
|
296
|
+
value = 0xFFFF * 1000 / time_msec
|
|
297
|
+
return int(value)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def native_value_to_motor_position_time(value: int) -> int:
|
|
301
|
+
"""Convert native LCN value to time.
|
|
302
|
+
|
|
303
|
+
Scales the given two-byte value (1..65535) to a time value in milliseconds.
|
|
304
|
+
|
|
305
|
+
:param int value: Duration of timer in native LCN units
|
|
306
|
+
|
|
307
|
+
:returns: The duration in milliseconds
|
|
308
|
+
:rtype: int
|
|
309
|
+
"""
|
|
310
|
+
if not 1 <= value <= 0xFFFF:
|
|
311
|
+
raise ValueError("Value has to be in range 1..65535")
|
|
312
|
+
|
|
313
|
+
time_msec = 0xFFFF * 1000 / value
|
|
314
|
+
return int(time_msec)
|
|
315
|
+
|
|
316
|
+
|
|
285
317
|
class Var(Enum):
|
|
286
318
|
"""LCN variable types."""
|
|
287
319
|
|
|
@@ -1242,9 +1274,18 @@ class MotorReverseTime(Enum):
|
|
|
1242
1274
|
For modules with FW<190C the release time has to be specified.
|
|
1243
1275
|
"""
|
|
1244
1276
|
|
|
1245
|
-
RT70 =
|
|
1246
|
-
RT600 =
|
|
1247
|
-
RT1200 =
|
|
1277
|
+
RT70 = "RT70" # 70ms
|
|
1278
|
+
RT600 = "RT600" # 600ms
|
|
1279
|
+
RT1200 = "RT1200" # 1200ms
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
class MotorPositioningMode(Enum):
|
|
1283
|
+
"""Motor positioning mode used in LCN commands."""
|
|
1284
|
+
|
|
1285
|
+
NONE = "NONE"
|
|
1286
|
+
BS4 = "BS4"
|
|
1287
|
+
MODULE = "MODULE"
|
|
1288
|
+
# EMULATED = "EMULATED"
|
|
1248
1289
|
|
|
1249
1290
|
|
|
1250
1291
|
class RelVarRef(Enum):
|
|
@@ -232,22 +232,46 @@ class AbstractConnection:
|
|
|
232
232
|
self.wants_ack, PckGenerator.control_relays_timer(time_msec, states)
|
|
233
233
|
)
|
|
234
234
|
|
|
235
|
-
async def
|
|
236
|
-
self,
|
|
235
|
+
async def control_motor_relays(
|
|
236
|
+
self,
|
|
237
|
+
motor_id: int,
|
|
238
|
+
state: lcn_defs.MotorStateModifier,
|
|
239
|
+
mode: lcn_defs.MotorPositioningMode = lcn_defs.MotorPositioningMode.NONE,
|
|
237
240
|
) -> bool:
|
|
238
241
|
"""Send a command to control motors via relays.
|
|
239
242
|
|
|
240
|
-
:param
|
|
241
|
-
:
|
|
243
|
+
:param int motor_id: The motor id 0..3
|
|
244
|
+
:param MotorStateModifier state: The modifier for the
|
|
245
|
+
:param MotorPositioningMode mode: The motor positioning mode (ooptional)
|
|
246
|
+
|
|
247
|
+
:returns: True if command was sent successfully, False otherwise
|
|
248
|
+
:rtype: bool
|
|
249
|
+
"""
|
|
250
|
+
return await self.send_command(
|
|
251
|
+
self.wants_ack, PckGenerator.control_motor_relays(motor_id, state, mode)
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
async def control_motor_relays_position(
|
|
255
|
+
self,
|
|
256
|
+
motor_id: int,
|
|
257
|
+
position: float,
|
|
258
|
+
mode: lcn_defs.MotorPositioningMode,
|
|
259
|
+
) -> bool:
|
|
260
|
+
"""Control motor position via relays and BS4.
|
|
261
|
+
|
|
262
|
+
:param int motor_id: The motor port of the LCN module
|
|
263
|
+
:param float position: The position to set in percentage (0..100)
|
|
264
|
+
:param MotorPositioningMode mode: The motor positioning mode
|
|
242
265
|
|
|
243
266
|
:returns: True if command was sent successfully, False otherwise
|
|
244
267
|
:rtype: bool
|
|
245
268
|
"""
|
|
246
269
|
return await self.send_command(
|
|
247
|
-
self.wants_ack,
|
|
270
|
+
self.wants_ack,
|
|
271
|
+
PckGenerator.control_motor_relays_position(motor_id, position, mode),
|
|
248
272
|
)
|
|
249
273
|
|
|
250
|
-
async def
|
|
274
|
+
async def control_motor_outputs(
|
|
251
275
|
self,
|
|
252
276
|
state: lcn_defs.MotorStateModifier,
|
|
253
277
|
reverse_time: lcn_defs.MotorReverseTime | None = None,
|
|
@@ -264,7 +288,7 @@ class AbstractConnection:
|
|
|
264
288
|
"""
|
|
265
289
|
return await self.send_command(
|
|
266
290
|
self.wants_ack,
|
|
267
|
-
PckGenerator.
|
|
291
|
+
PckGenerator.control_motor_outputs(state, reverse_time),
|
|
268
292
|
)
|
|
269
293
|
|
|
270
294
|
async def activate_scene(
|
|
@@ -736,7 +760,7 @@ class GroupConnection(AbstractConnection):
|
|
|
736
760
|
result &= await super().var_rel(var, value, software_serial=0)
|
|
737
761
|
return result
|
|
738
762
|
|
|
739
|
-
async def activate_status_request_handler(self, item: Any) -> None:
|
|
763
|
+
async def activate_status_request_handler(self, item: Any, option: Any) -> None:
|
|
740
764
|
"""Activate a specific TimeoutRetryHandler for status requests."""
|
|
741
765
|
await self.conn.segment_scan_completed_event.wait()
|
|
742
766
|
|
|
@@ -849,9 +873,13 @@ class ModuleConnection(AbstractConnection):
|
|
|
849
873
|
"""
|
|
850
874
|
await self.acknowledges.put(code)
|
|
851
875
|
|
|
852
|
-
async def activate_status_request_handler(
|
|
876
|
+
async def activate_status_request_handler(
|
|
877
|
+
self, item: Any, option: Any = None
|
|
878
|
+
) -> None:
|
|
853
879
|
"""Activate a specific TimeoutRetryHandler for status requests."""
|
|
854
|
-
self.task_registry.create_task(
|
|
880
|
+
self.task_registry.create_task(
|
|
881
|
+
self.status_requests_handler.activate(item, option)
|
|
882
|
+
)
|
|
855
883
|
|
|
856
884
|
async def activate_status_request_handlers(self) -> None:
|
|
857
885
|
"""Activate all TimeoutRetryHandlers for status requests."""
|
|
@@ -192,6 +192,21 @@ class PckParser:
|
|
|
192
192
|
r"(?P<code1>\d{3})(?P<code2>\d{3})(?P<code3>\d{3})"
|
|
193
193
|
)
|
|
194
194
|
|
|
195
|
+
# Pattern to parse motor position BS4 status messages.
|
|
196
|
+
PATTERN_STATUS_MOTOR_POSITION_BS4 = re.compile(
|
|
197
|
+
r"=M(?P<seg_id>\d{3})(?P<mod_id>\d{3})\."
|
|
198
|
+
r"RM(?P<motor1_id>[1-4])(?P<position1>[0-9]{3})(?P<limit1>[0-9]{3}|\?)"
|
|
199
|
+
r"(?P<time_down1>[0-9]{5}|\?)(?P<time_up1>[0-9]{5}|\?)"
|
|
200
|
+
r"RM(?P<motor2_id>[1-4])(?P<position2>[0-9]{3})(?P<limit2>[0-9]{3}|\?)"
|
|
201
|
+
r"(?P<time_down2>[0-9]{5}|\?)(?P<time_up2>[0-9]{5}|\?)"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Pattern to parse motor position module status messages.
|
|
205
|
+
PATTERN_STATUS_MOTOR_POSITION_MODULE = re.compile(
|
|
206
|
+
r":M(?P<seg_id>\d{3})(?P<mod_id>\d{3})"
|
|
207
|
+
r"P(?P<motor_id>[1-4])(?P<position>[0-9]{3})"
|
|
208
|
+
)
|
|
209
|
+
|
|
195
210
|
@staticmethod
|
|
196
211
|
def get_boolean_value(input_byte: int) -> list[bool]:
|
|
197
212
|
"""Get boolean representation for the given byte.
|
|
@@ -536,44 +551,117 @@ class PckGenerator:
|
|
|
536
551
|
return ret
|
|
537
552
|
|
|
538
553
|
@staticmethod
|
|
539
|
-
def
|
|
554
|
+
def control_motor_relays(
|
|
555
|
+
motor_id: int,
|
|
556
|
+
state: lcn_defs.MotorStateModifier,
|
|
557
|
+
mode: lcn_defs.MotorPositioningMode = lcn_defs.MotorPositioningMode.NONE,
|
|
558
|
+
) -> str:
|
|
540
559
|
"""Generate a command to control motors via relays.
|
|
541
560
|
|
|
542
|
-
:param
|
|
543
|
-
|
|
561
|
+
:param int motor_id: The motor id 0..3
|
|
562
|
+
:param MotorStateModifier state: The modifier for the
|
|
563
|
+
motor state
|
|
544
564
|
:return: The PCK command (without address header) as text
|
|
545
565
|
:rtype: str
|
|
546
566
|
"""
|
|
547
|
-
if
|
|
548
|
-
raise ValueError("Invalid
|
|
549
|
-
ret = "R8"
|
|
550
|
-
for state in states:
|
|
551
|
-
if state == lcn_defs.MotorStateModifier.UP:
|
|
552
|
-
ret += lcn_defs.RelayStateModifier.ON.value
|
|
553
|
-
ret += lcn_defs.RelayStateModifier.OFF.value
|
|
554
|
-
elif state == lcn_defs.MotorStateModifier.DOWN:
|
|
555
|
-
ret += lcn_defs.RelayStateModifier.ON.value
|
|
556
|
-
ret += lcn_defs.RelayStateModifier.ON.value
|
|
557
|
-
elif state == lcn_defs.MotorStateModifier.STOP:
|
|
558
|
-
ret += lcn_defs.RelayStateModifier.OFF.value
|
|
559
|
-
ret += lcn_defs.RelayStateModifier.NOCHANGE.value
|
|
560
|
-
elif state == lcn_defs.MotorStateModifier.TOGGLEONOFF:
|
|
561
|
-
ret += lcn_defs.RelayStateModifier.TOGGLE.value
|
|
562
|
-
ret += lcn_defs.RelayStateModifier.NOCHANGE.value
|
|
563
|
-
elif state == lcn_defs.MotorStateModifier.TOGGLEDIR:
|
|
564
|
-
ret += lcn_defs.RelayStateModifier.NOCHANGE.value
|
|
565
|
-
ret += lcn_defs.RelayStateModifier.TOGGLE.value
|
|
566
|
-
elif state == lcn_defs.MotorStateModifier.CYCLE:
|
|
567
|
-
ret += lcn_defs.RelayStateModifier.TOGGLE.value
|
|
568
|
-
ret += lcn_defs.RelayStateModifier.TOGGLE.value
|
|
569
|
-
elif state == lcn_defs.MotorStateModifier.NOCHANGE:
|
|
570
|
-
ret += lcn_defs.RelayStateModifier.NOCHANGE.value
|
|
571
|
-
ret += lcn_defs.RelayStateModifier.NOCHANGE.value
|
|
567
|
+
if 0 > motor_id > 3:
|
|
568
|
+
raise ValueError("Invalid motor id")
|
|
572
569
|
|
|
573
|
-
|
|
570
|
+
if mode not in lcn_defs.MotorPositioningMode:
|
|
571
|
+
raise ValueError("Wrong motor position mode")
|
|
572
|
+
|
|
573
|
+
if mode == lcn_defs.MotorPositioningMode.BS4:
|
|
574
|
+
if state not in [
|
|
575
|
+
lcn_defs.MotorStateModifier.UP,
|
|
576
|
+
lcn_defs.MotorStateModifier.DOWN,
|
|
577
|
+
]:
|
|
578
|
+
raise ValueError("Invalid motor state for BS4 mode")
|
|
579
|
+
|
|
580
|
+
new_motor_id = [1, 2, 5, 6][motor_id]
|
|
581
|
+
# AU=window open / cover down
|
|
582
|
+
# ZU=window close / cover up
|
|
583
|
+
action = "AU" if state == lcn_defs.MotorStateModifier.DOWN else "ZU"
|
|
584
|
+
return f"R8M{new_motor_id}{action}"
|
|
585
|
+
|
|
586
|
+
# lcn_defs.MotorPositioningMode.NONE
|
|
587
|
+
# lcn_defs.MotorPositioningMode.MODULE
|
|
588
|
+
if state == lcn_defs.MotorStateModifier.UP:
|
|
589
|
+
port_onoff = lcn_defs.RelayStateModifier.ON
|
|
590
|
+
port_updown = lcn_defs.RelayStateModifier.OFF
|
|
591
|
+
elif state == lcn_defs.MotorStateModifier.DOWN:
|
|
592
|
+
port_onoff = lcn_defs.RelayStateModifier.ON
|
|
593
|
+
port_updown = lcn_defs.RelayStateModifier.ON
|
|
594
|
+
elif state == lcn_defs.MotorStateModifier.STOP:
|
|
595
|
+
port_onoff = lcn_defs.RelayStateModifier.OFF
|
|
596
|
+
port_updown = lcn_defs.RelayStateModifier.NOCHANGE
|
|
597
|
+
elif state == lcn_defs.MotorStateModifier.TOGGLEONOFF:
|
|
598
|
+
port_onoff = lcn_defs.RelayStateModifier.TOGGLE
|
|
599
|
+
port_updown = lcn_defs.RelayStateModifier.NOCHANGE
|
|
600
|
+
elif state == lcn_defs.MotorStateModifier.TOGGLEDIR:
|
|
601
|
+
port_onoff = lcn_defs.RelayStateModifier.NOCHANGE
|
|
602
|
+
port_updown = lcn_defs.RelayStateModifier.TOGGLE
|
|
603
|
+
elif state == lcn_defs.MotorStateModifier.CYCLE:
|
|
604
|
+
port_onoff = lcn_defs.RelayStateModifier.TOGGLE
|
|
605
|
+
port_updown = lcn_defs.RelayStateModifier.TOGGLE
|
|
606
|
+
elif state == lcn_defs.MotorStateModifier.NOCHANGE:
|
|
607
|
+
port_onoff = lcn_defs.RelayStateModifier.NOCHANGE
|
|
608
|
+
port_updown = lcn_defs.RelayStateModifier.NOCHANGE
|
|
609
|
+
else:
|
|
610
|
+
raise ValueError("Invalid motor state")
|
|
611
|
+
|
|
612
|
+
states = [lcn_defs.RelayStateModifier.NOCHANGE] * 8
|
|
613
|
+
states[motor_id * 2] = port_onoff
|
|
614
|
+
states[motor_id * 2 + 1] = port_updown
|
|
615
|
+
return "R8" + "".join([state.value for state in states])
|
|
574
616
|
|
|
575
617
|
@staticmethod
|
|
576
|
-
def
|
|
618
|
+
def control_motor_relays_position(
|
|
619
|
+
motor_id: int, position: float, mode: lcn_defs.MotorPositioningMode
|
|
620
|
+
) -> str:
|
|
621
|
+
"""Control motor position via relays and BS4 or module.
|
|
622
|
+
|
|
623
|
+
:param int motor_id: The motor port of the LCN module
|
|
624
|
+
:param float position: The position to set in percentage (0..100)
|
|
625
|
+
(0: closed cover, 100: open cover)
|
|
626
|
+
:param MotorPositioningMode mode: The motor positioning mode
|
|
627
|
+
|
|
628
|
+
:return: The PCK command (without address header) as text
|
|
629
|
+
:rtype: str
|
|
630
|
+
"""
|
|
631
|
+
if mode not in (
|
|
632
|
+
lcn_defs.MotorPositioningMode.BS4,
|
|
633
|
+
lcn_defs.MotorPositioningMode.MODULE,
|
|
634
|
+
):
|
|
635
|
+
raise ValueError("Wrong motor positioning mode")
|
|
636
|
+
|
|
637
|
+
if 0 > motor_id > 3:
|
|
638
|
+
raise ValueError("Invalid motor")
|
|
639
|
+
|
|
640
|
+
if mode == lcn_defs.MotorPositioningMode.BS4:
|
|
641
|
+
new_motor_id = [1, 2, 5, 6][motor_id]
|
|
642
|
+
action = f"GP{int(200 - 2 * position):03d}"
|
|
643
|
+
return f"R8M{new_motor_id}{action}"
|
|
644
|
+
elif mode == lcn_defs.MotorPositioningMode.MODULE:
|
|
645
|
+
new_motor_id = 1 << motor_id
|
|
646
|
+
return f"JH{position:03d}{new_motor_id:03d}"
|
|
647
|
+
|
|
648
|
+
return ""
|
|
649
|
+
|
|
650
|
+
@staticmethod
|
|
651
|
+
def request_motor_position_status(motor_pair: int) -> str:
|
|
652
|
+
"""Generate a motor position status request for BS4.
|
|
653
|
+
|
|
654
|
+
:param int motor_pair: Motor pair 0: 1, 2; 1: 3, 4
|
|
655
|
+
|
|
656
|
+
:return: The PCK command (without address header) as text
|
|
657
|
+
:rtype: str
|
|
658
|
+
"""
|
|
659
|
+
if motor_pair not in [0, 1]:
|
|
660
|
+
raise ValueError("Invalid motor_pair.")
|
|
661
|
+
return f"R8M{7 if motor_pair else 3}P{motor_pair + 1}"
|
|
662
|
+
|
|
663
|
+
@staticmethod
|
|
664
|
+
def control_motor_outputs(
|
|
577
665
|
state: lcn_defs.MotorStateModifier,
|
|
578
666
|
reverse_time: lcn_defs.MotorReverseTime | None = None,
|
|
579
667
|
) -> str:
|
|
@@ -728,16 +816,11 @@ class PckGenerator:
|
|
|
728
816
|
if var_id == 0:
|
|
729
817
|
# Old command for variable 1 / T-var (compatible with all
|
|
730
818
|
# modules)
|
|
731
|
-
pck = "Z
|
|
819
|
+
pck = f"Z{'A' if value >= 0 else 'S'}{abs(value)}"
|
|
732
820
|
else:
|
|
733
821
|
# New command for variable 1-12 (compatible with all modules,
|
|
734
822
|
# since LCN-PCHK 2.8)
|
|
735
|
-
pck = (
|
|
736
|
-
"Z"
|
|
737
|
-
f"{'+' if value >= 0 else '-'}"
|
|
738
|
-
f"{var_id + 1:03d}"
|
|
739
|
-
f"{abs(value)}"
|
|
740
|
-
)
|
|
823
|
+
pck = f"Z{'+' if value >= 0 else '-'}{var_id + 1:03d}{abs(value)}"
|
|
741
824
|
return pck
|
|
742
825
|
|
|
743
826
|
set_point_id = lcn_defs.Var.to_set_point_id(var)
|
|
@@ -1051,7 +1134,7 @@ class PckGenerator:
|
|
|
1051
1134
|
raise ValueError("Wrong target_value.")
|
|
1052
1135
|
if (target_value != -1) and (software_serial >= 0x120301) and state:
|
|
1053
1136
|
reg_byte = reg_id * 0x40 + 0x07
|
|
1054
|
-
return f"X2{0x1E:03d}{reg_byte:03d}{int(2*target_value):03d}"
|
|
1137
|
+
return f"X2{0x1E:03d}{reg_byte:03d}{int(2 * target_value):03d}"
|
|
1055
1138
|
return f"RE{'A' if reg_id == 0 else 'B'}X{'S' if state else 'A'}"
|
|
1056
1139
|
|
|
1057
1140
|
@staticmethod
|
|
@@ -466,6 +466,17 @@ class StatusRequestsHandler:
|
|
|
466
466
|
self.request_status_relays_timeout
|
|
467
467
|
)
|
|
468
468
|
|
|
469
|
+
# Motor positions request status (1, 2 and 3, 4)
|
|
470
|
+
self.request_status_motor_positions = []
|
|
471
|
+
for motor_pair in range(2):
|
|
472
|
+
trh = TimeoutRetryHandler(
|
|
473
|
+
self.task_registry, -1, self.settings["MAX_STATUS_POLLED_VALUEAGE"]
|
|
474
|
+
)
|
|
475
|
+
trh.set_timeout_callback(
|
|
476
|
+
self.request_status_motor_positions_timeout, motor_pair
|
|
477
|
+
)
|
|
478
|
+
self.request_status_motor_positions.append(trh)
|
|
479
|
+
|
|
469
480
|
# Binary-sensors request status (all 8)
|
|
470
481
|
self.request_status_bin_sensors = TimeoutRetryHandler(
|
|
471
482
|
self.task_registry, -1, self.settings["MAX_STATUS_EVENTBASED_VALUEAGE"]
|
|
@@ -542,6 +553,15 @@ class StatusRequestsHandler:
|
|
|
542
553
|
False, PckGenerator.request_relays_status()
|
|
543
554
|
)
|
|
544
555
|
|
|
556
|
+
async def request_status_motor_positions_timeout(
|
|
557
|
+
self, failed: bool = False, motor_pair: int = 0
|
|
558
|
+
) -> None:
|
|
559
|
+
"""Is called on motor position status request timeout."""
|
|
560
|
+
if not failed:
|
|
561
|
+
await self.addr_conn.send_command(
|
|
562
|
+
False, PckGenerator.request_motor_position_status(motor_pair)
|
|
563
|
+
)
|
|
564
|
+
|
|
545
565
|
async def request_status_bin_sensors_timeout(self, failed: bool = False) -> None:
|
|
546
566
|
"""Is called on binary sensor status request timeout."""
|
|
547
567
|
if not failed:
|
|
@@ -589,7 +609,7 @@ class StatusRequestsHandler:
|
|
|
589
609
|
False, PckGenerator.request_key_lock_status()
|
|
590
610
|
)
|
|
591
611
|
|
|
592
|
-
async def activate(self, item: Any) -> None:
|
|
612
|
+
async def activate(self, item: Any, option: Any = None) -> None:
|
|
593
613
|
"""Activate status requests for given item."""
|
|
594
614
|
await self.addr_conn.conn.segment_scan_completed_event.wait()
|
|
595
615
|
# handle variables independently
|
|
@@ -608,6 +628,8 @@ class StatusRequestsHandler:
|
|
|
608
628
|
self.request_status_relays.activate()
|
|
609
629
|
elif item in lcn_defs.MotorPort:
|
|
610
630
|
self.request_status_relays.activate()
|
|
631
|
+
if option == lcn_defs.MotorPositioningMode.BS4:
|
|
632
|
+
self.request_status_motor_positions[item.value // 2].activate()
|
|
611
633
|
elif item in lcn_defs.BinSensorPort:
|
|
612
634
|
self.request_status_bin_sensors.activate()
|
|
613
635
|
elif item in lcn_defs.LedPort:
|
|
@@ -627,6 +649,7 @@ class StatusRequestsHandler:
|
|
|
627
649
|
await self.request_status_relays.cancel()
|
|
628
650
|
elif item in lcn_defs.MotorPort:
|
|
629
651
|
await self.request_status_relays.cancel()
|
|
652
|
+
await self.request_status_motor_positions[item.value // 2].cancel()
|
|
630
653
|
elif item in lcn_defs.BinSensorPort:
|
|
631
654
|
await self.request_status_bin_sensors.cancel()
|
|
632
655
|
elif item in lcn_defs.LedPort:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pypck
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.6
|
|
4
4
|
Summary: LCN-PCK library
|
|
5
5
|
Home-page: https://github.com/alengwenus/pypck
|
|
6
6
|
Author-email: Andre Lengwenus <alengwenus@gmail.com>
|
|
@@ -18,6 +18,7 @@ Classifier: Topic :: Home Automation
|
|
|
18
18
|
Requires-Python: >=3.11
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
|
+
Dynamic: license-file
|
|
21
22
|
|
|
22
23
|
# pypck - Asynchronous LCN-PCK library written in Python
|
|
23
24
|
|
|
@@ -7,6 +7,7 @@ from pypck.lcn_defs import (
|
|
|
7
7
|
BeepSound,
|
|
8
8
|
KeyLockStateModifier,
|
|
9
9
|
LedStatus,
|
|
10
|
+
MotorPositioningMode,
|
|
10
11
|
MotorReverseTime,
|
|
11
12
|
MotorStateModifier,
|
|
12
13
|
OutputPort,
|
|
@@ -71,9 +72,9 @@ COMMANDS = {
|
|
|
71
72
|
# General status commands
|
|
72
73
|
"SK": (PckGenerator.segment_coupler_scan,),
|
|
73
74
|
"SN": (PckGenerator.request_serial,),
|
|
74
|
-
**{f"NMN{block+1}": (PckGenerator.request_name, block) for block in range(2)},
|
|
75
|
-
**{f"NMK{block+1}": (PckGenerator.request_comment, block) for block in range(3)},
|
|
76
|
-
**{f"NMO{block+1}": (PckGenerator.request_oem_text, block) for block in range(4)},
|
|
75
|
+
**{f"NMN{block + 1}": (PckGenerator.request_name, block) for block in range(2)},
|
|
76
|
+
**{f"NMK{block + 1}": (PckGenerator.request_comment, block) for block in range(3)},
|
|
77
|
+
**{f"NMO{block + 1}": (PckGenerator.request_oem_text, block) for block in range(4)},
|
|
77
78
|
"GP": (PckGenerator.request_group_membership_static,),
|
|
78
79
|
"GD": (PckGenerator.request_group_membership_dynamic,),
|
|
79
80
|
# Output, relay, binsensors, ... status commands
|
|
@@ -87,7 +88,7 @@ COMMANDS = {
|
|
|
87
88
|
"STX": (PckGenerator.request_key_lock_status,),
|
|
88
89
|
# Variable status (new commands)
|
|
89
90
|
**{
|
|
90
|
-
f"MWT{Var.to_var_id(var)+1:03d}": (
|
|
91
|
+
f"MWT{Var.to_var_id(var) + 1:03d}": (
|
|
91
92
|
PckGenerator.request_var_status,
|
|
92
93
|
var,
|
|
93
94
|
NEW_VAR_SW_AGE,
|
|
@@ -95,7 +96,7 @@ COMMANDS = {
|
|
|
95
96
|
for var in Var.variables # type: ignore
|
|
96
97
|
},
|
|
97
98
|
**{
|
|
98
|
-
f"MWS{Var.to_set_point_id(var)+1:03d}": (
|
|
99
|
+
f"MWS{Var.to_set_point_id(var) + 1:03d}": (
|
|
99
100
|
PckGenerator.request_var_status,
|
|
100
101
|
var,
|
|
101
102
|
NEW_VAR_SW_AGE,
|
|
@@ -103,7 +104,7 @@ COMMANDS = {
|
|
|
103
104
|
for var in Var.set_points # type: ignore
|
|
104
105
|
},
|
|
105
106
|
**{
|
|
106
|
-
f"MWC{Var.to_s0_id(var)+1:03d}": (
|
|
107
|
+
f"MWC{Var.to_s0_id(var) + 1:03d}": (
|
|
107
108
|
PckGenerator.request_var_status,
|
|
108
109
|
var,
|
|
109
110
|
NEW_VAR_SW_AGE,
|
|
@@ -111,7 +112,7 @@ COMMANDS = {
|
|
|
111
112
|
for var in Var.s0s # type: ignore
|
|
112
113
|
},
|
|
113
114
|
**{
|
|
114
|
-
f"SE{Var.to_thrs_register_id(var)+1:03d}": (
|
|
115
|
+
f"SE{Var.to_thrs_register_id(var) + 1:03d}": (
|
|
115
116
|
PckGenerator.request_var_status,
|
|
116
117
|
var,
|
|
117
118
|
NEW_VAR_SW_AGE,
|
|
@@ -131,11 +132,11 @@ COMMANDS = {
|
|
|
131
132
|
},
|
|
132
133
|
# Output manipulation
|
|
133
134
|
**{
|
|
134
|
-
f"A{output+1:d}DI050123": (PckGenerator.dim_output, output, 50.0, 123)
|
|
135
|
+
f"A{output + 1:d}DI050123": (PckGenerator.dim_output, output, 50.0, 123)
|
|
135
136
|
for output in range(4)
|
|
136
137
|
},
|
|
137
138
|
**{
|
|
138
|
-
f"O{output+1:d}DI101123": (PckGenerator.dim_output, output, 50.5, 123)
|
|
139
|
+
f"O{output + 1:d}DI101123": (PckGenerator.dim_output, output, 50.5, 123)
|
|
139
140
|
for output in range(4)
|
|
140
141
|
},
|
|
141
142
|
"OY100100100100123": (PckGenerator.dim_all_outputs, 50.0, 123, 0x180501),
|
|
@@ -145,23 +146,23 @@ COMMANDS = {
|
|
|
145
146
|
"AE123": (PckGenerator.dim_all_outputs, 100.0, 123, 0x180500),
|
|
146
147
|
"AH050": (PckGenerator.dim_all_outputs, 50.0, 123, 0x180500),
|
|
147
148
|
**{
|
|
148
|
-
f"A{output+1:d}AD050": (PckGenerator.rel_output, output, 50.0)
|
|
149
|
+
f"A{output + 1:d}AD050": (PckGenerator.rel_output, output, 50.0)
|
|
149
150
|
for output in range(4)
|
|
150
151
|
},
|
|
151
152
|
**{
|
|
152
|
-
f"A{output+1:d}SB050": (PckGenerator.rel_output, output, -50.0)
|
|
153
|
+
f"A{output + 1:d}SB050": (PckGenerator.rel_output, output, -50.0)
|
|
153
154
|
for output in range(4)
|
|
154
155
|
},
|
|
155
156
|
**{
|
|
156
|
-
f"O{output+1:d}AD101": (PckGenerator.rel_output, output, 50.5)
|
|
157
|
+
f"O{output + 1:d}AD101": (PckGenerator.rel_output, output, 50.5)
|
|
157
158
|
for output in range(4)
|
|
158
159
|
},
|
|
159
160
|
**{
|
|
160
|
-
f"O{output+1:d}SB101": (PckGenerator.rel_output, output, -50.5)
|
|
161
|
+
f"O{output + 1:d}SB101": (PckGenerator.rel_output, output, -50.5)
|
|
161
162
|
for output in range(4)
|
|
162
163
|
},
|
|
163
164
|
**{
|
|
164
|
-
f"A{output+1:d}TA123": (PckGenerator.toggle_output, output, 123)
|
|
165
|
+
f"A{output + 1:d}TA123": (PckGenerator.toggle_output, output, 123)
|
|
165
166
|
for output in range(4)
|
|
166
167
|
},
|
|
167
168
|
"AU123": (PckGenerator.toggle_all_outputs, 123),
|
|
@@ -193,60 +194,90 @@ COMMANDS = {
|
|
|
193
194
|
RelayStateModifier.OFF,
|
|
194
195
|
],
|
|
195
196
|
),
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
MotorStateModifier.STOP,
|
|
202
|
-
MotorStateModifier.NOCHANGE,
|
|
203
|
-
],
|
|
197
|
+
# Motor state manipulation
|
|
198
|
+
"R8--10----": (
|
|
199
|
+
PckGenerator.control_motor_relays,
|
|
200
|
+
1,
|
|
201
|
+
MotorStateModifier.UP,
|
|
204
202
|
),
|
|
205
|
-
"
|
|
206
|
-
PckGenerator.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
203
|
+
"R8-----U--": (
|
|
204
|
+
PckGenerator.control_motor_relays,
|
|
205
|
+
2,
|
|
206
|
+
MotorStateModifier.TOGGLEDIR,
|
|
207
|
+
),
|
|
208
|
+
"R8UU------": (
|
|
209
|
+
PckGenerator.control_motor_relays,
|
|
210
|
+
0,
|
|
211
|
+
MotorStateModifier.CYCLE,
|
|
212
|
+
),
|
|
213
|
+
"R8M1GP200": (
|
|
214
|
+
PckGenerator.control_motor_relays_position,
|
|
215
|
+
0,
|
|
216
|
+
0.0,
|
|
217
|
+
MotorPositioningMode.BS4,
|
|
218
|
+
),
|
|
219
|
+
"R8M6GP100": (
|
|
220
|
+
PckGenerator.control_motor_relays_position,
|
|
221
|
+
3,
|
|
222
|
+
50.0,
|
|
223
|
+
MotorPositioningMode.BS4,
|
|
224
|
+
),
|
|
225
|
+
"R8M3P1": (
|
|
226
|
+
PckGenerator.request_motor_position_status,
|
|
227
|
+
0,
|
|
228
|
+
),
|
|
229
|
+
"R8M7P2": (
|
|
230
|
+
PckGenerator.request_motor_position_status,
|
|
231
|
+
1,
|
|
232
|
+
),
|
|
233
|
+
"JH050001": (
|
|
234
|
+
PckGenerator.control_motor_relays_position,
|
|
235
|
+
0,
|
|
236
|
+
50,
|
|
237
|
+
MotorPositioningMode.MODULE,
|
|
238
|
+
),
|
|
239
|
+
"JH100004": (
|
|
240
|
+
PckGenerator.control_motor_relays_position,
|
|
241
|
+
2,
|
|
242
|
+
100,
|
|
243
|
+
MotorPositioningMode.MODULE,
|
|
213
244
|
),
|
|
214
245
|
"X2001228000": (
|
|
215
|
-
PckGenerator.
|
|
246
|
+
PckGenerator.control_motor_outputs,
|
|
216
247
|
MotorStateModifier.UP,
|
|
217
248
|
MotorReverseTime.RT70,
|
|
218
249
|
),
|
|
219
250
|
"A1DI100008": (
|
|
220
|
-
PckGenerator.
|
|
251
|
+
PckGenerator.control_motor_outputs,
|
|
221
252
|
MotorStateModifier.UP,
|
|
222
253
|
MotorReverseTime.RT600,
|
|
223
254
|
),
|
|
224
255
|
"A1DI100011": (
|
|
225
|
-
PckGenerator.
|
|
256
|
+
PckGenerator.control_motor_outputs,
|
|
226
257
|
MotorStateModifier.UP,
|
|
227
258
|
MotorReverseTime.RT1200,
|
|
228
259
|
),
|
|
229
260
|
"X2001000228": (
|
|
230
|
-
PckGenerator.
|
|
261
|
+
PckGenerator.control_motor_outputs,
|
|
231
262
|
MotorStateModifier.DOWN,
|
|
232
263
|
MotorReverseTime.RT70,
|
|
233
264
|
),
|
|
234
265
|
"A2DI100008": (
|
|
235
|
-
PckGenerator.
|
|
266
|
+
PckGenerator.control_motor_outputs,
|
|
236
267
|
MotorStateModifier.DOWN,
|
|
237
268
|
MotorReverseTime.RT600,
|
|
238
269
|
),
|
|
239
270
|
"A2DI100011": (
|
|
240
|
-
PckGenerator.
|
|
271
|
+
PckGenerator.control_motor_outputs,
|
|
241
272
|
MotorStateModifier.DOWN,
|
|
242
273
|
MotorReverseTime.RT1200,
|
|
243
274
|
),
|
|
244
275
|
"AY000000": (
|
|
245
|
-
PckGenerator.
|
|
276
|
+
PckGenerator.control_motor_outputs,
|
|
246
277
|
MotorStateModifier.STOP,
|
|
247
278
|
),
|
|
248
279
|
"JE": (
|
|
249
|
-
PckGenerator.
|
|
280
|
+
PckGenerator.control_motor_outputs,
|
|
250
281
|
MotorStateModifier.CYCLE,
|
|
251
282
|
),
|
|
252
283
|
# Variable manipulation
|
|
@@ -277,7 +308,7 @@ COMMANDS = {
|
|
|
277
308
|
if var != Var.TVAR
|
|
278
309
|
},
|
|
279
310
|
**{
|
|
280
|
-
f"RE{('A','B')[nvar]}S{('A','P')[nref]}-500": (
|
|
311
|
+
f"RE{('A', 'B')[nvar]}S{('A', 'P')[nref]}-500": (
|
|
281
312
|
PckGenerator.var_rel,
|
|
282
313
|
var,
|
|
283
314
|
ref,
|
|
@@ -289,7 +320,7 @@ COMMANDS = {
|
|
|
289
320
|
for sw_age in (0x170206, 0x170205)
|
|
290
321
|
},
|
|
291
322
|
**{
|
|
292
|
-
f"RE{('A','B')[nvar]}S{('A','P')[nref]}+500": (
|
|
323
|
+
f"RE{('A', 'B')[nvar]}S{('A', 'P')[nref]}+500": (
|
|
293
324
|
PckGenerator.var_rel,
|
|
294
325
|
var,
|
|
295
326
|
ref,
|
|
@@ -301,7 +332,7 @@ COMMANDS = {
|
|
|
301
332
|
for sw_age in (0x170206, 0x170205)
|
|
302
333
|
},
|
|
303
334
|
**{
|
|
304
|
-
f"SS{('R','E')[nref]}0500SR{r+1}{i+1}": (
|
|
335
|
+
f"SS{('R', 'E')[nref]}0500SR{r + 1}{i + 1}": (
|
|
305
336
|
PckGenerator.var_rel,
|
|
306
337
|
Var.thresholds[r][i], # type: ignore
|
|
307
338
|
ref,
|
|
@@ -313,7 +344,7 @@ COMMANDS = {
|
|
|
313
344
|
for nref, ref in enumerate(RelVarRef)
|
|
314
345
|
},
|
|
315
346
|
**{
|
|
316
|
-
f"SS{('R','E')[nref]}0500AR{r+1}{i+1}": (
|
|
347
|
+
f"SS{('R', 'E')[nref]}0500AR{r + 1}{i + 1}": (
|
|
317
348
|
PckGenerator.var_rel,
|
|
318
349
|
Var.thresholds[r][i], # type: ignore
|
|
319
350
|
ref,
|
|
@@ -325,7 +356,7 @@ COMMANDS = {
|
|
|
325
356
|
for nref, ref in enumerate(RelVarRef)
|
|
326
357
|
},
|
|
327
358
|
**{
|
|
328
|
-
f"SS{('R','E')[nref]}0500S{1<<(4-i):05b}": (
|
|
359
|
+
f"SS{('R', 'E')[nref]}0500S{1 << (4 - i):05b}": (
|
|
329
360
|
PckGenerator.var_rel,
|
|
330
361
|
Var.thresholds[0][i], # type: ignore
|
|
331
362
|
ref,
|
|
@@ -336,7 +367,7 @@ COMMANDS = {
|
|
|
336
367
|
for nref, ref in enumerate(RelVarRef)
|
|
337
368
|
},
|
|
338
369
|
**{
|
|
339
|
-
f"SS{('R','E')[nref]}0500A{1<<(4-i):05b}": (
|
|
370
|
+
f"SS{('R', 'E')[nref]}0500A{1 << (4 - i):05b}": (
|
|
340
371
|
PckGenerator.var_rel,
|
|
341
372
|
Var.thresholds[0][i], # type: ignore
|
|
342
373
|
ref,
|
|
@@ -348,7 +379,7 @@ COMMANDS = {
|
|
|
348
379
|
},
|
|
349
380
|
# Led manipulation
|
|
350
381
|
**{
|
|
351
|
-
f"LA{led+1:03d}{state.value}": (PckGenerator.control_led, led, state)
|
|
382
|
+
f"LA{led + 1:03d}{state.value}": (PckGenerator.control_led, led, state)
|
|
352
383
|
for led in range(12)
|
|
353
384
|
for state in LedStatus
|
|
354
385
|
},
|
|
@@ -378,7 +409,7 @@ COMMANDS = {
|
|
|
378
409
|
if dcmd != SendKeyCommand.DONTSEND
|
|
379
410
|
},
|
|
380
411
|
**{
|
|
381
|
-
f"TV{('A','B','C','D')[table]}040{unit.value}11001110": (
|
|
412
|
+
f"TV{('A', 'B', 'C', 'D')[table]}040{unit.value}11001110": (
|
|
382
413
|
PckGenerator.send_keys_hit_deferred,
|
|
383
414
|
table,
|
|
384
415
|
40,
|
|
@@ -390,7 +421,7 @@ COMMANDS = {
|
|
|
390
421
|
},
|
|
391
422
|
# Lock keys
|
|
392
423
|
**{
|
|
393
|
-
f"TX{('A','B','C','D')[table]}10U--01U": (
|
|
424
|
+
f"TX{('A', 'B', 'C', 'D')[table]}10U--01U": (
|
|
394
425
|
PckGenerator.lock_keys,
|
|
395
426
|
table,
|
|
396
427
|
[
|
|
@@ -417,15 +448,15 @@ COMMANDS = {
|
|
|
417
448
|
},
|
|
418
449
|
# Lock regulator
|
|
419
450
|
**{
|
|
420
|
-
f"RE{('A','B')[reg]:s}XS": (PckGenerator.lock_regulator, reg, True, -1)
|
|
451
|
+
f"RE{('A', 'B')[reg]:s}XS": (PckGenerator.lock_regulator, reg, True, -1)
|
|
421
452
|
for reg in range(2)
|
|
422
453
|
},
|
|
423
454
|
**{
|
|
424
|
-
f"RE{('A','B')[reg]:s}XA": (PckGenerator.lock_regulator, reg, False, -1)
|
|
455
|
+
f"RE{('A', 'B')[reg]:s}XA": (PckGenerator.lock_regulator, reg, False, -1)
|
|
425
456
|
for reg in range(2)
|
|
426
457
|
},
|
|
427
458
|
**{
|
|
428
|
-
f"X2030{0x40*reg + 0x07:03d}{2*value:03d}": (
|
|
459
|
+
f"X2030{0x40 * reg + 0x07:03d}{2 * value:03d}": (
|
|
429
460
|
PckGenerator.lock_regulator,
|
|
430
461
|
reg,
|
|
431
462
|
True,
|
|
@@ -436,11 +467,11 @@ COMMANDS = {
|
|
|
436
467
|
for value in (0, 50, 100)
|
|
437
468
|
},
|
|
438
469
|
**{
|
|
439
|
-
f"RE{('A','B')[reg]:s}XS": (PckGenerator.lock_regulator, reg, True, 0x120301)
|
|
470
|
+
f"RE{('A', 'B')[reg]:s}XS": (PckGenerator.lock_regulator, reg, True, 0x120301)
|
|
440
471
|
for reg in range(2)
|
|
441
472
|
},
|
|
442
473
|
**{
|
|
443
|
-
f"RE{('A','B')[reg]:s}XA": (PckGenerator.lock_regulator, reg, False, 0x120301)
|
|
474
|
+
f"RE{('A', 'B')[reg]:s}XA": (PckGenerator.lock_regulator, reg, False, 0x120301)
|
|
444
475
|
for reg in range(2)
|
|
445
476
|
},
|
|
446
477
|
# scenes
|
|
@@ -490,7 +521,7 @@ COMMANDS = {
|
|
|
490
521
|
),
|
|
491
522
|
# dynamic text
|
|
492
523
|
**{
|
|
493
|
-
f"GTDT{row+1:d}{part+1:d}asdfasdfasdf".encode(): (
|
|
524
|
+
f"GTDT{row + 1:d}{part + 1:d}asdfasdfasdf".encode(): (
|
|
494
525
|
PckGenerator.dyn_text_part,
|
|
495
526
|
row,
|
|
496
527
|
part,
|
|
@@ -15,6 +15,8 @@ from pypck.inputs import (
|
|
|
15
15
|
ModStatusGroups,
|
|
16
16
|
ModStatusKeyLocks,
|
|
17
17
|
ModStatusLedsAndLogicOps,
|
|
18
|
+
ModStatusMotorPositionBS4,
|
|
19
|
+
ModStatusMotorPositionModule,
|
|
18
20
|
ModStatusOutput,
|
|
19
21
|
ModStatusOutputNative,
|
|
20
22
|
ModStatusRelays,
|
|
@@ -239,6 +241,33 @@ MESSAGES = {
|
|
|
239
241
|
[150, 100, 0, 200],
|
|
240
242
|
)
|
|
241
243
|
],
|
|
244
|
+
# Status motor position via BS4
|
|
245
|
+
"=M000010.RM1100?1234567890RM2200200??": [
|
|
246
|
+
(
|
|
247
|
+
ModStatusMotorPositionBS4,
|
|
248
|
+
0,
|
|
249
|
+
50,
|
|
250
|
+
None,
|
|
251
|
+
12345,
|
|
252
|
+
67890,
|
|
253
|
+
),
|
|
254
|
+
(
|
|
255
|
+
ModStatusMotorPositionBS4,
|
|
256
|
+
1,
|
|
257
|
+
0,
|
|
258
|
+
0,
|
|
259
|
+
None,
|
|
260
|
+
None,
|
|
261
|
+
),
|
|
262
|
+
],
|
|
263
|
+
# Status motor position via module
|
|
264
|
+
":M000010P1050": [
|
|
265
|
+
(
|
|
266
|
+
ModStatusMotorPositionModule,
|
|
267
|
+
0,
|
|
268
|
+
50,
|
|
269
|
+
)
|
|
270
|
+
],
|
|
242
271
|
# SKH
|
|
243
272
|
"+M004000010.SKH000001": [(ModSendCommandHost, (0, 1))],
|
|
244
273
|
"+M004000010.SKH000001002003004005": [
|
pypck-0.8.5/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.8.5
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|