pypck 0.8.5__tar.gz → 0.8.7__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.7}/PKG-INFO +3 -2
- pypck-0.8.7/VERSION +1 -0
- {pypck-0.8.5 → pypck-0.8.7}/pypck/inputs.py +156 -5
- {pypck-0.8.5 → pypck-0.8.7}/pypck/lcn_defs.py +48 -7
- {pypck-0.8.5 → pypck-0.8.7}/pypck/module.py +38 -10
- {pypck-0.8.5 → pypck-0.8.7}/pypck/pck_commands.py +122 -37
- {pypck-0.8.5 → pypck-0.8.7}/pypck/request_handlers.py +24 -1
- {pypck-0.8.5 → pypck-0.8.7/pypck.egg-info}/PKG-INFO +3 -2
- {pypck-0.8.5 → pypck-0.8.7}/tests/test_commands.py +102 -53
- {pypck-0.8.5 → pypck-0.8.7}/tests/test_messages.py +29 -0
- pypck-0.8.5/VERSION +0 -1
- {pypck-0.8.5 → pypck-0.8.7}/LICENSE +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/README.md +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/pypck/__init__.py +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/pypck/connection.py +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/pypck/helpers.py +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/pypck/lcn_addr.py +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/pypck/timeout_retry.py +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/pypck.egg-info/SOURCES.txt +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/pypck.egg-info/dependency_links.txt +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/pypck.egg-info/not-zip-safe +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/pypck.egg-info/top_level.txt +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/pyproject.toml +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/setup.cfg +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/tests/test_connection.py +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/tests/test_dyn_text.py +0 -0
- {pypck-0.8.5 → pypck-0.8.7}/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.7
|
|
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.7/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.8.7
|
|
@@ -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,119 @@ 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
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
567
|
+
if 0 > motor_id > 3:
|
|
568
|
+
raise ValueError("Invalid motor id")
|
|
569
|
+
|
|
570
|
+
if mode not in lcn_defs.MotorPositioningMode:
|
|
571
|
+
raise ValueError("Wrong motor position mode")
|
|
572
|
+
|
|
573
|
+
if mode == lcn_defs.MotorPositioningMode.BS4:
|
|
574
|
+
new_motor_id = [1, 2, 5, 6][motor_id]
|
|
575
|
+
if state == lcn_defs.MotorStateModifier.DOWN:
|
|
576
|
+
# AU=window open / cover down
|
|
577
|
+
action = "AU"
|
|
578
|
+
elif state == lcn_defs.MotorStateModifier.UP:
|
|
579
|
+
# ZU=window close / cover up
|
|
580
|
+
action = "ZU"
|
|
557
581
|
elif state == lcn_defs.MotorStateModifier.STOP:
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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
|
|
582
|
+
action = "ST"
|
|
583
|
+
else:
|
|
584
|
+
raise ValueError("Invalid motor state for BS4 mode")
|
|
572
585
|
|
|
573
|
-
|
|
586
|
+
return f"R8M{new_motor_id}{action}"
|
|
587
|
+
|
|
588
|
+
# lcn_defs.MotorPositioningMode.NONE
|
|
589
|
+
# lcn_defs.MotorPositioningMode.MODULE
|
|
590
|
+
if state == lcn_defs.MotorStateModifier.UP:
|
|
591
|
+
port_onoff = lcn_defs.RelayStateModifier.ON
|
|
592
|
+
port_updown = lcn_defs.RelayStateModifier.OFF
|
|
593
|
+
elif state == lcn_defs.MotorStateModifier.DOWN:
|
|
594
|
+
port_onoff = lcn_defs.RelayStateModifier.ON
|
|
595
|
+
port_updown = lcn_defs.RelayStateModifier.ON
|
|
596
|
+
elif state == lcn_defs.MotorStateModifier.STOP:
|
|
597
|
+
port_onoff = lcn_defs.RelayStateModifier.OFF
|
|
598
|
+
port_updown = lcn_defs.RelayStateModifier.NOCHANGE
|
|
599
|
+
elif state == lcn_defs.MotorStateModifier.TOGGLEONOFF:
|
|
600
|
+
port_onoff = lcn_defs.RelayStateModifier.TOGGLE
|
|
601
|
+
port_updown = lcn_defs.RelayStateModifier.NOCHANGE
|
|
602
|
+
elif state == lcn_defs.MotorStateModifier.TOGGLEDIR:
|
|
603
|
+
port_onoff = lcn_defs.RelayStateModifier.NOCHANGE
|
|
604
|
+
port_updown = lcn_defs.RelayStateModifier.TOGGLE
|
|
605
|
+
elif state == lcn_defs.MotorStateModifier.CYCLE:
|
|
606
|
+
port_onoff = lcn_defs.RelayStateModifier.TOGGLE
|
|
607
|
+
port_updown = lcn_defs.RelayStateModifier.TOGGLE
|
|
608
|
+
elif state == lcn_defs.MotorStateModifier.NOCHANGE:
|
|
609
|
+
port_onoff = lcn_defs.RelayStateModifier.NOCHANGE
|
|
610
|
+
port_updown = lcn_defs.RelayStateModifier.NOCHANGE
|
|
611
|
+
else:
|
|
612
|
+
raise ValueError("Invalid motor state")
|
|
613
|
+
|
|
614
|
+
states = [lcn_defs.RelayStateModifier.NOCHANGE] * 8
|
|
615
|
+
states[motor_id * 2] = port_onoff
|
|
616
|
+
states[motor_id * 2 + 1] = port_updown
|
|
617
|
+
return "R8" + "".join([state.value for state in states])
|
|
618
|
+
|
|
619
|
+
@staticmethod
|
|
620
|
+
def control_motor_relays_position(
|
|
621
|
+
motor_id: int, position: float, mode: lcn_defs.MotorPositioningMode
|
|
622
|
+
) -> str:
|
|
623
|
+
"""Control motor position via relays and BS4 or module.
|
|
624
|
+
|
|
625
|
+
:param int motor_id: The motor port of the LCN module
|
|
626
|
+
:param float position: The position to set in percentage (0..100)
|
|
627
|
+
(0: closed cover, 100: open cover)
|
|
628
|
+
:param MotorPositioningMode mode: The motor positioning mode
|
|
629
|
+
|
|
630
|
+
:return: The PCK command (without address header) as text
|
|
631
|
+
:rtype: str
|
|
632
|
+
"""
|
|
633
|
+
if mode not in (
|
|
634
|
+
lcn_defs.MotorPositioningMode.BS4,
|
|
635
|
+
lcn_defs.MotorPositioningMode.MODULE,
|
|
636
|
+
):
|
|
637
|
+
raise ValueError("Wrong motor positioning mode")
|
|
638
|
+
|
|
639
|
+
if 0 > motor_id > 3:
|
|
640
|
+
raise ValueError("Invalid motor")
|
|
641
|
+
|
|
642
|
+
if mode == lcn_defs.MotorPositioningMode.BS4:
|
|
643
|
+
new_motor_id = [1, 2, 5, 6][motor_id]
|
|
644
|
+
action = f"GP{int(200 - 2 * position):03d}"
|
|
645
|
+
return f"R8M{new_motor_id}{action}"
|
|
646
|
+
elif mode == lcn_defs.MotorPositioningMode.MODULE:
|
|
647
|
+
new_motor_id = 1 << motor_id
|
|
648
|
+
return f"JH{position:03d}{new_motor_id:03d}"
|
|
649
|
+
|
|
650
|
+
return ""
|
|
651
|
+
|
|
652
|
+
@staticmethod
|
|
653
|
+
def request_motor_position_status(motor_pair: int) -> str:
|
|
654
|
+
"""Generate a motor position status request for BS4.
|
|
655
|
+
|
|
656
|
+
:param int motor_pair: Motor pair 0: 1, 2; 1: 3, 4
|
|
657
|
+
|
|
658
|
+
:return: The PCK command (without address header) as text
|
|
659
|
+
:rtype: str
|
|
660
|
+
"""
|
|
661
|
+
if motor_pair not in [0, 1]:
|
|
662
|
+
raise ValueError("Invalid motor_pair.")
|
|
663
|
+
return f"R8M{7 if motor_pair else 3}P{motor_pair + 1}"
|
|
574
664
|
|
|
575
665
|
@staticmethod
|
|
576
|
-
def
|
|
666
|
+
def control_motor_outputs(
|
|
577
667
|
state: lcn_defs.MotorStateModifier,
|
|
578
668
|
reverse_time: lcn_defs.MotorReverseTime | None = None,
|
|
579
669
|
) -> str:
|
|
@@ -728,16 +818,11 @@ class PckGenerator:
|
|
|
728
818
|
if var_id == 0:
|
|
729
819
|
# Old command for variable 1 / T-var (compatible with all
|
|
730
820
|
# modules)
|
|
731
|
-
pck = "Z
|
|
821
|
+
pck = f"Z{'A' if value >= 0 else 'S'}{abs(value)}"
|
|
732
822
|
else:
|
|
733
823
|
# New command for variable 1-12 (compatible with all modules,
|
|
734
824
|
# 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
|
-
)
|
|
825
|
+
pck = f"Z{'+' if value >= 0 else '-'}{var_id + 1:03d}{abs(value)}"
|
|
741
826
|
return pck
|
|
742
827
|
|
|
743
828
|
set_point_id = lcn_defs.Var.to_set_point_id(var)
|
|
@@ -1051,7 +1136,7 @@ class PckGenerator:
|
|
|
1051
1136
|
raise ValueError("Wrong target_value.")
|
|
1052
1137
|
if (target_value != -1) and (software_serial >= 0x120301) and state:
|
|
1053
1138
|
reg_byte = reg_id * 0x40 + 0x07
|
|
1054
|
-
return f"X2{0x1E:03d}{reg_byte:03d}{int(2*target_value):03d}"
|
|
1139
|
+
return f"X2{0x1E:03d}{reg_byte:03d}{int(2 * target_value):03d}"
|
|
1055
1140
|
return f"RE{'A' if reg_id == 0 else 'B'}X{'S' if state else 'A'}"
|
|
1056
1141
|
|
|
1057
1142
|
@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.7
|
|
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,108 @@ 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
|
+
"R8M1ZU": (
|
|
214
|
+
PckGenerator.control_motor_relays,
|
|
215
|
+
0,
|
|
216
|
+
MotorStateModifier.UP,
|
|
217
|
+
MotorPositioningMode.BS4,
|
|
218
|
+
),
|
|
219
|
+
"R8M2AU": (
|
|
220
|
+
PckGenerator.control_motor_relays,
|
|
221
|
+
1,
|
|
222
|
+
MotorStateModifier.DOWN,
|
|
223
|
+
MotorPositioningMode.BS4,
|
|
224
|
+
),
|
|
225
|
+
"R8M5ST": (
|
|
226
|
+
PckGenerator.control_motor_relays,
|
|
227
|
+
2,
|
|
228
|
+
MotorStateModifier.STOP,
|
|
229
|
+
MotorPositioningMode.BS4,
|
|
230
|
+
),
|
|
231
|
+
"R8M1GP200": (
|
|
232
|
+
PckGenerator.control_motor_relays_position,
|
|
233
|
+
0,
|
|
234
|
+
0.0,
|
|
235
|
+
MotorPositioningMode.BS4,
|
|
236
|
+
),
|
|
237
|
+
"R8M6GP100": (
|
|
238
|
+
PckGenerator.control_motor_relays_position,
|
|
239
|
+
3,
|
|
240
|
+
50.0,
|
|
241
|
+
MotorPositioningMode.BS4,
|
|
242
|
+
),
|
|
243
|
+
"R8M3P1": (
|
|
244
|
+
PckGenerator.request_motor_position_status,
|
|
245
|
+
0,
|
|
246
|
+
),
|
|
247
|
+
"R8M7P2": (
|
|
248
|
+
PckGenerator.request_motor_position_status,
|
|
249
|
+
1,
|
|
250
|
+
),
|
|
251
|
+
"JH050001": (
|
|
252
|
+
PckGenerator.control_motor_relays_position,
|
|
253
|
+
0,
|
|
254
|
+
50,
|
|
255
|
+
MotorPositioningMode.MODULE,
|
|
256
|
+
),
|
|
257
|
+
"JH100004": (
|
|
258
|
+
PckGenerator.control_motor_relays_position,
|
|
259
|
+
2,
|
|
260
|
+
100,
|
|
261
|
+
MotorPositioningMode.MODULE,
|
|
213
262
|
),
|
|
214
263
|
"X2001228000": (
|
|
215
|
-
PckGenerator.
|
|
264
|
+
PckGenerator.control_motor_outputs,
|
|
216
265
|
MotorStateModifier.UP,
|
|
217
266
|
MotorReverseTime.RT70,
|
|
218
267
|
),
|
|
219
268
|
"A1DI100008": (
|
|
220
|
-
PckGenerator.
|
|
269
|
+
PckGenerator.control_motor_outputs,
|
|
221
270
|
MotorStateModifier.UP,
|
|
222
271
|
MotorReverseTime.RT600,
|
|
223
272
|
),
|
|
224
273
|
"A1DI100011": (
|
|
225
|
-
PckGenerator.
|
|
274
|
+
PckGenerator.control_motor_outputs,
|
|
226
275
|
MotorStateModifier.UP,
|
|
227
276
|
MotorReverseTime.RT1200,
|
|
228
277
|
),
|
|
229
278
|
"X2001000228": (
|
|
230
|
-
PckGenerator.
|
|
279
|
+
PckGenerator.control_motor_outputs,
|
|
231
280
|
MotorStateModifier.DOWN,
|
|
232
281
|
MotorReverseTime.RT70,
|
|
233
282
|
),
|
|
234
283
|
"A2DI100008": (
|
|
235
|
-
PckGenerator.
|
|
284
|
+
PckGenerator.control_motor_outputs,
|
|
236
285
|
MotorStateModifier.DOWN,
|
|
237
286
|
MotorReverseTime.RT600,
|
|
238
287
|
),
|
|
239
288
|
"A2DI100011": (
|
|
240
|
-
PckGenerator.
|
|
289
|
+
PckGenerator.control_motor_outputs,
|
|
241
290
|
MotorStateModifier.DOWN,
|
|
242
291
|
MotorReverseTime.RT1200,
|
|
243
292
|
),
|
|
244
293
|
"AY000000": (
|
|
245
|
-
PckGenerator.
|
|
294
|
+
PckGenerator.control_motor_outputs,
|
|
246
295
|
MotorStateModifier.STOP,
|
|
247
296
|
),
|
|
248
297
|
"JE": (
|
|
249
|
-
PckGenerator.
|
|
298
|
+
PckGenerator.control_motor_outputs,
|
|
250
299
|
MotorStateModifier.CYCLE,
|
|
251
300
|
),
|
|
252
301
|
# Variable manipulation
|
|
@@ -277,7 +326,7 @@ COMMANDS = {
|
|
|
277
326
|
if var != Var.TVAR
|
|
278
327
|
},
|
|
279
328
|
**{
|
|
280
|
-
f"RE{('A','B')[nvar]}S{('A','P')[nref]}-500": (
|
|
329
|
+
f"RE{('A', 'B')[nvar]}S{('A', 'P')[nref]}-500": (
|
|
281
330
|
PckGenerator.var_rel,
|
|
282
331
|
var,
|
|
283
332
|
ref,
|
|
@@ -289,7 +338,7 @@ COMMANDS = {
|
|
|
289
338
|
for sw_age in (0x170206, 0x170205)
|
|
290
339
|
},
|
|
291
340
|
**{
|
|
292
|
-
f"RE{('A','B')[nvar]}S{('A','P')[nref]}+500": (
|
|
341
|
+
f"RE{('A', 'B')[nvar]}S{('A', 'P')[nref]}+500": (
|
|
293
342
|
PckGenerator.var_rel,
|
|
294
343
|
var,
|
|
295
344
|
ref,
|
|
@@ -301,7 +350,7 @@ COMMANDS = {
|
|
|
301
350
|
for sw_age in (0x170206, 0x170205)
|
|
302
351
|
},
|
|
303
352
|
**{
|
|
304
|
-
f"SS{('R','E')[nref]}0500SR{r+1}{i+1}": (
|
|
353
|
+
f"SS{('R', 'E')[nref]}0500SR{r + 1}{i + 1}": (
|
|
305
354
|
PckGenerator.var_rel,
|
|
306
355
|
Var.thresholds[r][i], # type: ignore
|
|
307
356
|
ref,
|
|
@@ -313,7 +362,7 @@ COMMANDS = {
|
|
|
313
362
|
for nref, ref in enumerate(RelVarRef)
|
|
314
363
|
},
|
|
315
364
|
**{
|
|
316
|
-
f"SS{('R','E')[nref]}0500AR{r+1}{i+1}": (
|
|
365
|
+
f"SS{('R', 'E')[nref]}0500AR{r + 1}{i + 1}": (
|
|
317
366
|
PckGenerator.var_rel,
|
|
318
367
|
Var.thresholds[r][i], # type: ignore
|
|
319
368
|
ref,
|
|
@@ -325,7 +374,7 @@ COMMANDS = {
|
|
|
325
374
|
for nref, ref in enumerate(RelVarRef)
|
|
326
375
|
},
|
|
327
376
|
**{
|
|
328
|
-
f"SS{('R','E')[nref]}0500S{1<<(4-i):05b}": (
|
|
377
|
+
f"SS{('R', 'E')[nref]}0500S{1 << (4 - i):05b}": (
|
|
329
378
|
PckGenerator.var_rel,
|
|
330
379
|
Var.thresholds[0][i], # type: ignore
|
|
331
380
|
ref,
|
|
@@ -336,7 +385,7 @@ COMMANDS = {
|
|
|
336
385
|
for nref, ref in enumerate(RelVarRef)
|
|
337
386
|
},
|
|
338
387
|
**{
|
|
339
|
-
f"SS{('R','E')[nref]}0500A{1<<(4-i):05b}": (
|
|
388
|
+
f"SS{('R', 'E')[nref]}0500A{1 << (4 - i):05b}": (
|
|
340
389
|
PckGenerator.var_rel,
|
|
341
390
|
Var.thresholds[0][i], # type: ignore
|
|
342
391
|
ref,
|
|
@@ -348,7 +397,7 @@ COMMANDS = {
|
|
|
348
397
|
},
|
|
349
398
|
# Led manipulation
|
|
350
399
|
**{
|
|
351
|
-
f"LA{led+1:03d}{state.value}": (PckGenerator.control_led, led, state)
|
|
400
|
+
f"LA{led + 1:03d}{state.value}": (PckGenerator.control_led, led, state)
|
|
352
401
|
for led in range(12)
|
|
353
402
|
for state in LedStatus
|
|
354
403
|
},
|
|
@@ -378,7 +427,7 @@ COMMANDS = {
|
|
|
378
427
|
if dcmd != SendKeyCommand.DONTSEND
|
|
379
428
|
},
|
|
380
429
|
**{
|
|
381
|
-
f"TV{('A','B','C','D')[table]}040{unit.value}11001110": (
|
|
430
|
+
f"TV{('A', 'B', 'C', 'D')[table]}040{unit.value}11001110": (
|
|
382
431
|
PckGenerator.send_keys_hit_deferred,
|
|
383
432
|
table,
|
|
384
433
|
40,
|
|
@@ -390,7 +439,7 @@ COMMANDS = {
|
|
|
390
439
|
},
|
|
391
440
|
# Lock keys
|
|
392
441
|
**{
|
|
393
|
-
f"TX{('A','B','C','D')[table]}10U--01U": (
|
|
442
|
+
f"TX{('A', 'B', 'C', 'D')[table]}10U--01U": (
|
|
394
443
|
PckGenerator.lock_keys,
|
|
395
444
|
table,
|
|
396
445
|
[
|
|
@@ -417,15 +466,15 @@ COMMANDS = {
|
|
|
417
466
|
},
|
|
418
467
|
# Lock regulator
|
|
419
468
|
**{
|
|
420
|
-
f"RE{('A','B')[reg]:s}XS": (PckGenerator.lock_regulator, reg, True, -1)
|
|
469
|
+
f"RE{('A', 'B')[reg]:s}XS": (PckGenerator.lock_regulator, reg, True, -1)
|
|
421
470
|
for reg in range(2)
|
|
422
471
|
},
|
|
423
472
|
**{
|
|
424
|
-
f"RE{('A','B')[reg]:s}XA": (PckGenerator.lock_regulator, reg, False, -1)
|
|
473
|
+
f"RE{('A', 'B')[reg]:s}XA": (PckGenerator.lock_regulator, reg, False, -1)
|
|
425
474
|
for reg in range(2)
|
|
426
475
|
},
|
|
427
476
|
**{
|
|
428
|
-
f"X2030{0x40*reg + 0x07:03d}{2*value:03d}": (
|
|
477
|
+
f"X2030{0x40 * reg + 0x07:03d}{2 * value:03d}": (
|
|
429
478
|
PckGenerator.lock_regulator,
|
|
430
479
|
reg,
|
|
431
480
|
True,
|
|
@@ -436,11 +485,11 @@ COMMANDS = {
|
|
|
436
485
|
for value in (0, 50, 100)
|
|
437
486
|
},
|
|
438
487
|
**{
|
|
439
|
-
f"RE{('A','B')[reg]:s}XS": (PckGenerator.lock_regulator, reg, True, 0x120301)
|
|
488
|
+
f"RE{('A', 'B')[reg]:s}XS": (PckGenerator.lock_regulator, reg, True, 0x120301)
|
|
440
489
|
for reg in range(2)
|
|
441
490
|
},
|
|
442
491
|
**{
|
|
443
|
-
f"RE{('A','B')[reg]:s}XA": (PckGenerator.lock_regulator, reg, False, 0x120301)
|
|
492
|
+
f"RE{('A', 'B')[reg]:s}XA": (PckGenerator.lock_regulator, reg, False, 0x120301)
|
|
444
493
|
for reg in range(2)
|
|
445
494
|
},
|
|
446
495
|
# scenes
|
|
@@ -490,7 +539,7 @@ COMMANDS = {
|
|
|
490
539
|
),
|
|
491
540
|
# dynamic text
|
|
492
541
|
**{
|
|
493
|
-
f"GTDT{row+1:d}{part+1:d}asdfasdfasdf".encode(): (
|
|
542
|
+
f"GTDT{row + 1:d}{part + 1:d}asdfasdfasdf".encode(): (
|
|
494
543
|
PckGenerator.dyn_text_part,
|
|
495
544
|
row,
|
|
496
545
|
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
|