pydiagral 1.0.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.
- pydiagral/__init__.py +28 -0
- pydiagral/api.py +1249 -0
- pydiagral/constants.py +4 -0
- pydiagral/exceptions.py +39 -0
- pydiagral/models.py +1498 -0
- pydiagral/utils.py +14 -0
- pydiagral-1.0.0.dist-info/METADATA +828 -0
- pydiagral-1.0.0.dist-info/RECORD +10 -0
- pydiagral-1.0.0.dist-info/WHEEL +4 -0
- pydiagral-1.0.0.dist-info/licenses/LICENSE +674 -0
pydiagral/models.py
ADDED
@@ -0,0 +1,1498 @@
|
|
1
|
+
"""Module containing data models for interacting with the Diagral API.
|
2
|
+
|
3
|
+
The models include representations for login responses, API key creation and validation,
|
4
|
+
and other related data structures.
|
5
|
+
"""
|
6
|
+
|
7
|
+
# Minimum Python version: 3.10
|
8
|
+
|
9
|
+
from __future__ import annotations
|
10
|
+
|
11
|
+
from dataclasses import dataclass, field, fields
|
12
|
+
from datetime import datetime, timezone
|
13
|
+
import logging
|
14
|
+
import re
|
15
|
+
import types
|
16
|
+
from typing import TypeVar, Union, get_args, get_origin, get_type_hints
|
17
|
+
|
18
|
+
logger = logging.getLogger(__name__)
|
19
|
+
|
20
|
+
|
21
|
+
#######################################################
|
22
|
+
# Class for converting between camelCase and snake_case
|
23
|
+
#######################################################
|
24
|
+
|
25
|
+
|
26
|
+
class CamelCaseModel:
|
27
|
+
"""CamelCaseModel is a base class for models that need to convert between snake_case and camelCase keys.
|
28
|
+
|
29
|
+
Methods:
|
30
|
+
to_dict() -> dict:
|
31
|
+
Convert the model instance to a dictionary with camelCase keys.
|
32
|
+
_from_dict_recursive(cls, data: dict, target_cls: type[T]) -> T:
|
33
|
+
Recursively create an instance of the target class from a dictionary.
|
34
|
+
from_dict(cls: type[T], data: dict) -> T:
|
35
|
+
Create an instance of the model from a dictionary.
|
36
|
+
snake_to_camel(string: str) -> str:
|
37
|
+
Convert a snake_case string to camelCase.
|
38
|
+
camel_to_snake(string: str) -> str:
|
39
|
+
Convert a camelCase string to snake_case.
|
40
|
+
|
41
|
+
Examples:
|
42
|
+
>>> @dataclass
|
43
|
+
... class ExampleModel(CamelCaseModel):
|
44
|
+
... first_name: str
|
45
|
+
... last_name: str
|
46
|
+
...
|
47
|
+
>>> example = ExampleModel(first_name="Luke", last_name="Skywalker")
|
48
|
+
>>> example_dict = example.to_dict()
|
49
|
+
>>> print(example_dict)
|
50
|
+
{'firstName': 'Luke', 'lastName': 'Skywalker'}
|
51
|
+
>>> new_example = ExampleModel.from_dict(example_dict)
|
52
|
+
>>> print(new_example)
|
53
|
+
ExampleModel(first_name='Luke', last_name='Skywalker')
|
54
|
+
|
55
|
+
"""
|
56
|
+
|
57
|
+
# Type variable for the class itself
|
58
|
+
T = TypeVar("T", bound="CamelCaseModel")
|
59
|
+
|
60
|
+
def to_dict(self) -> dict:
|
61
|
+
"""Convert the instance attributes to a dictionary, transforming attribute names.
|
62
|
+
|
63
|
+
from snake_case to camelCase and handling nested CamelCaseModel instances.
|
64
|
+
|
65
|
+
Returns:
|
66
|
+
dict: A dictionary representation of the instance with camelCase keys.
|
67
|
+
|
68
|
+
Example:
|
69
|
+
>>> class ExampleModel(CamelCaseModel):
|
70
|
+
... first_name: str
|
71
|
+
... last_name: str
|
72
|
+
...
|
73
|
+
>>> example = ExampleModel(first_name="Luke", last_name="Skywalker")
|
74
|
+
>>> example.to_dict()
|
75
|
+
{'firstName': 'Luke', 'lastName': 'Skywalker'}
|
76
|
+
|
77
|
+
"""
|
78
|
+
|
79
|
+
result = {}
|
80
|
+
for k, v in self.__dict__.items():
|
81
|
+
if v is not None:
|
82
|
+
if isinstance(v, CamelCaseModel):
|
83
|
+
v = v.to_dict()
|
84
|
+
elif isinstance(v, list) and v and isinstance(v[0], CamelCaseModel):
|
85
|
+
v = [item.to_dict() for item in v]
|
86
|
+
key = getattr(self.__class__, k).metadata.get(
|
87
|
+
"alias", self.snake_to_camel(k)
|
88
|
+
)
|
89
|
+
result[key] = v
|
90
|
+
return result
|
91
|
+
|
92
|
+
@classmethod
|
93
|
+
def _from_dict_recursive(cls, data: dict, target_cls: type[T]) -> T:
|
94
|
+
"""Recursively converts a dictionary to an instance of the specified target class.
|
95
|
+
|
96
|
+
This method handles nested dictionaries and lists, converting them to the appropriate
|
97
|
+
types as specified by the target class's type hints. It also supports optional fields
|
98
|
+
by handling `Union` types and removing `None` from the type hints.
|
99
|
+
|
100
|
+
Args:
|
101
|
+
cls: The class that this method is a part of.
|
102
|
+
data (dict): The dictionary to convert.
|
103
|
+
target_cls (type[T]): The target class to convert the dictionary to.
|
104
|
+
|
105
|
+
Returns:
|
106
|
+
T: An instance of the target class populated with the data from the dictionary.
|
107
|
+
|
108
|
+
Raises:
|
109
|
+
TypeError: If the target class cannot be instantiated with the provided data.
|
110
|
+
|
111
|
+
Notes:
|
112
|
+
- The method assumes that the target class and its nested classes (if any) are
|
113
|
+
annotated with type hints.
|
114
|
+
- The method uses snake_case to camelCase conversion for dictionary keys to match
|
115
|
+
the field names in the target class.
|
116
|
+
- The method logs detailed debug information about the conversion process.
|
117
|
+
|
118
|
+
"""
|
119
|
+
|
120
|
+
logger.debug("Converting data: %s to %s", data, target_cls)
|
121
|
+
|
122
|
+
logger.debug("Extracted target_cls: %s", target_cls)
|
123
|
+
if get_origin(target_cls) is Union:
|
124
|
+
# Extract the real type by removing None
|
125
|
+
target_cls = next(t for t in get_args(target_cls) if t is not type(None))
|
126
|
+
logger.debug("Extracted target_cls: %s", target_cls)
|
127
|
+
|
128
|
+
init_values = {}
|
129
|
+
fields_dict = {field.name: field for field in fields(target_cls)}
|
130
|
+
for field_name, field_type in get_type_hints(target_cls).items():
|
131
|
+
field = fields_dict.get(field_name)
|
132
|
+
logger.debug("Field Metadata: %s", field.metadata if field else {})
|
133
|
+
# alias = cls.snake_to_camel(field_name) # Old version who don't support field with underscore and without alias
|
134
|
+
alias = field.metadata.get("alias", field_name)
|
135
|
+
logger.debug(
|
136
|
+
"Processing field: %s (alias: %s, type: %s)",
|
137
|
+
field_name,
|
138
|
+
alias,
|
139
|
+
field_type,
|
140
|
+
)
|
141
|
+
|
142
|
+
logger.debug("Extracted field_type: %s", field_type)
|
143
|
+
if get_origin(field_type) is types.UnionType:
|
144
|
+
# Extract the real type by removing None
|
145
|
+
field_type = next(
|
146
|
+
t for t in get_args(field_type) if t is not type(None)
|
147
|
+
)
|
148
|
+
logger.debug("Extracted field_type: %s", field_type)
|
149
|
+
|
150
|
+
logger.debug("Checking if alias %s is in data: %s", alias, data)
|
151
|
+
if any(alias.lower() == key.lower() for key in data):
|
152
|
+
alias = next(key for key in data if alias.lower() == key.lower())
|
153
|
+
value = data[alias]
|
154
|
+
logger.debug("Found value for %s: %s", alias, value)
|
155
|
+
|
156
|
+
if (
|
157
|
+
isinstance(value, dict)
|
158
|
+
and isinstance(field_type, type)
|
159
|
+
and issubclass(field_type, CamelCaseModel)
|
160
|
+
):
|
161
|
+
logger.debug(
|
162
|
+
"Recursively converting nested dict for field: %s", field_name
|
163
|
+
)
|
164
|
+
init_values[field_name] = cls._from_dict_recursive(
|
165
|
+
value, field_type
|
166
|
+
)
|
167
|
+
elif isinstance(value, list) and get_origin(field_type) is list:
|
168
|
+
item_type = get_args(field_type)[0]
|
169
|
+
logger.debug(
|
170
|
+
"Recursively converting list for field: %s with item type: %s",
|
171
|
+
field_name,
|
172
|
+
item_type,
|
173
|
+
)
|
174
|
+
init_values[field_name] = [
|
175
|
+
cls._from_dict_recursive(item, item_type)
|
176
|
+
if isinstance(item, dict)
|
177
|
+
else item
|
178
|
+
for item in value
|
179
|
+
]
|
180
|
+
else:
|
181
|
+
init_values[field_name] = value
|
182
|
+
else:
|
183
|
+
init_values[field_name] = None
|
184
|
+
logger.debug("No value found for %s, setting to None", alias)
|
185
|
+
|
186
|
+
logger.debug("Initialized values for %s: %s", target_cls, init_values)
|
187
|
+
return target_cls(**init_values)
|
188
|
+
|
189
|
+
@classmethod
|
190
|
+
def from_dict(cls: type[T], data: dict) -> T:
|
191
|
+
"""Create an instance of the class from a dictionary.
|
192
|
+
|
193
|
+
Args:
|
194
|
+
cls (type[T]): The class type to instantiate.
|
195
|
+
data (dict): The dictionary containing the data to populate the instance.
|
196
|
+
|
197
|
+
Returns:
|
198
|
+
T: An instance of the class populated with the data from the dictionary.
|
199
|
+
|
200
|
+
Example:
|
201
|
+
>>> data = {"diagral_id": 123, "user_id": 456, "access_token": "abc123"}
|
202
|
+
>>> login_response = LoginResponse.from_dict(data)
|
203
|
+
>>> login_response.diagral_id
|
204
|
+
123
|
205
|
+
>>> login_response.user_id
|
206
|
+
456
|
207
|
+
>>> login_response.access_token
|
208
|
+
'abc123'
|
209
|
+
|
210
|
+
"""
|
211
|
+
|
212
|
+
return cls._from_dict_recursive(data, cls)
|
213
|
+
|
214
|
+
@staticmethod
|
215
|
+
def snake_to_camel(string: str) -> str:
|
216
|
+
"""Convert a snake_case string to camelCase.
|
217
|
+
|
218
|
+
Args:
|
219
|
+
string (str): The snake_case string to be converted.
|
220
|
+
|
221
|
+
Returns:
|
222
|
+
str: The converted camelCase string.
|
223
|
+
|
224
|
+
Example:
|
225
|
+
>>> snake_to_camel("example_string")
|
226
|
+
'exampleString'
|
227
|
+
|
228
|
+
"""
|
229
|
+
|
230
|
+
components = string.split("_")
|
231
|
+
return components[0] + "".join(x.title() for x in components[1:])
|
232
|
+
|
233
|
+
@staticmethod
|
234
|
+
def camel_to_snake(string: str) -> str:
|
235
|
+
"""Convert a CamelCase string to snake_case.
|
236
|
+
|
237
|
+
Args:
|
238
|
+
string (str): The CamelCase string to be converted.
|
239
|
+
|
240
|
+
Returns:
|
241
|
+
str: The converted snake_case string.
|
242
|
+
|
243
|
+
Example:
|
244
|
+
>>> camel_to_snake("CamelCaseString")
|
245
|
+
'camel_case_string'
|
246
|
+
|
247
|
+
"""
|
248
|
+
|
249
|
+
# Replace capital letters with _ followed by the lowercase letter
|
250
|
+
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", string)
|
251
|
+
# Handle cases where multiple capitals are together
|
252
|
+
s2 = re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1)
|
253
|
+
return s2.lower()
|
254
|
+
|
255
|
+
|
256
|
+
##############################################
|
257
|
+
# Data models for Diagral API Authentification
|
258
|
+
##############################################
|
259
|
+
|
260
|
+
|
261
|
+
@dataclass
|
262
|
+
class LoginResponse(CamelCaseModel):
|
263
|
+
"""LoginResponse model represents the response received after a successful login.
|
264
|
+
|
265
|
+
Attributes:
|
266
|
+
access_token (str): The access token provided for authentication.
|
267
|
+
|
268
|
+
Example:
|
269
|
+
>>> response = LoginResponse(
|
270
|
+
... access_token="abc123",
|
271
|
+
... )
|
272
|
+
>>> print(response.access_token)
|
273
|
+
abc123
|
274
|
+
|
275
|
+
"""
|
276
|
+
|
277
|
+
access_token: str
|
278
|
+
|
279
|
+
|
280
|
+
@dataclass
|
281
|
+
class ApiKeyWithSecret(CamelCaseModel):
|
282
|
+
"""ApiKeyWithSecret is a model that represents an API key and its corresponding secret key.
|
283
|
+
|
284
|
+
Attributes:
|
285
|
+
api_key (str): The API key, which must be a non-empty string.
|
286
|
+
secret_key (str): The secret key associated with the API key, which must also be a non-empty string.
|
287
|
+
|
288
|
+
Methods:
|
289
|
+
__post_init__(): Post-initialization processing to validate the API key and secret key.
|
290
|
+
|
291
|
+
Example:
|
292
|
+
>>> api_key_with_secret = ApiKeyWithSecret(api_key="your_api_key", secret_key="your_secret_key")
|
293
|
+
>>> print(api_key_with_secret.api_key)
|
294
|
+
your_api_key
|
295
|
+
>>> print(api_key_with_secret.secret_key)
|
296
|
+
your_secret_key
|
297
|
+
|
298
|
+
"""
|
299
|
+
|
300
|
+
api_key: str
|
301
|
+
secret_key: str
|
302
|
+
|
303
|
+
def __post_init__(self):
|
304
|
+
"""Post-initialization processing to validate API key and secret key."""
|
305
|
+
if not self.api_key or not isinstance(self.api_key, str):
|
306
|
+
raise ValueError("api_key must be a non-empty string")
|
307
|
+
if not self.secret_key or not isinstance(self.secret_key, str):
|
308
|
+
raise ValueError("secret_key must be a non-empty string")
|
309
|
+
|
310
|
+
|
311
|
+
@dataclass
|
312
|
+
class ApiKey(CamelCaseModel):
|
313
|
+
"""Represents an API key model.
|
314
|
+
|
315
|
+
Attributes:
|
316
|
+
api_key (str): The API key as a string.
|
317
|
+
|
318
|
+
Example:
|
319
|
+
>>> api_key = ApiKey(api_key="your_api_key")
|
320
|
+
>>> print(api_key.api_key)
|
321
|
+
your_api_key
|
322
|
+
|
323
|
+
"""
|
324
|
+
|
325
|
+
api_key: str
|
326
|
+
|
327
|
+
|
328
|
+
@dataclass
|
329
|
+
class ApiKeys(CamelCaseModel):
|
330
|
+
"""ApiKeys model to represent a collection of API keys.
|
331
|
+
|
332
|
+
Attributes:
|
333
|
+
api_keys (list[ApiKey]): A list of ApiKey instances.
|
334
|
+
|
335
|
+
Methods:
|
336
|
+
from_dict(data: dict) -> ApiKeys:
|
337
|
+
Class method to create an instance of ApiKeys from a dictionary.
|
338
|
+
|
339
|
+
Example:
|
340
|
+
>>> data = {"api_keys": [{"api_key": "key1"}, {"api_key": "key2"}]}
|
341
|
+
>>> api_keys = ApiKeys.from_dict(data)
|
342
|
+
>>> print(api_keys.api_keys)
|
343
|
+
[ApiKey(api_key='key1'), ApiKey(api_key='key2')]
|
344
|
+
|
345
|
+
"""
|
346
|
+
|
347
|
+
api_keys: list[ApiKey]
|
348
|
+
|
349
|
+
@classmethod
|
350
|
+
def from_dict(cls, data: dict) -> ApiKeys:
|
351
|
+
"""Create an instance of ApiKeys from a dictionary."""
|
352
|
+
|
353
|
+
return cls(
|
354
|
+
api_keys=[ApiKey(**key_info) for key_info in data.get("api_keys", [])]
|
355
|
+
)
|
356
|
+
|
357
|
+
|
358
|
+
#####################################
|
359
|
+
# Data models for alarm configuration
|
360
|
+
#####################################
|
361
|
+
|
362
|
+
|
363
|
+
@dataclass
|
364
|
+
class FirmwareModel(CamelCaseModel):
|
365
|
+
"""FirmwareModel represents the firmware details of a device.
|
366
|
+
|
367
|
+
Attributes:
|
368
|
+
box (str | None): The firmware version of the box, aliased as "BOX".
|
369
|
+
central (str | None): The firmware version of the central unit, aliased as "CENTRAL".
|
370
|
+
centralradio (str | None): The firmware version of the central radio unit, aliased as "CENTRALRADIO".
|
371
|
+
|
372
|
+
Example:
|
373
|
+
>>> firmware = FirmwareModel(box="1.0.0", central="2.0.0", centralradio="3.0.0")
|
374
|
+
>>> print(firmware.box)
|
375
|
+
'1.0.0'
|
376
|
+
>>> print(firmware.central)
|
377
|
+
'2.0.0'
|
378
|
+
>>> print(firmware.centralradio)
|
379
|
+
'3.0.0'
|
380
|
+
|
381
|
+
"""
|
382
|
+
|
383
|
+
box: str | None = field(default=None, metadata={"alias": "BOX"})
|
384
|
+
central: str | None = field(default=None, metadata={"alias": "CENTRAL"})
|
385
|
+
centralradio: str | None = field(default=None, metadata={"alias": "CENTRALRADIO"})
|
386
|
+
|
387
|
+
|
388
|
+
@dataclass
|
389
|
+
class CentralPlugModel(CamelCaseModel):
|
390
|
+
"""CentralPlugModel represents the central plug device.
|
391
|
+
|
392
|
+
Attributes:
|
393
|
+
name (str | None): The name of the central plug device.
|
394
|
+
serial (str | None): The serial number of the central plug device.
|
395
|
+
vendor (str | None): The vendor of the central plug device.
|
396
|
+
firmwares (FirmwareModel | None): The firmware information of the central plug device.
|
397
|
+
|
398
|
+
Example:
|
399
|
+
>>> firmware = FirmwareModel(box="1.0.0", central="2.0.0", centralradio="3.0.0")
|
400
|
+
>>> central_plug = CentralPlugModel(
|
401
|
+
... name="Central Plug 1",
|
402
|
+
... serial="123456789",
|
403
|
+
... vendor="VendorName",
|
404
|
+
... firmwares=firmware
|
405
|
+
... )
|
406
|
+
>>> print(central_plug.name)
|
407
|
+
Central Plug 1
|
408
|
+
>>> print(central_plug.serial)
|
409
|
+
123456789
|
410
|
+
>>> print(central_plug.vendor)
|
411
|
+
VendorName
|
412
|
+
>>> print(central_plug.firmwares.box)
|
413
|
+
1.0.0
|
414
|
+
|
415
|
+
"""
|
416
|
+
|
417
|
+
name: str | None = None
|
418
|
+
serial: str | None = None
|
419
|
+
vendor: str | None = None
|
420
|
+
firmwares: FirmwareModel | None = None
|
421
|
+
|
422
|
+
|
423
|
+
@dataclass
|
424
|
+
class Group(CamelCaseModel):
|
425
|
+
"""Represents a Group model.
|
426
|
+
|
427
|
+
Attributes:
|
428
|
+
name (str | None): The name of the group. Defaults to None.
|
429
|
+
index (int | None): The index of the group. Defaults to None.
|
430
|
+
input_delay (int | None): The input delay of the group, aliased as 'inputDelay'. Defaults to None.
|
431
|
+
output_delay (int | None): The output delay of the group, aliased as 'outputDelay'. Defaults to None.
|
432
|
+
|
433
|
+
Example:
|
434
|
+
>>> group = Group(name="Group A", index=1, input_delay=10, output_delay=20)
|
435
|
+
>>> print(group.name)
|
436
|
+
Group A
|
437
|
+
>>> print(group.index)
|
438
|
+
1
|
439
|
+
>>> print(group.input_delay)
|
440
|
+
10
|
441
|
+
>>> print(group.output_delay)
|
442
|
+
20
|
443
|
+
|
444
|
+
"""
|
445
|
+
|
446
|
+
name: str | None = None
|
447
|
+
index: int | None = None
|
448
|
+
input_delay: int | None = field(default=None, metadata={"alias": "inputDelay"})
|
449
|
+
output_delay: int | None = field(default=None, metadata={"alias": "outputDelay"})
|
450
|
+
|
451
|
+
|
452
|
+
@dataclass
|
453
|
+
class ConfAnomaliesModel(CamelCaseModel):
|
454
|
+
"""ConfAnomaliesModel is a data model that represents various configuration anomalies in a system.
|
455
|
+
|
456
|
+
Attributes:
|
457
|
+
radio_alert (bool | None): Indicates if there is a radio alert. Alias: "radioAlert".
|
458
|
+
power_supply_alert (bool | None): Indicates if there is a power supply alert. Alias: "powerSupplyAlert".
|
459
|
+
autoprotection_mechanical_alert (bool | None): Indicates if there is an autoprotection mechanical alert. Alias: "autoprotectionMechanicalAlert".
|
460
|
+
loop_alert (bool | None): Indicates if there is a loop alert. Alias: "loopAlert".
|
461
|
+
mask_alert (bool | None): Indicates if there is a mask alert. Alias: "maskAlert".
|
462
|
+
sensor_alert (bool | None): Indicates if there is a sensor alert. Alias: "sensorAlert".
|
463
|
+
media_gsm_alert (bool | None): Indicates if there is a GSM media alert. Alias: "mediaGSMAlert".
|
464
|
+
media_rtc_alert (bool | None): Indicates if there is an RTC media alert. Alias: "mediaRTCAlert".
|
465
|
+
media_adsl_alert (bool | None): Indicates if there is an ADSL media alert. Alias: "mediaADSLAlert".
|
466
|
+
out_of_order_alert (bool | None): Indicates if there is an out of order alert. Alias: "outOfOrderAlert".
|
467
|
+
main_power_supply_alert (bool | None): Indicates if there is a main power supply alert. Alias: "mainPowerSupplyAlert".
|
468
|
+
secondary_power_supply_alert (bool | None): Indicates if there is a secondary power supply alert. Alias: "secondaryPowerSupplyAlert".
|
469
|
+
default_media_alert (bool | None): Indicates if there is a default media alert. Alias: "defaultMediaAlert".
|
470
|
+
autoprotection_wired_alert (bool | None): Indicates if there is an autoprotection wired alert. Alias: "autoprotectionWiredAlert".
|
471
|
+
|
472
|
+
Example:
|
473
|
+
>>> anomalies = ConfAnomaliesModel(
|
474
|
+
... radio_alert=True,
|
475
|
+
... power_supply_alert=False,
|
476
|
+
... autoprotection_mechanical_alert=True,
|
477
|
+
... loop_alert=False,
|
478
|
+
... mask_alert=True,
|
479
|
+
... sensor_alert=False,
|
480
|
+
... media_gsm_alert=True,
|
481
|
+
... media_rtc_alert=False,
|
482
|
+
... media_adsl_alert=True,
|
483
|
+
... out_of_order_alert=False,
|
484
|
+
... main_power_supply_alert=True,
|
485
|
+
... secondary_power_supply_alert=False,
|
486
|
+
... default_media_alert=True,
|
487
|
+
... autoprotection_wired_alert=False
|
488
|
+
... )
|
489
|
+
>>> print(anomalies.radio_alert)
|
490
|
+
True
|
491
|
+
>>> print(anomalies.power_supply_alert)
|
492
|
+
False
|
493
|
+
|
494
|
+
"""
|
495
|
+
|
496
|
+
radio_alert: bool | None = field(default=None, metadata={"alias": "radioAlert"})
|
497
|
+
power_supply_alert: bool | None = field(
|
498
|
+
default=None, metadata={"alias": "powerSupplyAlert"}
|
499
|
+
)
|
500
|
+
autoprotection_mechanical_alert: bool | None = field(
|
501
|
+
default=None, metadata={"alias": "autoprotectionMechanicalAlert"}
|
502
|
+
)
|
503
|
+
loop_alert: bool | None = field(default=None, metadata={"alias": "loopAlert"})
|
504
|
+
mask_alert: bool | None = field(default=None, metadata={"alias": "maskAlert"})
|
505
|
+
sensor_alert: bool | None = field(default=None, metadata={"alias": "sensorAlert"})
|
506
|
+
media_gsm_alert: bool | None = field(
|
507
|
+
default=None, metadata={"alias": "mediaGSMAlert"}
|
508
|
+
)
|
509
|
+
media_rtc_alert: bool | None = field(
|
510
|
+
default=None, metadata={"alias": "mediaRTCAlert"}
|
511
|
+
)
|
512
|
+
media_adsl_alert: bool | None = field(
|
513
|
+
default=None, metadata={"alias": "mediaADSLAlert"}
|
514
|
+
)
|
515
|
+
out_of_order_alert: bool | None = field(
|
516
|
+
default=None, metadata={"alias": "outOfOrderAlert"}
|
517
|
+
)
|
518
|
+
main_power_supply_alert: bool | None = field(
|
519
|
+
default=None, metadata={"alias": "mainPowerSupplyAlert"}
|
520
|
+
)
|
521
|
+
secondary_power_supply_alert: bool | None = field(
|
522
|
+
default=None, metadata={"alias": "secondaryPowerSupplyAlert"}
|
523
|
+
)
|
524
|
+
default_media_alert: bool | None = field(
|
525
|
+
default=None, metadata={"alias": "defaultMediaAlert"}
|
526
|
+
)
|
527
|
+
autoprotection_wired_alert: bool | None = field(
|
528
|
+
default=None, metadata={"alias": "autoprotectionWiredAlert"}
|
529
|
+
)
|
530
|
+
|
531
|
+
|
532
|
+
@dataclass
|
533
|
+
class SensorModel(CamelCaseModel):
|
534
|
+
"""SensorModel represents the data structure for a sensor.
|
535
|
+
|
536
|
+
Attributes:
|
537
|
+
uid (str | None): Unique identifier for the sensor.
|
538
|
+
type (int | None): Type of the sensor.
|
539
|
+
gamme (int | None): Range or category of the sensor.
|
540
|
+
group (int | None): Group to which the sensor belongs.
|
541
|
+
index (int | None): Index of the sensor.
|
542
|
+
label (str | None): Label or name of the sensor.
|
543
|
+
serial (str | None): Serial number of the sensor.
|
544
|
+
is_video (bool | None): Indicates if the sensor is a video sensor (alias: isVideo).
|
545
|
+
ref_code (str | None): Reference code of the sensor (alias: refCode).
|
546
|
+
subtype (int | None): Subtype of the sensor.
|
547
|
+
anomalies (ConfAnomaliesModel | None): Configuration anomalies associated with the sensor.
|
548
|
+
inhibited (bool | None): Indicates if the sensor is inhibited.
|
549
|
+
can_inhibit (bool | None): Indicates if the sensor can be inhibited (alias: canInhibit).
|
550
|
+
|
551
|
+
Example:
|
552
|
+
>>> anomalies = ConfAnomaliesModel(radio_alert=True)
|
553
|
+
>>> sensor = SensorModel(
|
554
|
+
... uid="12345",
|
555
|
+
... type=1,
|
556
|
+
... gamme=2,
|
557
|
+
... group=3,
|
558
|
+
... index=4,
|
559
|
+
... label="Sensor 1",
|
560
|
+
... serial="SN12345",
|
561
|
+
... is_video=True,
|
562
|
+
... ref_code="RC123",
|
563
|
+
... subtype=5,
|
564
|
+
... anomalies=anomalies,
|
565
|
+
... inhibited=False,
|
566
|
+
... can_inhibit=True
|
567
|
+
... )
|
568
|
+
>>> print(sensor.uid)
|
569
|
+
12345
|
570
|
+
|
571
|
+
"""
|
572
|
+
|
573
|
+
uid: str | None = None
|
574
|
+
type: int | None = None
|
575
|
+
gamme: int | None = None
|
576
|
+
group: int | None = None
|
577
|
+
index: int | None = None
|
578
|
+
label: str | None = None
|
579
|
+
serial: str | None = None
|
580
|
+
is_video: bool | None = field(default=None, metadata={"alias": "isVideo"})
|
581
|
+
ref_code: str | None = field(default=None, metadata={"alias": "refCode"})
|
582
|
+
subtype: int | None = None
|
583
|
+
anomalies: ConfAnomaliesModel | None = None
|
584
|
+
inhibited: bool | None = None
|
585
|
+
can_inhibit: bool | None = field(default=None, metadata={"alias": "canInhibit"})
|
586
|
+
|
587
|
+
|
588
|
+
@dataclass
|
589
|
+
class Cameras(SensorModel):
|
590
|
+
"""Cameras model representing a sensor with an installation date, inheriting from SensorModel.
|
591
|
+
|
592
|
+
Attributes:
|
593
|
+
installation_date (datetime | None): The date when the camera was installed.
|
594
|
+
Defaults to None. This attribute is aliased as 'installationDate' in metadata.
|
595
|
+
|
596
|
+
Example:
|
597
|
+
>>> camera = Cameras(
|
598
|
+
... uid="12345",
|
599
|
+
... type=1,
|
600
|
+
... gamme=2,
|
601
|
+
... group=3,
|
602
|
+
... index=4,
|
603
|
+
... label="Camera 1",
|
604
|
+
... serial="SN12345",
|
605
|
+
... is_video=True,
|
606
|
+
... ref_code="RC123",
|
607
|
+
... subtype=5,
|
608
|
+
... anomalies=None,
|
609
|
+
... inhibited=False,
|
610
|
+
... can_inhibit=True,
|
611
|
+
... installation_date=datetime(2023, 10, 1)
|
612
|
+
... )
|
613
|
+
>>> print(camera.installation_date)
|
614
|
+
2023-10-01 00:00:00
|
615
|
+
|
616
|
+
"""
|
617
|
+
|
618
|
+
installation_date: datetime | None = field(
|
619
|
+
default=None, metadata={"alias": "installationDate"}
|
620
|
+
)
|
621
|
+
|
622
|
+
|
623
|
+
@dataclass
|
624
|
+
class TransceiverModel(SensorModel):
|
625
|
+
"""TransceiverModel represents a model for a transceiver device, inheriting from SensorModel.
|
626
|
+
|
627
|
+
Attributes:
|
628
|
+
firmwares (FirmwareModel | None): An optional attribute that holds the firmware information associated with the transceiver.
|
629
|
+
|
630
|
+
Example:
|
631
|
+
>>> firmware = FirmwareModel(box="1.0.0", central="2.0.0", centralradio="3.0.0")
|
632
|
+
>>> transceiver = TransceiverModel(
|
633
|
+
... uid="12345",
|
634
|
+
... type=1,
|
635
|
+
... gamme=2,
|
636
|
+
... group=3,
|
637
|
+
... index=4,
|
638
|
+
... label="Transceiver 1",
|
639
|
+
... serial="SN12345",
|
640
|
+
... is_video=True,
|
641
|
+
... ref_code="RC123",
|
642
|
+
... subtype=5,
|
643
|
+
... anomalies=None,
|
644
|
+
... inhibited=False,
|
645
|
+
... can_inhibit=True,
|
646
|
+
... firmwares=firmware
|
647
|
+
... )
|
648
|
+
>>> print(transceiver.uid)
|
649
|
+
12345
|
650
|
+
|
651
|
+
"""
|
652
|
+
|
653
|
+
firmwares: FirmwareModel | None = None
|
654
|
+
|
655
|
+
|
656
|
+
@dataclass
|
657
|
+
class TransmitterModel(SensorModel):
|
658
|
+
"""TransmitterModel represents a model for a transmitter device, inheriting from SensorModel.
|
659
|
+
|
660
|
+
Attributes:
|
661
|
+
firmwares (FirmwareModel | None): The firmware associated with the transmitter, if any.
|
662
|
+
is_plug (bool | None): Indicates whether the transmitter is a plug, with metadata alias "isPlug".
|
663
|
+
|
664
|
+
Example:
|
665
|
+
>>> firmware = FirmwareModel(box="1.0.0", central="2.0.0", centralradio="3.0.0")
|
666
|
+
>>> transmitter = TransmitterModel(
|
667
|
+
... uid="12345",
|
668
|
+
... type=1,
|
669
|
+
... gamme=2,
|
670
|
+
... group=3,
|
671
|
+
... index=4,
|
672
|
+
... label="Transmitter 1",
|
673
|
+
... serial="SN12345",
|
674
|
+
... is_video=True,
|
675
|
+
... ref_code="RC123",
|
676
|
+
... subtype=5,
|
677
|
+
... anomalies=None,
|
678
|
+
... inhibited=False,
|
679
|
+
... can_inhibit=True,
|
680
|
+
... firmwares=firmware,
|
681
|
+
... is_plug=True
|
682
|
+
... )
|
683
|
+
>>> print(transmitter.uid)
|
684
|
+
12345
|
685
|
+
|
686
|
+
"""
|
687
|
+
|
688
|
+
firmwares: FirmwareModel | None = None
|
689
|
+
is_plug: bool | None = field(default=None, metadata={"alias": "isPlug"})
|
690
|
+
|
691
|
+
|
692
|
+
@dataclass
|
693
|
+
class CentralInformation(CamelCaseModel):
|
694
|
+
"""CentralInformation model represents the central unit's configuration and status information.
|
695
|
+
|
696
|
+
Attributes:
|
697
|
+
has_plug (bool | None): Indicates if the central unit has a plug. Alias: "hasPlug".
|
698
|
+
plug_gsm (bool | None): Indicates if the central unit has a GSM plug. Alias: "plugGSM".
|
699
|
+
plug_rtc (bool | None): Indicates if the central unit has an RTC plug. Alias: "plugRTC".
|
700
|
+
plug_adsl (bool | None): Indicates if the central unit has an ADSL plug. Alias: "plugADSL".
|
701
|
+
anomalies (ConfAnomaliesModel | None): Represents the configuration anomalies of the central unit.
|
702
|
+
firmwares (FirmwareModel | None): Represents the firmware information of the central unit.
|
703
|
+
relay_card (bool | None): Indicates if the central unit has a relay card. Alias: "relayCard".
|
704
|
+
can_inhibit (bool | None): Indicates if the central unit can be inhibited. Alias: "canInhibit".
|
705
|
+
parameter_gsm_saved (bool | None): Indicates if the GSM parameters are saved. Alias: "parameterGsmSaved".
|
706
|
+
|
707
|
+
Example:
|
708
|
+
>>> anomalies = ConfAnomaliesModel(radio_alert=True)
|
709
|
+
>>> firmware = FirmwareModel(box="1.0.0", central="2.0.0", centralradio="3.0.0")
|
710
|
+
>>> central_info = CentralInformation(
|
711
|
+
... has_plug=True,
|
712
|
+
... plug_gsm=True,
|
713
|
+
... plug_rtc=False,
|
714
|
+
... plug_adsl=True,
|
715
|
+
... anomalies=anomalies,
|
716
|
+
... firmwares=firmware,
|
717
|
+
... relay_card=True,
|
718
|
+
... can_inhibit=True,
|
719
|
+
... parameter_gsm_saved=False
|
720
|
+
... )
|
721
|
+
>>> print(central_info.has_plug)
|
722
|
+
True
|
723
|
+
|
724
|
+
"""
|
725
|
+
|
726
|
+
has_plug: bool | None = field(default=None, metadata={"alias": "hasPlug"})
|
727
|
+
plug_gsm: bool | None = field(default=None, metadata={"alias": "plugGSM"})
|
728
|
+
plug_rtc: bool | None = field(default=None, metadata={"alias": "plugRTC"})
|
729
|
+
plug_adsl: bool | None = field(default=None, metadata={"alias": "plugADSL"})
|
730
|
+
anomalies: ConfAnomaliesModel | None = None
|
731
|
+
firmwares: FirmwareModel | None = None
|
732
|
+
relay_card: bool | None = field(default=None, metadata={"alias": "relayCard"})
|
733
|
+
can_inhibit: bool | None = field(default=None, metadata={"alias": "canInhibit"})
|
734
|
+
parameter_gsm_saved: bool | None = field(
|
735
|
+
default=None, metadata={"alias": "parameterGsmSaved"}
|
736
|
+
)
|
737
|
+
|
738
|
+
|
739
|
+
@dataclass
|
740
|
+
class BoxModel(CamelCaseModel):
|
741
|
+
"""BoxModel represents a model for a box with various attributes.
|
742
|
+
|
743
|
+
Attributes:
|
744
|
+
name (str | None): The name of the box. Defaults to None.
|
745
|
+
serial (str | None): The serial number of the box. Defaults to None.
|
746
|
+
vendor (str | None): The vendor of the box. Defaults to None.
|
747
|
+
firmwares (FirmwareModel | None): The firmware model associated with the box. Defaults to None.
|
748
|
+
|
749
|
+
Example:
|
750
|
+
>>> firmware = FirmwareModel(box="1.0.0", central="2.0.0", centralradio="3.0.0")
|
751
|
+
>>> box = BoxModel(name="Box 1", serial="123456789", vendor="VendorName", firmwares=firmware)
|
752
|
+
>>> print(box.name)
|
753
|
+
Box 1
|
754
|
+
|
755
|
+
"""
|
756
|
+
|
757
|
+
name: str | None = None
|
758
|
+
serial: str | None = None
|
759
|
+
vendor: str | None = None
|
760
|
+
firmwares: FirmwareModel | None = None
|
761
|
+
|
762
|
+
|
763
|
+
@dataclass
|
764
|
+
class AlarmModel(CamelCaseModel):
|
765
|
+
"""AlarmModel represents the configuration and state of an alarm system.
|
766
|
+
|
767
|
+
Attributes:
|
768
|
+
box (BoxModel | None): The box model associated with the alarm system.
|
769
|
+
plug (CentralPlugModel | None): The central plug model for the alarm system.
|
770
|
+
tls (bool | None): Indicates if TLS (Transport Layer Security) is enabled.
|
771
|
+
name (str | None): The name of the alarm system.
|
772
|
+
central (CentralPlugModel | None): The central plug model for the alarm system.
|
773
|
+
force_push_config (bool | None): Indicates if the configuration should be forcefully pushed.
|
774
|
+
This attribute is aliased as "forcePushConfig".
|
775
|
+
|
776
|
+
Example:
|
777
|
+
>>> box = BoxModel(name="Box 1", serial="123456789", vendor="VendorName")
|
778
|
+
>>> plug = CentralPlugModel(name="Central Plug 1", serial="987654321", vendor="VendorName")
|
779
|
+
>>> alarm = AlarmModel(box=box, plug=plug, tls=True, name="Home Alarm", central=plug, force_push_config=True)
|
780
|
+
>>> print(alarm.name)
|
781
|
+
Home Alarm
|
782
|
+
|
783
|
+
"""
|
784
|
+
|
785
|
+
box: BoxModel | None = None
|
786
|
+
plug: CentralPlugModel | None = None
|
787
|
+
tls: bool | None = None
|
788
|
+
name: str | None = None
|
789
|
+
central: CentralPlugModel | None = None
|
790
|
+
force_push_config: bool | None = field(
|
791
|
+
default=None, metadata={"alias": "forcePushConfig"}
|
792
|
+
)
|
793
|
+
|
794
|
+
|
795
|
+
@dataclass
|
796
|
+
class AlarmConfiguration(CamelCaseModel):
|
797
|
+
"""AlarmConfiguration model represents the configuration of an alarm system.
|
798
|
+
|
799
|
+
Attributes:
|
800
|
+
alarm (AlarmModel | None): The alarm model associated with the configuration.
|
801
|
+
groups (list[Group] | None): A list of groups associated with the alarm configuration.
|
802
|
+
sirens (list[SensorModel] | None): A list of siren sensor models.
|
803
|
+
cameras (list[Cameras] | None): A list of camera models.
|
804
|
+
sensors (list[SensorModel] | None): A list of sensor models.
|
805
|
+
commands (list[SensorModel] | None): A list of command sensor models.
|
806
|
+
reading_date (datetime | None): The date when the configuration was read, aliased as "readingDate".
|
807
|
+
transceivers (list[TransceiverModel] | None): A list of transceiver models.
|
808
|
+
transmitters (list[TransmitterModel] | None): A list of transmitter models.
|
809
|
+
grp_marche_presence (list[int] | None): A list of group marche presence, aliased as "grpMarchePresence".
|
810
|
+
installation_state (int | None): The state of the installation, aliased as "installationState".
|
811
|
+
central_information (CentralInformation | None): Information about the central unit, aliased as "centralInformation".
|
812
|
+
grp_marche_partielle1 (list[int] | None): A list of group marche partielle 1, aliased as "grpMarchePartielle1".
|
813
|
+
grp_marche_partielle2 (list[int] | None): A list of group marche partielle 2, aliased as "grpMarchePartielle2".
|
814
|
+
|
815
|
+
Example:
|
816
|
+
>>> alarm_config = AlarmConfiguration(
|
817
|
+
... alarm=AlarmModel(name="Home Alarm"),
|
818
|
+
... groups=[Group(name="Group A", index=1)],
|
819
|
+
... sirens=[SensorModel(uid="12345", type=1)],
|
820
|
+
... cameras=[Cameras(uid="67890", type=2)],
|
821
|
+
... sensors=[SensorModel(uid="54321", type=3)],
|
822
|
+
... commands=[SensorModel(uid="98765", type=4)],
|
823
|
+
... reading_date=datetime(2023, 10, 1),
|
824
|
+
... transceivers=[TransceiverModel(uid="11223", type=5)],
|
825
|
+
... transmitters=[TransmitterModel(uid="44556", type=6)],
|
826
|
+
... grp_marche_presence=[1, 2, 3],
|
827
|
+
... installation_state=1,
|
828
|
+
... central_information=CentralInformation(has_plug=True),
|
829
|
+
... grp_marche_partielle1=[4, 5, 6],
|
830
|
+
... grp_marche_partielle2=[7, 8, 9]
|
831
|
+
... )
|
832
|
+
>>> print(alarm_config.alarm.name)
|
833
|
+
Home Alarm
|
834
|
+
|
835
|
+
"""
|
836
|
+
|
837
|
+
alarm: AlarmModel | None = None
|
838
|
+
# badges: list[] # Not yet implemented. No enough information in documentation
|
839
|
+
groups: list[Group] | None = None
|
840
|
+
sirens: list[SensorModel] | None = None
|
841
|
+
cameras: list[Cameras] | None = None
|
842
|
+
sensors: list[SensorModel] | None = None
|
843
|
+
commands: list[SensorModel] | None = None
|
844
|
+
reading_date: datetime | None = field(
|
845
|
+
default=None, metadata={"alias": "readingDate"}
|
846
|
+
)
|
847
|
+
transceivers: list[TransceiverModel] | None = None
|
848
|
+
transmitters: list[TransmitterModel] | None = None
|
849
|
+
grp_marche_presence: list[int] | None = field(
|
850
|
+
default=None, metadata={"alias": "grpMarchePresence"}
|
851
|
+
)
|
852
|
+
installation_state: int | None = field(
|
853
|
+
default=None, metadata={"alias": "installationState"}
|
854
|
+
)
|
855
|
+
central_information: CentralInformation | None = field(
|
856
|
+
default=None, metadata={"alias": "centralInformation"}
|
857
|
+
)
|
858
|
+
grp_marche_partielle1: list[int] | None = field(
|
859
|
+
default=None, metadata={"alias": "grpMarchePartielle1"}
|
860
|
+
)
|
861
|
+
grp_marche_partielle2: list[int] | None = field(
|
862
|
+
default=None, metadata={"alias": "grpMarchePartielle2"}
|
863
|
+
)
|
864
|
+
|
865
|
+
|
866
|
+
############################
|
867
|
+
# Data model for device list
|
868
|
+
############################
|
869
|
+
|
870
|
+
|
871
|
+
@dataclass
|
872
|
+
class DeviceInfos(CamelCaseModel):
|
873
|
+
"""DeviceInfos model represents the information of a device.
|
874
|
+
|
875
|
+
Attributes:
|
876
|
+
index (int): The index of the device.
|
877
|
+
label (str): The label or name of the device.
|
878
|
+
|
879
|
+
Example:
|
880
|
+
>>> device_info = DeviceInfos(index=1, label="Sensor 1")
|
881
|
+
>>> print(device_info.index)
|
882
|
+
1
|
883
|
+
>>> print(device_info.label)
|
884
|
+
Sensor 1
|
885
|
+
|
886
|
+
"""
|
887
|
+
|
888
|
+
index: int
|
889
|
+
label: str
|
890
|
+
|
891
|
+
|
892
|
+
@dataclass
|
893
|
+
class DeviceList(CamelCaseModel):
|
894
|
+
"""DeviceList model representing a collection of various device types.
|
895
|
+
|
896
|
+
Attributes:
|
897
|
+
cameras (list[DeviceInfos] | None): A list of camera devices or None if not available.
|
898
|
+
commands (list[DeviceInfos] | None): A list of command devices or None if not available.
|
899
|
+
sensors (list[DeviceInfos] | None): A list of sensor devices or None if not available.
|
900
|
+
sirens (list[DeviceInfos] | None): A list of siren devices or None if not available.
|
901
|
+
transmitters (list[DeviceInfos] | None): A list of transmitter devices or None if not available.
|
902
|
+
|
903
|
+
Example:
|
904
|
+
>>> device_list = DeviceList(
|
905
|
+
... cameras=[DeviceInfos(index=1, label="Camera 1")],
|
906
|
+
... commands=[DeviceInfos(index=2, label="Command 1")],
|
907
|
+
... sensors=[DeviceInfos(index=3, label="Sensor 1")],
|
908
|
+
... sirens=[DeviceInfos(index=4, label="Siren 1")],
|
909
|
+
... transmitters=[DeviceInfos(index=5, label="Transmitter 1")]
|
910
|
+
... )
|
911
|
+
>>> print(device_list.cameras[0].label)
|
912
|
+
Camera 1
|
913
|
+
|
914
|
+
"""
|
915
|
+
|
916
|
+
cameras: list[DeviceInfos] | None = None
|
917
|
+
commands: list[DeviceInfos] | None = None
|
918
|
+
sensors: list[DeviceInfos] | None = None
|
919
|
+
sirens: list[DeviceInfos] | None = None
|
920
|
+
transmitters: list[DeviceInfos] | None = None
|
921
|
+
|
922
|
+
|
923
|
+
###############################
|
924
|
+
# Data model for system details
|
925
|
+
###############################
|
926
|
+
|
927
|
+
|
928
|
+
@dataclass
|
929
|
+
class SystemDetails(CamelCaseModel):
|
930
|
+
"""SystemDetails model represents the details of a system with various attributes.
|
931
|
+
|
932
|
+
Attributes:
|
933
|
+
device_type (str): The type of the device.
|
934
|
+
firmware_version (str): The firmware version of the device.
|
935
|
+
ip_address (str): The IP address of the device.
|
936
|
+
ipoda_version (str): The version of the IPODA.
|
937
|
+
mode (str): The mode of the device.
|
938
|
+
first_vocal_contact (str): The first vocal contact information.
|
939
|
+
is_alarm_file_present (bool): Indicates if the alarm file is present.
|
940
|
+
is_mjpeg_archive_video_supported (str): Indicates if MJPEG archive video is supported.
|
941
|
+
is_mass_storage_present (str): Indicates if mass storage is present.
|
942
|
+
is_remote_startup_shutdown_allowed (str): Indicates if remote startup/shutdown is allowed.
|
943
|
+
is_video_password_protected (str): Indicates if the video is password protected.
|
944
|
+
|
945
|
+
Example:
|
946
|
+
>>> system_details = SystemDetails(
|
947
|
+
... device_type="Camera",
|
948
|
+
... firmware_version="1.0.0",
|
949
|
+
... ip_address="192.168.1.1",
|
950
|
+
... ipoda_version="2.0.0",
|
951
|
+
... mode="Active",
|
952
|
+
... first_vocal_contact="2023-10-01T12:00:00Z",
|
953
|
+
... is_alarm_file_present=True,
|
954
|
+
... is_mjpeg_archive_video_supported="Yes",
|
955
|
+
... is_mass_storage_present="Yes",
|
956
|
+
... is_remote_startup_shutdown_allowed="No",
|
957
|
+
... is_video_password_protected="Yes"
|
958
|
+
... )
|
959
|
+
>>> print(system_details.device_type)
|
960
|
+
Camera
|
961
|
+
|
962
|
+
"""
|
963
|
+
|
964
|
+
device_type: str = field(metadata={"alias": "DeviceType"})
|
965
|
+
firmware_version: str = field(metadata={"alias": "FirmwareVersion"})
|
966
|
+
ip_address: str = field(metadata={"alias": "IpAddress"})
|
967
|
+
ipoda_version: str = field(metadata={"alias": "IpodaVersion"})
|
968
|
+
mode: str = field(metadata={"alias": "Mode"})
|
969
|
+
first_vocal_contact: str = field(metadata={"alias": "FirstVocalContact"})
|
970
|
+
is_alarm_file_present: bool = field(metadata={"alias": "IsAlarmFilePresent"})
|
971
|
+
is_mjpeg_archive_video_supported: str = field(
|
972
|
+
metadata={"alias": "IsMJPEGArchiveVideoSupported"}
|
973
|
+
)
|
974
|
+
is_mass_storage_present: str = field(metadata={"alias": "IsMassStoragePresent"})
|
975
|
+
is_remote_startup_shutdown_allowed: str = field(
|
976
|
+
metadata={"alias": "IsRemoteStartupShutdownAllowed"}
|
977
|
+
)
|
978
|
+
is_video_password_protected: str = field(
|
979
|
+
metadata={"alias": "IsVideoPasswordProtected"}
|
980
|
+
)
|
981
|
+
|
982
|
+
|
983
|
+
##############################
|
984
|
+
# Data model for system status
|
985
|
+
##############################
|
986
|
+
|
987
|
+
|
988
|
+
@dataclass
|
989
|
+
class SystemStatus(CamelCaseModel):
|
990
|
+
"""SystemStatus represents the status of a system with various attributes.
|
991
|
+
|
992
|
+
Attributes:
|
993
|
+
status (str): The current status of the system.
|
994
|
+
activated_groups (list[int]): A list of IDs representing the activated groups within the system.
|
995
|
+
|
996
|
+
Example:
|
997
|
+
>>> system_status = SystemStatus(status="Active", activated_groups=[1, 2, 3])
|
998
|
+
>>> print(system_status.status)
|
999
|
+
Active
|
1000
|
+
>>> print(system_status.activated_groups)
|
1001
|
+
[1, 2, 3]
|
1002
|
+
|
1003
|
+
"""
|
1004
|
+
|
1005
|
+
status: str
|
1006
|
+
activated_groups: list[int]
|
1007
|
+
|
1008
|
+
|
1009
|
+
#######################################
|
1010
|
+
# Data model for anomalies informations
|
1011
|
+
#######################################
|
1012
|
+
|
1013
|
+
|
1014
|
+
@dataclass
|
1015
|
+
class AnomalyName(CamelCaseModel):
|
1016
|
+
"""AnomalyName model representing an anomaly with an identifier and a name.
|
1017
|
+
|
1018
|
+
Attributes:
|
1019
|
+
id (int): The unique identifier for the anomaly.
|
1020
|
+
name (str): The name of the anomaly.
|
1021
|
+
|
1022
|
+
Example:
|
1023
|
+
>>> anomaly = AnomalyName(id=1, name="Low Battery")
|
1024
|
+
>>> print(anomaly.id)
|
1025
|
+
1
|
1026
|
+
>>> print(anomaly.name)
|
1027
|
+
Low Battery
|
1028
|
+
|
1029
|
+
"""
|
1030
|
+
|
1031
|
+
id: int
|
1032
|
+
name: str
|
1033
|
+
|
1034
|
+
|
1035
|
+
@dataclass
|
1036
|
+
class AnomalyDetail(CamelCaseModel):
|
1037
|
+
"""AnomalyDetail represents detailed information about an anomaly.
|
1038
|
+
|
1039
|
+
Attributes:
|
1040
|
+
anomaly_names (list[AnomalyName]): A list of anomaly names associated with this detail.
|
1041
|
+
serial (str | None): An optional serial number associated with the anomaly.
|
1042
|
+
index (int | None): An optional index value for the anomaly.
|
1043
|
+
group (int | None): An optional group identifier for the anomaly.
|
1044
|
+
label (str | None): An optional label describing the anomaly.
|
1045
|
+
|
1046
|
+
Example:
|
1047
|
+
>>> anomaly_names = [AnomalyName(id=1, name="Low Battery")]
|
1048
|
+
>>> anomaly_detail = AnomalyDetail(
|
1049
|
+
... anomaly_names=anomaly_names,
|
1050
|
+
... serial="SN12345",
|
1051
|
+
... index=1,
|
1052
|
+
... group=2,
|
1053
|
+
... label="Sensor Anomaly"
|
1054
|
+
... )
|
1055
|
+
>>> print(anomaly_detail.serial)
|
1056
|
+
SN12345
|
1057
|
+
|
1058
|
+
"""
|
1059
|
+
|
1060
|
+
anomaly_names: list[AnomalyName]
|
1061
|
+
serial: str | None = None
|
1062
|
+
index: int | None = None
|
1063
|
+
group: int | None = None
|
1064
|
+
label: str | None = None
|
1065
|
+
|
1066
|
+
|
1067
|
+
@dataclass
|
1068
|
+
class Anomalies(CamelCaseModel):
|
1069
|
+
"""A model representing anomalies detected in various devices.
|
1070
|
+
|
1071
|
+
Attributes:
|
1072
|
+
created_at (datetime): The timestamp when the anomalies were created.
|
1073
|
+
sensors (list[AnomalyDetail] | None): A list of anomaly details for sensors, or None if no anomalies.
|
1074
|
+
badges (list[AnomalyDetail] | None): A list of anomaly details for badges, or None if no anomalies.
|
1075
|
+
sirens (list[AnomalyDetail] | None): A list of anomaly details for sirens, or None if no anomalies.
|
1076
|
+
cameras (list[AnomalyDetail] | None): A list of anomaly details for cameras, or None if no anomalies.
|
1077
|
+
commands (list[AnomalyDetail] | None): A list of anomaly details for commands, or None if no anomalies.
|
1078
|
+
transceivers (list[AnomalyDetail] | None): A list of anomaly details for transceivers, or None if no anomalies.
|
1079
|
+
transmitters (list[AnomalyDetail] | None): A list of anomaly details for transmitters, or None if no anomalies.
|
1080
|
+
central (list[AnomalyDetail] | None): A list of anomaly details for central devices, or None if no anomalies.
|
1081
|
+
|
1082
|
+
Methods:
|
1083
|
+
from_dict(data: dict) -> Anomalies:
|
1084
|
+
Create an instance of Anomalies from a dictionary.
|
1085
|
+
|
1086
|
+
Example:
|
1087
|
+
>>> data = {
|
1088
|
+
... "created_at": "2025-02-16T10:15:12.625165",
|
1089
|
+
... "sensors": [
|
1090
|
+
... {
|
1091
|
+
... "serial": "SN12345",
|
1092
|
+
... "index": 1,
|
1093
|
+
... "group": 2,
|
1094
|
+
... "label": "Sensor Anomaly",
|
1095
|
+
... "anomaly_names": [{"id": 1, "name": "Low Battery"}]
|
1096
|
+
... }
|
1097
|
+
... ]
|
1098
|
+
... }
|
1099
|
+
>>> anomalies = Anomalies.from_dict(data)
|
1100
|
+
>>> print(anomalies.created_at)
|
1101
|
+
2025-02-16 10:15:12.625165+00:00
|
1102
|
+
>>> print(anomalies.sensors[0].serial)
|
1103
|
+
SN12345
|
1104
|
+
|
1105
|
+
"""
|
1106
|
+
|
1107
|
+
created_at: datetime
|
1108
|
+
sensors: list[AnomalyDetail] | None = None
|
1109
|
+
badges: list[AnomalyDetail] | None = None
|
1110
|
+
sirens: list[AnomalyDetail] | None = None
|
1111
|
+
cameras: list[AnomalyDetail] | None = None
|
1112
|
+
commands: list[AnomalyDetail] | None = None
|
1113
|
+
transceivers: list[AnomalyDetail] | None = None
|
1114
|
+
transmitters: list[AnomalyDetail] | None = None
|
1115
|
+
central: list[AnomalyDetail] | None = None
|
1116
|
+
|
1117
|
+
@classmethod
|
1118
|
+
def from_dict(cls, data: dict) -> Anomalies:
|
1119
|
+
"""Create an instance of Anomalies from a dictionary."""
|
1120
|
+
|
1121
|
+
def create_devices(device_data):
|
1122
|
+
return [
|
1123
|
+
AnomalyDetail(
|
1124
|
+
serial=data.get("serial"),
|
1125
|
+
index=data.get("index"),
|
1126
|
+
group=data.get("group"),
|
1127
|
+
label=data.get("label"),
|
1128
|
+
anomaly_names=[AnomalyName(**a) for a in data.get("anomaly_names")],
|
1129
|
+
)
|
1130
|
+
for data in device_data
|
1131
|
+
]
|
1132
|
+
|
1133
|
+
# Convert the created_at field to a datetime object with UTC timezone
|
1134
|
+
created_at = datetime.fromisoformat(data["created_at"]).replace(
|
1135
|
+
tzinfo=timezone.utc
|
1136
|
+
)
|
1137
|
+
|
1138
|
+
return cls(
|
1139
|
+
created_at=created_at,
|
1140
|
+
sensors=create_devices(data.get("sensors", [])),
|
1141
|
+
badges=create_devices(data.get("badges", [])),
|
1142
|
+
sirens=create_devices(data.get("sirens", [])),
|
1143
|
+
cameras=create_devices(data.get("cameras", [])),
|
1144
|
+
commands=create_devices(data.get("commands", [])),
|
1145
|
+
transceivers=create_devices(data.get("transceivers", [])),
|
1146
|
+
transmitters=create_devices(data.get("transmitters", [])),
|
1147
|
+
central=create_devices(data.get("central", [])),
|
1148
|
+
)
|
1149
|
+
|
1150
|
+
|
1151
|
+
############################
|
1152
|
+
# Data Model for webhooks
|
1153
|
+
############################
|
1154
|
+
|
1155
|
+
|
1156
|
+
@dataclass
|
1157
|
+
class WebhookSubscription(CamelCaseModel):
|
1158
|
+
"""Represents a subscription to webhook notifications.
|
1159
|
+
|
1160
|
+
Attributes:
|
1161
|
+
anomaly (bool): Indicates whether anomaly notifications are enabled.
|
1162
|
+
alert (bool): Indicates whether alert notifications are enabled.
|
1163
|
+
state (bool): Indicates whether state notifications are enabled.
|
1164
|
+
|
1165
|
+
Example:
|
1166
|
+
>>> subscription = WebhookSubscription(anomaly=True, alert=False, state=True)
|
1167
|
+
>>> print(subscription.anomaly)
|
1168
|
+
True
|
1169
|
+
|
1170
|
+
"""
|
1171
|
+
|
1172
|
+
anomaly: bool
|
1173
|
+
alert: bool
|
1174
|
+
state: bool
|
1175
|
+
|
1176
|
+
|
1177
|
+
@dataclass
|
1178
|
+
class Webhook(CamelCaseModel):
|
1179
|
+
"""Represents a Webhook model.
|
1180
|
+
|
1181
|
+
Attributes:
|
1182
|
+
transmitter_id (str): The unique identifier for the transmitter.
|
1183
|
+
webhook_url (str): The URL to which the webhook will send data.
|
1184
|
+
subscriptions (WebhookSubscription): The subscription details for the webhook.
|
1185
|
+
|
1186
|
+
Example:
|
1187
|
+
>>> subscription = WebhookSubscription(anomaly=True, alert=False, state=True)
|
1188
|
+
>>> webhook = Webhook(transmitter_id="12345", webhook_url="https://example.com/webhook", subscriptions=subscription)
|
1189
|
+
>>> print(webhook.transmitter_id)
|
1190
|
+
12345
|
1191
|
+
|
1192
|
+
"""
|
1193
|
+
|
1194
|
+
transmitter_id: str
|
1195
|
+
webhook_url: str
|
1196
|
+
subscriptions: WebhookSubscription
|
1197
|
+
|
1198
|
+
|
1199
|
+
@dataclass
|
1200
|
+
class WebHookNotificationDetail(CamelCaseModel):
|
1201
|
+
"""WebHookNotificationDetail model represents the details of a webhook notification.
|
1202
|
+
|
1203
|
+
Attributes:
|
1204
|
+
device_type (str): The type of the device.
|
1205
|
+
device_index (str): The index of the device.
|
1206
|
+
device_label (str | None): The label of the device, which is optional.
|
1207
|
+
|
1208
|
+
Example:
|
1209
|
+
>>> detail = WebHookNotificationDetail(device_type="Sensor", device_index="1", device_label="Front Door Sensor")
|
1210
|
+
>>> print(detail.device_type)
|
1211
|
+
Sensor
|
1212
|
+
>>> print(detail.device_index)
|
1213
|
+
1
|
1214
|
+
>>> print(detail.device_label)
|
1215
|
+
Front Door Sensor
|
1216
|
+
|
1217
|
+
"""
|
1218
|
+
|
1219
|
+
device_type: str
|
1220
|
+
device_index: str
|
1221
|
+
device_label: str | None = None
|
1222
|
+
|
1223
|
+
|
1224
|
+
@dataclass
|
1225
|
+
class WebHookNotificationUser(CamelCaseModel):
|
1226
|
+
"""WebHookNotificationUser represents a user who receives webhook notifications.
|
1227
|
+
|
1228
|
+
Attributes:
|
1229
|
+
username (str): The username of the user.
|
1230
|
+
user_type (str): The type of the user.
|
1231
|
+
|
1232
|
+
Example:
|
1233
|
+
>>> user = WebHookNotificationUser(username="Dark Vador", user_type="owner")
|
1234
|
+
>>> print(user.username)
|
1235
|
+
Dark Vador
|
1236
|
+
>>> print(detail.user_type)
|
1237
|
+
owner
|
1238
|
+
|
1239
|
+
"""
|
1240
|
+
|
1241
|
+
username: str
|
1242
|
+
user_type: str
|
1243
|
+
|
1244
|
+
|
1245
|
+
@dataclass
|
1246
|
+
class WebHookNotification(CamelCaseModel):
|
1247
|
+
"""A model representing a webhook notification.
|
1248
|
+
|
1249
|
+
Attributes:
|
1250
|
+
transmitter_id (str): The ID of the transmitter sending the notification.
|
1251
|
+
alarm_type (str): The type of alarm, determined based on the alarm code.
|
1252
|
+
alarm_code (str): The code representing the specific alarm.
|
1253
|
+
alarm_description (str): A description of the alarm.
|
1254
|
+
group_index (str): The index of the group associated with the alarm.
|
1255
|
+
detail (WebHookNotificationDetail): Detailed information about the webhook notification.
|
1256
|
+
Only during anomaly/alert notification.
|
1257
|
+
user (WebHookNotificationUser): The user who trigger the notification.
|
1258
|
+
Only during change state notification.
|
1259
|
+
date_time (datetime): The date and time when the notification was generated.
|
1260
|
+
|
1261
|
+
Methods:
|
1262
|
+
from_dict(data: dict) -> WebHookNotification:
|
1263
|
+
Create an instance of WebHookNotification from a dictionary.
|
1264
|
+
|
1265
|
+
Example:
|
1266
|
+
>>> data = {
|
1267
|
+
... "transmitter_id": "12345",
|
1268
|
+
... "alarm_code": "1130",
|
1269
|
+
... "alarm_description": "Intrusion detected",
|
1270
|
+
... "group_index": "01",
|
1271
|
+
... "detail": {
|
1272
|
+
... "device_type": "Sensor",
|
1273
|
+
... "device_index": "1",
|
1274
|
+
... "device_label": "Front Door Sensor"
|
1275
|
+
... },
|
1276
|
+
... "date_time": "2023-10-01T12:00:00Z"
|
1277
|
+
... }
|
1278
|
+
>>> notification = WebHookNotification.from_dict(data)
|
1279
|
+
>>> print(notification.transmitter_id)
|
1280
|
+
12345
|
1281
|
+
|
1282
|
+
"""
|
1283
|
+
|
1284
|
+
transmitter_id: str
|
1285
|
+
alarm_type: str # Not included in Diagral answer. Added with below function
|
1286
|
+
alarm_code: str
|
1287
|
+
alarm_description: str
|
1288
|
+
group_index: str
|
1289
|
+
detail: WebHookNotificationDetail
|
1290
|
+
user: WebHookNotificationUser
|
1291
|
+
date_time: datetime
|
1292
|
+
|
1293
|
+
@classmethod
|
1294
|
+
def from_dict(cls, data: dict) -> WebHookNotification:
|
1295
|
+
"""Create an instance of WebHookNotification from a dictionary."""
|
1296
|
+
|
1297
|
+
def alarm_type(alarm_code):
|
1298
|
+
"""Determine the type of alarm based on the alarm code."""
|
1299
|
+
ANOMALY_CODES = [
|
1300
|
+
1301,
|
1301
|
+
3301,
|
1302
|
+
1137,
|
1303
|
+
3137,
|
1304
|
+
1355,
|
1305
|
+
3355,
|
1306
|
+
1381,
|
1307
|
+
3381,
|
1308
|
+
1144,
|
1309
|
+
3144,
|
1310
|
+
1302,
|
1311
|
+
1384,
|
1312
|
+
1570,
|
1313
|
+
3570,
|
1314
|
+
1352,
|
1315
|
+
3352,
|
1316
|
+
1351,
|
1317
|
+
3351,
|
1318
|
+
1573,
|
1319
|
+
]
|
1320
|
+
ALERT_CODES = [
|
1321
|
+
1130,
|
1322
|
+
1110,
|
1323
|
+
1111,
|
1324
|
+
1117,
|
1325
|
+
1158,
|
1326
|
+
1139,
|
1327
|
+
1344,
|
1328
|
+
1120,
|
1329
|
+
1122,
|
1330
|
+
1159,
|
1331
|
+
1152,
|
1332
|
+
1154,
|
1333
|
+
1150,
|
1334
|
+
1140,
|
1335
|
+
1141,
|
1336
|
+
1142,
|
1337
|
+
1143,
|
1338
|
+
3391,
|
1339
|
+
1391,
|
1340
|
+
]
|
1341
|
+
STATUS_CODES = [1306, 3401, 3407, 1401, 1407]
|
1342
|
+
|
1343
|
+
if int(alarm_code) in ANOMALY_CODES:
|
1344
|
+
return "ANOMALY"
|
1345
|
+
if int(alarm_code) in ALERT_CODES:
|
1346
|
+
return "ALERT"
|
1347
|
+
if int(alarm_code) in STATUS_CODES:
|
1348
|
+
return "STATUS"
|
1349
|
+
return "UNKNOWN"
|
1350
|
+
|
1351
|
+
return cls(
|
1352
|
+
transmitter_id=data.get("transmitter_id"),
|
1353
|
+
alarm_type=alarm_type(data.get("alarm_code")),
|
1354
|
+
alarm_code=data.get("alarm_code"),
|
1355
|
+
alarm_description=data.get("alarm_description"),
|
1356
|
+
group_index=data.get("group_index"),
|
1357
|
+
detail=WebHookNotificationDetail(
|
1358
|
+
device_type=data.get("detail", {}).get("device_type", None),
|
1359
|
+
device_index=data.get("detail", {}).get("device_index", None),
|
1360
|
+
device_label=data.get("detail", {}).get("device_label", None),
|
1361
|
+
),
|
1362
|
+
user=WebHookNotificationUser(
|
1363
|
+
username=data.get("user", {}).get("username", None),
|
1364
|
+
user_type=data.get("user", {}).get("user_type", None),
|
1365
|
+
),
|
1366
|
+
date_time=datetime.fromisoformat(data["date_time"].replace("Z", "+00:00")),
|
1367
|
+
)
|
1368
|
+
|
1369
|
+
|
1370
|
+
############################
|
1371
|
+
# Data Model for automations
|
1372
|
+
############################
|
1373
|
+
|
1374
|
+
|
1375
|
+
@dataclass
|
1376
|
+
class Rude(CamelCaseModel):
|
1377
|
+
"""Rude model representing a device with a name, canal, and mode.
|
1378
|
+
|
1379
|
+
Attributes:
|
1380
|
+
name (str): The name of the device.
|
1381
|
+
canal (str): The canal associated with the device.
|
1382
|
+
mode (str): The mode of operation for the device. Must be one of {"ON", "PULSE", "SWITCH", "TIMER"}.
|
1383
|
+
|
1384
|
+
Methods:
|
1385
|
+
__post_init__(): Post-initialization processing to validate the mode attribute.
|
1386
|
+
|
1387
|
+
Example:
|
1388
|
+
>>> rude = Rude(name="Device1", canal="Canal1", mode="ON")
|
1389
|
+
>>> print(rude.name)
|
1390
|
+
Device1
|
1391
|
+
|
1392
|
+
"""
|
1393
|
+
|
1394
|
+
name: str
|
1395
|
+
canal: str
|
1396
|
+
mode: str
|
1397
|
+
|
1398
|
+
def __post_init__(self):
|
1399
|
+
"""Post-initialization processing to validate mode."""
|
1400
|
+
valid_modes = {"ON", "PULSE", "SWITCH", "TIMER"}
|
1401
|
+
if self.mode not in valid_modes:
|
1402
|
+
raise ValueError(f"mode must be one of {valid_modes}")
|
1403
|
+
|
1404
|
+
|
1405
|
+
@dataclass
|
1406
|
+
class Rudes(CamelCaseModel):
|
1407
|
+
"""Rudes model representing a collection of Rude instances.
|
1408
|
+
|
1409
|
+
Attributes:
|
1410
|
+
rudes (list[Rude]): A list of Rude objects.
|
1411
|
+
|
1412
|
+
Example:
|
1413
|
+
>>> rude1 = Rude(name="Device1", canal="Canal1", mode="ON")
|
1414
|
+
>>> rude2 = Rude(name="Device2", canal="Canal2", mode="PULSE")
|
1415
|
+
>>> rudes = Rudes(rudes=[rude1, rude2])
|
1416
|
+
>>> print(rudes.rudes[0].name)
|
1417
|
+
Device1
|
1418
|
+
|
1419
|
+
"""
|
1420
|
+
|
1421
|
+
rudes: list[Rude]
|
1422
|
+
|
1423
|
+
|
1424
|
+
###########################
|
1425
|
+
# Data Model for exceptions
|
1426
|
+
###########################
|
1427
|
+
|
1428
|
+
|
1429
|
+
@dataclass
|
1430
|
+
class ValidationError(CamelCaseModel):
|
1431
|
+
"""ValidationError model represents an error that occurs during validation.
|
1432
|
+
|
1433
|
+
Attributes:
|
1434
|
+
loc (list[str] | None): The location of the error, typically indicating the field or attribute that caused the error.
|
1435
|
+
message (str | None): A human-readable message describing the error.
|
1436
|
+
type (str | None): The type or category of the error.
|
1437
|
+
input (str | None): The input value that caused the error.
|
1438
|
+
url (str | None): A URL providing more information about the error.
|
1439
|
+
|
1440
|
+
Example:
|
1441
|
+
>>> error = ValidationError(
|
1442
|
+
... loc=["body", "username"],
|
1443
|
+
... message="Username is required",
|
1444
|
+
... type="value_error.missing",
|
1445
|
+
... input=None,
|
1446
|
+
... url="https://example.com/errors/username-required"
|
1447
|
+
... )
|
1448
|
+
>>> print(error.message)
|
1449
|
+
Username is required
|
1450
|
+
|
1451
|
+
"""
|
1452
|
+
|
1453
|
+
loc: list[str] | None = None
|
1454
|
+
message: str | None = None
|
1455
|
+
type: str | None = None
|
1456
|
+
input: str | None = None
|
1457
|
+
url: str | None = None
|
1458
|
+
|
1459
|
+
|
1460
|
+
@dataclass
|
1461
|
+
class HTTPValidationError(ValidationError):
|
1462
|
+
"""HTTPValidationError is a subclass of ValidationError that represents an HTTP validation error.
|
1463
|
+
|
1464
|
+
Attributes:
|
1465
|
+
detail (list[ValidationError] | None): A list of ValidationError instances or None, providing detailed information about the validation errors.
|
1466
|
+
|
1467
|
+
Example:
|
1468
|
+
>>> error_detail = ValidationError(
|
1469
|
+
... loc=["body", "username"],
|
1470
|
+
... message="Username is required",
|
1471
|
+
... type="value_error.missing",
|
1472
|
+
... input=None,
|
1473
|
+
... url="https://example.com/errors/username-required"
|
1474
|
+
... )
|
1475
|
+
>>> http_error = HTTPValidationError(detail=[error_detail])
|
1476
|
+
>>> print(http_error.detail[0].message)
|
1477
|
+
Username is required
|
1478
|
+
|
1479
|
+
"""
|
1480
|
+
|
1481
|
+
detail: list[ValidationError] | None = None
|
1482
|
+
|
1483
|
+
|
1484
|
+
@dataclass
|
1485
|
+
class HTTPErrorResponse(CamelCaseModel):
|
1486
|
+
"""HTTPErrorResponse is a model that represents an HTTP error response.
|
1487
|
+
|
1488
|
+
Attributes:
|
1489
|
+
detail (str): A detailed message describing the error.
|
1490
|
+
|
1491
|
+
Example:
|
1492
|
+
>>> error_response = HTTPErrorResponse(detail="Not Found")
|
1493
|
+
>>> print(error_response.detail)
|
1494
|
+
Not Found
|
1495
|
+
|
1496
|
+
"""
|
1497
|
+
|
1498
|
+
detail: str
|