pyg90alarm 2.3.0__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.
- pyg90alarm/__init__.py +84 -0
- pyg90alarm/alarm.py +1274 -0
- pyg90alarm/callback.py +146 -0
- pyg90alarm/cloud/__init__.py +31 -0
- pyg90alarm/cloud/const.py +56 -0
- pyg90alarm/cloud/messages.py +593 -0
- pyg90alarm/cloud/notifications.py +410 -0
- pyg90alarm/cloud/protocol.py +518 -0
- pyg90alarm/const.py +273 -0
- pyg90alarm/definitions/__init__.py +3 -0
- pyg90alarm/definitions/base.py +247 -0
- pyg90alarm/definitions/devices.py +366 -0
- pyg90alarm/definitions/sensors.py +843 -0
- pyg90alarm/entities/__init__.py +3 -0
- pyg90alarm/entities/base_entity.py +93 -0
- pyg90alarm/entities/base_list.py +268 -0
- pyg90alarm/entities/device.py +97 -0
- pyg90alarm/entities/device_list.py +156 -0
- pyg90alarm/entities/sensor.py +891 -0
- pyg90alarm/entities/sensor_list.py +183 -0
- pyg90alarm/exceptions.py +63 -0
- pyg90alarm/local/__init__.py +0 -0
- pyg90alarm/local/base_cmd.py +293 -0
- pyg90alarm/local/config.py +157 -0
- pyg90alarm/local/discovery.py +103 -0
- pyg90alarm/local/history.py +272 -0
- pyg90alarm/local/host_info.py +89 -0
- pyg90alarm/local/host_status.py +52 -0
- pyg90alarm/local/notifications.py +117 -0
- pyg90alarm/local/paginated_cmd.py +132 -0
- pyg90alarm/local/paginated_result.py +135 -0
- pyg90alarm/local/targeted_discovery.py +162 -0
- pyg90alarm/local/user_data_crc.py +46 -0
- pyg90alarm/notifications/__init__.py +0 -0
- pyg90alarm/notifications/base.py +481 -0
- pyg90alarm/notifications/protocol.py +127 -0
- pyg90alarm/py.typed +0 -0
- pyg90alarm-2.3.0.dist-info/METADATA +277 -0
- pyg90alarm-2.3.0.dist-info/RECORD +42 -0
- pyg90alarm-2.3.0.dist-info/WHEEL +5 -0
- pyg90alarm-2.3.0.dist-info/licenses/LICENSE +21 -0
- pyg90alarm-2.3.0.dist-info/top_level.txt +1 -0
pyg90alarm/const.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# Copyright (c) 2021 Ilia Sotnikov
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
|
11
|
+
# all copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
# SOFTWARE.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
Definies different constants for G90 alarm panel.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
from enum import IntEnum
|
|
26
|
+
from typing import Optional
|
|
27
|
+
|
|
28
|
+
REMOTE_PORT = 12368
|
|
29
|
+
REMOTE_TARGETED_DISCOVERY_PORT = 12900
|
|
30
|
+
LOCAL_TARGETED_DISCOVERY_PORT = 12901
|
|
31
|
+
LOCAL_NOTIFICATIONS_HOST = '0.0.0.0'
|
|
32
|
+
LOCAL_NOTIFICATIONS_PORT = 12901
|
|
33
|
+
CLOUD_NOTIFICATIONS_HOST = '0.0.0.0'
|
|
34
|
+
CLOUD_NOTIFICATIONS_PORT = 5678
|
|
35
|
+
REMOTE_CLOUD_HOST = '47.88.7.61'
|
|
36
|
+
REMOTE_CLOUD_PORT = 5678
|
|
37
|
+
DEVICE_REGISTRATION_TIMEOUT = 30
|
|
38
|
+
ROOM_ID = 0
|
|
39
|
+
|
|
40
|
+
CMD_PAGE_SIZE = 10
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class G90Commands(IntEnum):
|
|
44
|
+
"""
|
|
45
|
+
Defines the alarm panel commands and their codes.
|
|
46
|
+
|
|
47
|
+
The list consists of the entities known so far, and does not pretend to be
|
|
48
|
+
comprehensive or complete.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __new__(cls, value: int, doc: Optional[str] = None) -> G90Commands:
|
|
52
|
+
"""
|
|
53
|
+
Allows to set the docstring along with the value to enum entry.
|
|
54
|
+
"""
|
|
55
|
+
obj = int.__new__(cls, value)
|
|
56
|
+
obj._value_ = value
|
|
57
|
+
obj.__doc__ = doc
|
|
58
|
+
return obj
|
|
59
|
+
|
|
60
|
+
NONE = (0, """
|
|
61
|
+
Pseudo command, to be used for proper typing with subclasses of
|
|
62
|
+
`G90BaseCommand` invoking its constructor but implementing special
|
|
63
|
+
processing
|
|
64
|
+
""")
|
|
65
|
+
|
|
66
|
+
# Host status
|
|
67
|
+
GETHOSTSTATUS = (100, 'Get host status')
|
|
68
|
+
SETHOSTSTATUS = (101, 'Set host status')
|
|
69
|
+
# Host info
|
|
70
|
+
GETHOSTINFO = 206
|
|
71
|
+
# History
|
|
72
|
+
GETHISTORY = 200
|
|
73
|
+
# Sensors
|
|
74
|
+
GETSENSORLIST = (102, """
|
|
75
|
+
Get list of sensors
|
|
76
|
+
|
|
77
|
+
.. note:: Paginated command, see :py:class:`.G90PaginatedResult`
|
|
78
|
+
""")
|
|
79
|
+
SETSINGLESENSOR = 103
|
|
80
|
+
DELSENSOR = 131
|
|
81
|
+
ADDSENSOR = 156
|
|
82
|
+
LEARNSENSOR = 157
|
|
83
|
+
CANCELLEARNSENSOR = 163
|
|
84
|
+
DELALLSENSORS = 202
|
|
85
|
+
# Switches (relays)
|
|
86
|
+
ADDDEVICE = 134
|
|
87
|
+
REGDEVICE = 135
|
|
88
|
+
DELDEVICE = 136
|
|
89
|
+
CONTROLDEVICE = 137
|
|
90
|
+
GETDEVICELIST = (138, """
|
|
91
|
+
Get list of devices (switches)
|
|
92
|
+
|
|
93
|
+
.. note:: Paginated command, see :py:class:`.G90PaginatedResult`
|
|
94
|
+
""")
|
|
95
|
+
GETSINGLEDEVICE = 139
|
|
96
|
+
SETSINGLEDEVICE = 140
|
|
97
|
+
SENDREGDEVICERESULT = 162
|
|
98
|
+
DELALLDEVICES = 203
|
|
99
|
+
# Host config
|
|
100
|
+
GETHOSTCONFIG = 106
|
|
101
|
+
SETHOSTCONFIG = 107
|
|
102
|
+
SETALMPHONE = 108
|
|
103
|
+
SETAUTOARM = 109
|
|
104
|
+
# Wireless sirens
|
|
105
|
+
GETSIREN = 110
|
|
106
|
+
SETSIREN = 111
|
|
107
|
+
# Alarm phones, notifications
|
|
108
|
+
GETALMPHONE = 114
|
|
109
|
+
GETAUTOARM = 115
|
|
110
|
+
SETNOTICEFLAG = 116
|
|
111
|
+
GETNOTICEFLAG = 117
|
|
112
|
+
# Factory reset
|
|
113
|
+
SETFACTORY = 118
|
|
114
|
+
GETALARM = 119
|
|
115
|
+
# Rooms
|
|
116
|
+
SETROOMINFO = 141
|
|
117
|
+
GETROOMINFO = 142
|
|
118
|
+
ADDROOM = 158
|
|
119
|
+
DELROOM = 159
|
|
120
|
+
# Scenes
|
|
121
|
+
ADDSCENE = 143
|
|
122
|
+
DELSCENE = 144
|
|
123
|
+
CTLSCENE = 145
|
|
124
|
+
GETSCENELIST = (146, """
|
|
125
|
+
Get list of scenes
|
|
126
|
+
|
|
127
|
+
.. note:: Paginated command, see :py:class:`.G90PaginatedResult`
|
|
128
|
+
""")
|
|
129
|
+
GETSINGLESCENE = 147
|
|
130
|
+
SETSINGLESCENE = 148
|
|
131
|
+
GETROOMANDSCENE = 149
|
|
132
|
+
DELALLSCENES = 204
|
|
133
|
+
# IFTTT (scenarios)
|
|
134
|
+
ADDIFTTT = 150
|
|
135
|
+
DELIFTTT = 151
|
|
136
|
+
GETIFTTTLIST = (152, """
|
|
137
|
+
Get list of if-then-else scenarios
|
|
138
|
+
|
|
139
|
+
.. note:: Paginated command, see :py:class:`.G90PaginatedResult`
|
|
140
|
+
""")
|
|
141
|
+
GETSINGLEIFTTT = 153
|
|
142
|
+
SETSINGLEIFTTT = 154
|
|
143
|
+
IFTTTREQTIMERID = 164
|
|
144
|
+
DELALLIFTTT = 205
|
|
145
|
+
# Data CRC
|
|
146
|
+
GETUSERDATACRC = 160
|
|
147
|
+
# Fingerprint scanners
|
|
148
|
+
GETFPLOCKLIST = (165, """
|
|
149
|
+
Get list of fingerprint scanners
|
|
150
|
+
|
|
151
|
+
.. note:: Paginated command, see :py:class:`.G90PaginatedResult`
|
|
152
|
+
""")
|
|
153
|
+
SETFPLOCKNAME = 166
|
|
154
|
+
GETFPLOCKUSERNAME = 167
|
|
155
|
+
SETFPLOCKUSERNAME = 168
|
|
156
|
+
DELALLLOCK = 223
|
|
157
|
+
# Miscellaneous
|
|
158
|
+
GETAPINFO = 212
|
|
159
|
+
PINGBYGPRS = 218
|
|
160
|
+
PING = 219
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class G90MessageTypes(IntEnum):
|
|
164
|
+
"""
|
|
165
|
+
Defines message types (codes) from messages coming from the alarm panel.
|
|
166
|
+
"""
|
|
167
|
+
NOTIFICATION = 170
|
|
168
|
+
ALERT = 208
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class G90NotificationTypes(IntEnum):
|
|
172
|
+
"""
|
|
173
|
+
Defines types of notifications sent by the alarm panel.
|
|
174
|
+
"""
|
|
175
|
+
ARM_DISARM = 1
|
|
176
|
+
SENSOR_CHANGE = 4
|
|
177
|
+
SENSOR_ACTIVITY = 5
|
|
178
|
+
DOOR_OPEN_WHEN_ARMING = 6
|
|
179
|
+
FIRMWARE_UPDATING = 8
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class G90ArmDisarmTypes(IntEnum):
|
|
183
|
+
"""
|
|
184
|
+
Defines arm/disarm states of the device, applicable both for setting device
|
|
185
|
+
state and one the device sends in notification messages.
|
|
186
|
+
"""
|
|
187
|
+
ARM_AWAY = 1
|
|
188
|
+
ARM_HOME = 2
|
|
189
|
+
DISARM = 3
|
|
190
|
+
ALARMED = 4
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class G90AlertTypes(IntEnum):
|
|
194
|
+
"""
|
|
195
|
+
Defines types of alerts sent by the alarm panel.
|
|
196
|
+
"""
|
|
197
|
+
HOST_SOS = 1
|
|
198
|
+
STATE_CHANGE = 2
|
|
199
|
+
ALARM = 3
|
|
200
|
+
SENSOR_ACTIVITY = 4
|
|
201
|
+
# Retained for compatibility, deprecated
|
|
202
|
+
DOOR_OPEN_CLOSE = 4
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class G90AlertSources(IntEnum):
|
|
206
|
+
"""
|
|
207
|
+
Defines possible sources of the alert sent by the panel.
|
|
208
|
+
"""
|
|
209
|
+
DEVICE = 0
|
|
210
|
+
SENSOR = 1
|
|
211
|
+
TAMPER = 3
|
|
212
|
+
REMOTE = 10
|
|
213
|
+
RFID = 11
|
|
214
|
+
DOORBELL = 12
|
|
215
|
+
FINGERPRINT = 15
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class G90AlertStates(IntEnum):
|
|
219
|
+
"""
|
|
220
|
+
Defines possible states of the alert sent by the panel.
|
|
221
|
+
"""
|
|
222
|
+
DOOR_CLOSE = 0
|
|
223
|
+
DOOR_OPEN = 1
|
|
224
|
+
SOS = 2
|
|
225
|
+
TAMPER = 3
|
|
226
|
+
LOW_BATTERY = 4
|
|
227
|
+
ALARM = 254
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class G90AlertStateChangeTypes(IntEnum):
|
|
231
|
+
"""
|
|
232
|
+
Defines types of alert for device state changes.
|
|
233
|
+
"""
|
|
234
|
+
AC_POWER_FAILURE = 1
|
|
235
|
+
AC_POWER_RECOVER = 2
|
|
236
|
+
DISARM = 3
|
|
237
|
+
ARM_AWAY = 4
|
|
238
|
+
ARM_HOME = 5
|
|
239
|
+
LOW_BATTERY = 6
|
|
240
|
+
WIFI_CONNECTED = 7
|
|
241
|
+
WIFI_DISCONNECTED = 8
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class G90HistoryStates(IntEnum):
|
|
245
|
+
"""
|
|
246
|
+
Defines possible states for history entities.
|
|
247
|
+
"""
|
|
248
|
+
DOOR_CLOSE = 1
|
|
249
|
+
DOOR_OPEN = 2
|
|
250
|
+
TAMPER = 3
|
|
251
|
+
ALARM = 4
|
|
252
|
+
AC_POWER_FAILURE = 5
|
|
253
|
+
AC_POWER_RECOVER = 6
|
|
254
|
+
DISARM = 7
|
|
255
|
+
ARM_AWAY = 8
|
|
256
|
+
ARM_HOME = 9
|
|
257
|
+
LOW_BATTERY = 10
|
|
258
|
+
WIFI_CONNECTED = 11
|
|
259
|
+
WIFI_DISCONNECTED = 12
|
|
260
|
+
REMOTE_BUTTON_ARM_AWAY = 13
|
|
261
|
+
REMOTE_BUTTON_ARM_HOME = 14
|
|
262
|
+
REMOTE_BUTTON_DISARM = 15
|
|
263
|
+
REMOTE_BUTTON_SOS = 16
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class G90RemoteButtonStates(IntEnum):
|
|
267
|
+
"""
|
|
268
|
+
Defines possible states for remote control buttons.
|
|
269
|
+
"""
|
|
270
|
+
ARM_AWAY = 0
|
|
271
|
+
ARM_HOME = 1
|
|
272
|
+
DISARM = 2
|
|
273
|
+
SOS = 3
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Copyright (c) 2025 Ilia Sotnikov
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
|
11
|
+
# all copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
# SOFTWARE.
|
|
20
|
+
"""
|
|
21
|
+
Base entities for peripheral definitions.
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from itertools import groupby
|
|
26
|
+
from enum import IntEnum
|
|
27
|
+
from abc import ABC, abstractmethod
|
|
28
|
+
import logging
|
|
29
|
+
from ..exceptions import G90PeripheralDefinitionNotFound, G90Error
|
|
30
|
+
|
|
31
|
+
_LOGGER = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class G90PeripheralProtocols(IntEnum):
|
|
35
|
+
"""
|
|
36
|
+
Protocol types for the peripherals.
|
|
37
|
+
"""
|
|
38
|
+
RF_1527 = 0
|
|
39
|
+
RF_2262 = 1
|
|
40
|
+
RF_PRIVATE = 2
|
|
41
|
+
RF_SLIDER = 3
|
|
42
|
+
CORD = 5
|
|
43
|
+
WIFI = 4
|
|
44
|
+
USB = 6
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class G90PeripheralTypes(IntEnum):
|
|
48
|
+
"""
|
|
49
|
+
Peripheral types.
|
|
50
|
+
"""
|
|
51
|
+
DOOR = 1
|
|
52
|
+
GLASS = 2
|
|
53
|
+
GAS = 3
|
|
54
|
+
SMOKE = 4
|
|
55
|
+
SOS = 5
|
|
56
|
+
VIB = 6
|
|
57
|
+
WATER = 7
|
|
58
|
+
INFRARED = 8
|
|
59
|
+
IN_BEAM = 9
|
|
60
|
+
REMOTE = 10
|
|
61
|
+
RFID = 11
|
|
62
|
+
DOORBELL = 12
|
|
63
|
+
BUTTONID = 13
|
|
64
|
+
WATCH = 14
|
|
65
|
+
FINGER_LOCK = 15
|
|
66
|
+
SUBHOST = 16
|
|
67
|
+
REMOTE_2_4G = 17
|
|
68
|
+
GAS_VALVE = 18
|
|
69
|
+
CORD_SENSOR = 126
|
|
70
|
+
SOCKET = 128
|
|
71
|
+
SIREN = 129
|
|
72
|
+
CURTAIN = 130
|
|
73
|
+
SLIDINGWIN = 131
|
|
74
|
+
AIRCON = 133
|
|
75
|
+
TV = 135
|
|
76
|
+
TV_BOX = 136
|
|
77
|
+
SMART_SWITCH = 137
|
|
78
|
+
NIGHTLIGHT = 138
|
|
79
|
+
SOCKET_2_4G = 140
|
|
80
|
+
SIREN_2_4G = 141
|
|
81
|
+
SWITCH_2_4G = 142
|
|
82
|
+
TOUCH_SWITCH_2_4G = 143
|
|
83
|
+
CURTAIN_2_4G = 144
|
|
84
|
+
IR_2_4G = 145
|
|
85
|
+
CORD_DEV = 254
|
|
86
|
+
UNKNOWN = 255
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class G90PeripheralMatchModes(IntEnum):
|
|
90
|
+
"""
|
|
91
|
+
Defines compare (match) mode for the peripheral.
|
|
92
|
+
"""
|
|
93
|
+
ALL = 0
|
|
94
|
+
ONLY20BITS = 1
|
|
95
|
+
ONLY16BITS = 2
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class G90PeripheralRwModes(IntEnum):
|
|
99
|
+
"""
|
|
100
|
+
Defines read/write mode for the peripheral.
|
|
101
|
+
"""
|
|
102
|
+
READ = 0
|
|
103
|
+
WRITE = 1
|
|
104
|
+
READ_WRITE = 2
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass(frozen=True)
|
|
108
|
+
class G90PeripheralDefinition:
|
|
109
|
+
# pylint: disable=too-many-instance-attributes
|
|
110
|
+
"""
|
|
111
|
+
Holds peripheral definition data.
|
|
112
|
+
"""
|
|
113
|
+
type: G90PeripheralTypes
|
|
114
|
+
subtype: int
|
|
115
|
+
rx: int
|
|
116
|
+
tx: int
|
|
117
|
+
private_data: str
|
|
118
|
+
rw_mode: G90PeripheralRwModes
|
|
119
|
+
match_mode: G90PeripheralMatchModes
|
|
120
|
+
name: str
|
|
121
|
+
protocol: G90PeripheralProtocols
|
|
122
|
+
timeout: int
|
|
123
|
+
baudrate: int
|
|
124
|
+
node_count: int
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def reserved_data(self) -> int:
|
|
128
|
+
"""
|
|
129
|
+
Peripheral's 'reserved_data' field to be written, combined of match
|
|
130
|
+
and RW mode values bitwise.
|
|
131
|
+
"""
|
|
132
|
+
return self.match_mode.value << 4 | self.rw_mode.value
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def unique_definitions(
|
|
136
|
+
obj: type[G90PeripheralDefinitionsBase]
|
|
137
|
+
) -> type[G90PeripheralDefinitionsBase]:
|
|
138
|
+
"""
|
|
139
|
+
Decorator to ensure that peripheral definitions are unique by name
|
|
140
|
+
and type/subtype/protocol.
|
|
141
|
+
|
|
142
|
+
:param obj: Class to decorate.
|
|
143
|
+
:return: Decorated class with unique definitions.
|
|
144
|
+
:raises G90Error: If definitions are not unique.
|
|
145
|
+
"""
|
|
146
|
+
names = groupby(
|
|
147
|
+
sorted(obj.definitions(), key=lambda x: x.name),
|
|
148
|
+
lambda x: x.name
|
|
149
|
+
)
|
|
150
|
+
type_subtype = groupby(
|
|
151
|
+
sorted(
|
|
152
|
+
obj.definitions(),
|
|
153
|
+
key=lambda x: (x.type, x.subtype, x.protocol)
|
|
154
|
+
),
|
|
155
|
+
lambda x: (x.type, x.subtype, x.protocol)
|
|
156
|
+
)
|
|
157
|
+
non_unique_names = [
|
|
158
|
+
k for _, group in names if len(k := list(group)) > 1
|
|
159
|
+
]
|
|
160
|
+
non_unique_types = [
|
|
161
|
+
k for _, group in type_subtype if len(k := list(group)) > 1
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
msgs = []
|
|
165
|
+
if non_unique_names:
|
|
166
|
+
msgs.append(
|
|
167
|
+
f"{obj}: Peripheral definitions have non-unique names: \n"
|
|
168
|
+
f"{non_unique_names}"
|
|
169
|
+
)
|
|
170
|
+
if non_unique_types:
|
|
171
|
+
msgs.append(
|
|
172
|
+
f"{obj}: Peripheral definitions have non-unique types: \n"
|
|
173
|
+
f"{non_unique_types}"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if msgs:
|
|
177
|
+
raise G90Error('.\n'.join(msgs))
|
|
178
|
+
|
|
179
|
+
return obj
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class G90PeripheralDefinitionsBase(ABC):
|
|
183
|
+
"""
|
|
184
|
+
Base class for peripheral definitions.
|
|
185
|
+
"""
|
|
186
|
+
@classmethod
|
|
187
|
+
@abstractmethod
|
|
188
|
+
def definitions(cls) -> list[G90PeripheralDefinition]:
|
|
189
|
+
"""
|
|
190
|
+
Get all peripheral definitions.
|
|
191
|
+
|
|
192
|
+
:return: List of peripheral definitions.
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
@classmethod
|
|
196
|
+
def get_by_id(
|
|
197
|
+
cls, id_type: G90PeripheralTypes, id_subtype: int,
|
|
198
|
+
protocol: G90PeripheralProtocols
|
|
199
|
+
) -> G90PeripheralDefinition:
|
|
200
|
+
"""
|
|
201
|
+
Gets peripheral definition by type, subtype and protocol.
|
|
202
|
+
|
|
203
|
+
:param id_type: Peripheral type.
|
|
204
|
+
:param id_subtype: Peripheral subtype.
|
|
205
|
+
:param protocol: Peripheral protocol.
|
|
206
|
+
:raises G90PeripheralDefinitionNotFound: If definition not found.
|
|
207
|
+
"""
|
|
208
|
+
for definition in cls.definitions():
|
|
209
|
+
if (
|
|
210
|
+
definition.type == id_type
|
|
211
|
+
and definition.subtype == id_subtype
|
|
212
|
+
and definition.protocol == protocol
|
|
213
|
+
):
|
|
214
|
+
_LOGGER.debug(
|
|
215
|
+
"Found peripheral definition by"
|
|
216
|
+
" type %d, subtype %d and protocol %d: %s",
|
|
217
|
+
id_type, id_subtype, protocol, definition
|
|
218
|
+
)
|
|
219
|
+
return definition
|
|
220
|
+
|
|
221
|
+
raise G90PeripheralDefinitionNotFound(
|
|
222
|
+
"Peripheral definition not found"
|
|
223
|
+
f" by type={id_type}, subtype={id_subtype}"
|
|
224
|
+
f" and protocol={protocol}",
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
@classmethod
|
|
228
|
+
def get_by_name(
|
|
229
|
+
cls, name: str
|
|
230
|
+
) -> G90PeripheralDefinition:
|
|
231
|
+
"""
|
|
232
|
+
Gets peripheral definition by name.
|
|
233
|
+
|
|
234
|
+
:param name: Peripheral name.
|
|
235
|
+
:raises G90PeripheralDefinitionNotFound: If definition not found.
|
|
236
|
+
"""
|
|
237
|
+
for definition in cls.definitions():
|
|
238
|
+
if definition.name == name:
|
|
239
|
+
_LOGGER.debug(
|
|
240
|
+
"Found peripheral definition by name '%s': %s",
|
|
241
|
+
name, definition
|
|
242
|
+
)
|
|
243
|
+
return definition
|
|
244
|
+
|
|
245
|
+
raise G90PeripheralDefinitionNotFound(
|
|
246
|
+
f"Peripheral definition not found by name='{name}'"
|
|
247
|
+
)
|