pydiagral 1.4.0__py3-none-any.whl → 1.5.0b2__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/api.py CHANGED
@@ -7,7 +7,6 @@ retrieving system status, and controlling various aspects of the alarm system.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
- from datetime import datetime
11
10
  import logging
12
11
  import re
13
12
  import time
@@ -37,11 +36,12 @@ from .models import (
37
36
  Rudes,
38
37
  SystemDetails,
39
38
  SystemStatus,
39
+ TryConnectResult,
40
40
  Webhook,
41
41
  )
42
42
  from .utils import generate_hmac_signature
43
43
 
44
- _LOGGER = logging.getLogger(__name__)
44
+ _LOGGER: logging.Logger = logging.getLogger(__name__)
45
45
 
46
46
  # Minimum Python version: 3.10
47
47
 
@@ -84,17 +84,17 @@ class DiagralAPI:
84
84
  or not self.__is_valid_email(username)
85
85
  ):
86
86
  raise ConfigurationError("username must be a valid non-empty email address")
87
- self.username = username
87
+ self.username: str = username
88
88
 
89
89
  # Validate password
90
90
  if not password or not isinstance(password, str):
91
91
  raise ConfigurationError("password must be a non-empty string")
92
- self.__password = password
92
+ self.__password: str = password
93
93
 
94
94
  # Validate serial_id
95
95
  if not serial_id or not isinstance(serial_id, str):
96
96
  raise ConfigurationError("serial_id must be a non-empty string")
97
- self.serial_id = serial_id
97
+ self.serial_id: str = serial_id
98
98
 
99
99
  # Set apikey and secret_key
100
100
  self.__apikey = apikey
@@ -104,12 +104,11 @@ class DiagralAPI:
104
104
  if pincode is not None:
105
105
  if not isinstance(pincode, int):
106
106
  raise ConfigurationError("pincode must be an integer")
107
- self.__pincode = pincode
107
+ self.__pincode: int | None = pincode
108
108
 
109
109
  # Initialize session and access_token
110
110
  self.session: aiohttp.ClientSession | None = None
111
111
  self.__access_token: str | None = None
112
- self.access_token_expires: datetime | None = None
113
112
 
114
113
  # Set default values for other attributes
115
114
  self.alarm_configuration: AlarmConfiguration | None = None
@@ -120,7 +119,7 @@ class DiagralAPI:
120
119
  _LOGGER.info("Successfully initialized DiagralAPI session")
121
120
  return self
122
121
 
123
- async def __aexit__(self, exc_type, exc, tb):
122
+ async def __aexit__(self, exc_type, exc, tb) -> None:
124
123
  """Close the aiohttp ClientSession."""
125
124
  if self.session:
126
125
  await self.session.close()
@@ -156,8 +155,8 @@ class DiagralAPI:
156
155
  _LOGGER.error(error_msg)
157
156
  raise SessionError(error_msg)
158
157
 
159
- url = f"{BASE_URL}/{API_VERSION}/{endpoint}"
160
- headers = kwargs.pop("headers", {})
158
+ url: str = f"{BASE_URL}/{API_VERSION}/{endpoint}"
159
+ headers: Any = kwargs.pop("headers", {})
161
160
  _LOGGER.debug(
162
161
  "Sending %s request to %s with headers %s and data %s",
163
162
  method,
@@ -170,7 +169,7 @@ class DiagralAPI:
170
169
  async with self.session.request(
171
170
  method, url, headers=headers, timeout=timeout, **kwargs
172
171
  ) as response:
173
- response_data = await response.json()
172
+ response_data: Any = await response.json()
174
173
  if response.status == 400:
175
174
  response = HTTPErrorResponse(**response_data)
176
175
  raise DiagralAPIError(
@@ -236,13 +235,13 @@ class DiagralAPI:
236
235
  raise SessionError(error_msg)
237
236
 
238
237
  _LOGGER.debug("Attempting to login to Diagral API")
239
- _DATA = {"username": self.username, "password": self.__password}
238
+ _DATA: dict[str, str] = {"username": self.username, "password": self.__password}
240
239
  try:
241
240
  response_data, *_ = await self._request(
242
241
  "POST", "users/authenticate/login?vendor=DIAGRAL", json=_DATA
243
242
  )
244
243
  _LOGGER.debug("Login Response data: %s", response_data)
245
- login_response = LoginResponse.from_dict(response_data)
244
+ login_response: LoginResponse = LoginResponse.from_dict(response_data)
246
245
  _LOGGER.debug("Login response: %s", login_response)
247
246
 
248
247
  self.__access_token = login_response.access_token
@@ -253,16 +252,14 @@ class DiagralAPI:
253
252
 
254
253
  _LOGGER.info("Successfully logged in to Diagral API")
255
254
  except DiagralAPIError as e:
256
- error_msg = f"Failed to login : {e!s}"
255
+ error_msg: str = f"Failed to login : {e!s}"
257
256
  _LOGGER.error(error_msg)
258
257
  raise AuthenticationError(error_msg) from e
259
258
 
260
259
  async def set_apikey(self) -> ApiKeyWithSecret:
261
260
  """Asynchronously set the API key for the Diagral API.
262
261
 
263
- This method first ensures that the access token is valid and not expired.
264
- If the access token is expired, it attempts to log in again to refresh it.
265
- Then, it sends a request to create a new API key using the current access token.
262
+ It sends a request to create a new API key using the current access token.
266
263
  If the API key is successfully created, it verifies the API key to ensure its validity.
267
264
 
268
265
  Returns:
@@ -276,16 +273,8 @@ class DiagralAPI:
276
273
  if not self.__access_token:
277
274
  await self.login()
278
275
 
279
- if (
280
- self.__access_token
281
- and self.access_token_expires
282
- and datetime.now() >= self.access_token_expires
283
- ):
284
- _LOGGER.warning("Access token has expired, attempting to login again")
285
- await self.login()
286
-
287
- _DATA = {"serial_id": self.serial_id}
288
- _HEADERS = {
276
+ _DATA: dict[str, str] = {"serial_id": self.serial_id}
277
+ _HEADERS: dict[str, str] = {
289
278
  "Authorization": f"Bearer {self.__access_token}",
290
279
  }
291
280
 
@@ -293,7 +282,9 @@ class DiagralAPI:
293
282
  response_data, *_ = await self._request(
294
283
  "POST", "users/api_key", json=_DATA, headers=_HEADERS
295
284
  )
296
- set_apikey_response = ApiKeyWithSecret.from_dict(response_data)
285
+ set_apikey_response: ApiKeyWithSecret = ApiKeyWithSecret.from_dict(
286
+ response_data
287
+ )
297
288
  self.__apikey = set_apikey_response.api_key
298
289
  if not self.__apikey:
299
290
  error_msg = "API key not found in response"
@@ -317,7 +308,7 @@ class DiagralAPI:
317
308
  self.__apikey = None
318
309
  raise
319
310
  except DiagralAPIError as e:
320
- error_msg = f"Failed to create API key: {e!s}"
311
+ error_msg: str = f"Failed to create API key: {e!s}"
321
312
  _LOGGER.error(error_msg)
322
313
  raise AuthenticationError(error_msg) from e
323
314
 
@@ -341,7 +332,7 @@ class DiagralAPI:
341
332
 
342
333
  """
343
334
 
344
- apikey_to_validate = apikey or self.__apikey
335
+ apikey_to_validate: str = apikey or self.__apikey
345
336
 
346
337
  if not apikey_to_validate:
347
338
  _LOGGER.warning("No API key provided to validate")
@@ -350,7 +341,7 @@ class DiagralAPI:
350
341
  if not self.__access_token:
351
342
  await self.login()
352
343
 
353
- _HEADERS = {
344
+ _HEADERS: dict[str, str] = {
354
345
  "Authorization": f"Bearer {self.__access_token}",
355
346
  }
356
347
  response_data, *_ = await self._request(
@@ -358,7 +349,7 @@ class DiagralAPI:
358
349
  f"users/systems/{self.serial_id}/api_keys",
359
350
  headers=_HEADERS,
360
351
  )
361
- validate_apikey_response = ApiKeys.from_dict(response_data)
352
+ validate_apikey_response: ApiKeys = ApiKeys.from_dict(response_data)
362
353
  is_valid = any(
363
354
  key_info.api_key == apikey_to_validate
364
355
  for key_info in validate_apikey_response.api_keys
@@ -387,7 +378,7 @@ class DiagralAPI:
387
378
 
388
379
  """
389
380
 
390
- apikey_to_delete = apikey or self.__apikey
381
+ apikey_to_delete: str = apikey or self.__apikey
391
382
 
392
383
  if not apikey_to_delete:
393
384
  raise AuthenticationError("An API key is required to delete it")
@@ -395,7 +386,7 @@ class DiagralAPI:
395
386
  if not self.__access_token:
396
387
  await self.login()
397
388
 
398
- _HEADERS = {
389
+ _HEADERS: dict[str, str] = {
399
390
  "Authorization": f"Bearer {self.__access_token}",
400
391
  }
401
392
  await self._request(
@@ -409,6 +400,50 @@ class DiagralAPI:
409
400
  self.__apikey = None
410
401
  self.__secret_key = None
411
402
 
403
+ async def try_connection(self, ephemeral: bool = True) -> bool:
404
+ """Test connection with the Diagral system.
405
+
406
+ This method tests the connection by either using provided API credentials or generating
407
+ temporary ones. It validates the connection by checking the system status.
408
+
409
+ Args:
410
+ ephemeral (bool, optional): If True, temporary API keys will be deleted after
411
+ connection test. Defaults to True.
412
+
413
+ Returns:
414
+ TryConnectResult: Object containing connection test results and optionally API keys
415
+ if non-ephemeral temporary keys were generated.
416
+
417
+ Raises:
418
+ DiagralAPIError: If connection attempt fails or system status check fails.
419
+
420
+ Note:
421
+ If API credentials are not provided during client initialization, temporary
422
+ keys will be generated (if ephemeral) for the connection test. These keys will be:
423
+ - Deleted after the test if ephemeral=True
424
+ - Returned in the result if ephemeral=False
425
+
426
+ """
427
+
428
+ result: TryConnectResult = TryConnectResult()
429
+ api_keys_provided = bool(self.__apikey and self.__secret_key)
430
+ try:
431
+ # If API keys are not provided, generate temporary keys
432
+ if not api_keys_provided:
433
+ api_key_response: ApiKeyWithSecret = await self.set_apikey()
434
+
435
+ # Retrieve system status to validate connection
436
+ await self.get_system_status()
437
+ # If connection is successful, clean up temporary keys if requested (ephemeral)
438
+ if ephemeral and not api_keys_provided:
439
+ await self.delete_apikey(apikey=self.__apikey)
440
+ elif not ephemeral and not api_keys_provided:
441
+ result.keys = api_key_response
442
+ except DiagralAPIError as e:
443
+ raise DiagralAPIError(f"Failed to connect to the system: {e}") from e
444
+ result.result = True
445
+ return result
446
+
412
447
  async def get_configuration(self) -> None:
413
448
  """Asynchronously retrieve the configuration of the Diagral system.
414
449
 
@@ -433,14 +468,14 @@ class DiagralAPI:
433
468
  )
434
469
 
435
470
  _TIMESTAMP = str(int(time.time()))
436
- _HMAC = generate_hmac_signature(
471
+ _HMAC: str = generate_hmac_signature(
437
472
  timestamp=_TIMESTAMP,
438
473
  serial_id=self.serial_id,
439
474
  api_key=self.__apikey,
440
475
  secret_key=self.__secret_key,
441
476
  )
442
477
 
443
- _HEADERS = {
478
+ _HEADERS: dict[str, str] = {
444
479
  "X-HMAC": _HMAC,
445
480
  "X-TIMESTAMP": _TIMESTAMP,
446
481
  "X-APIKEY": self.__apikey,
@@ -475,13 +510,13 @@ class DiagralAPI:
475
510
  if not self.alarm_configuration:
476
511
  raise ConfigurationError("Failed to retrieve alarm configuration")
477
512
 
478
- device_types = sorted(
513
+ device_types: list[str] = sorted(
479
514
  ["cameras", "commands", "sensors", "sirens", "transmitters"]
480
515
  )
481
516
  devices_infos = {}
482
517
  for device_type in device_types:
483
518
  _LOGGER.debug("Retrieving devices information for %s", device_type)
484
- devices = getattr(self.alarm_configuration, device_type, None)
519
+ devices: Any | None = getattr(self.alarm_configuration, device_type, None)
485
520
  if devices is not None:
486
521
  devices_infos[device_type] = [
487
522
  {"index": device.index, "label": device.label} for device in devices
@@ -515,14 +550,14 @@ class DiagralAPI:
515
550
  raise AuthenticationError("PIN code required to get system details")
516
551
 
517
552
  _TIMESTAMP = str(int(time.time()))
518
- _HMAC = generate_hmac_signature(
553
+ _HMAC: str = generate_hmac_signature(
519
554
  timestamp=_TIMESTAMP,
520
555
  serial_id=self.serial_id,
521
556
  api_key=self.__apikey,
522
557
  secret_key=self.__secret_key,
523
558
  )
524
559
 
525
- _HEADERS = {
560
+ _HEADERS: dict[str, str] = {
526
561
  "X-PIN-CODE": str(self.__pincode),
527
562
  "X-HMAC": _HMAC,
528
563
  "X-TIMESTAMP": _TIMESTAMP,
@@ -558,14 +593,14 @@ class DiagralAPI:
558
593
  raise AuthenticationError("PIN code required to get system details")
559
594
 
560
595
  _TIMESTAMP = str(int(time.time()))
561
- _HMAC = generate_hmac_signature(
596
+ _HMAC: str = generate_hmac_signature(
562
597
  timestamp=_TIMESTAMP,
563
598
  serial_id=self.serial_id,
564
599
  api_key=self.__apikey,
565
600
  secret_key=self.__secret_key,
566
601
  )
567
602
 
568
- _HEADERS = {
603
+ _HEADERS: dict[str, str] = {
569
604
  "X-PIN-CODE": str(self.__pincode),
570
605
  "X-HMAC": _HMAC,
571
606
  "X-TIMESTAMP": _TIMESTAMP,
@@ -612,14 +647,14 @@ class DiagralAPI:
612
647
  raise AuthenticationError(f"PIN code required to do system action {action}")
613
648
 
614
649
  _TIMESTAMP = str(int(time.time()))
615
- _HMAC = generate_hmac_signature(
650
+ _HMAC: str = generate_hmac_signature(
616
651
  timestamp=_TIMESTAMP,
617
652
  serial_id=self.serial_id,
618
653
  api_key=self.__apikey,
619
654
  secret_key=self.__secret_key,
620
655
  )
621
656
 
622
- _HEADERS = {
657
+ _HEADERS: dict[str, str] = {
623
658
  "X-PIN-CODE": str(self.__pincode),
624
659
  "X-HMAC": _HMAC,
625
660
  "X-TIMESTAMP": _TIMESTAMP,
@@ -736,7 +771,7 @@ class DiagralAPI:
736
771
  await self.get_configuration()
737
772
 
738
773
  # Check if the groups are valid
739
- invalid_groups = [
774
+ invalid_groups: list[int] = [
740
775
  group
741
776
  for group in groups
742
777
  if group not in [g.index for g in self.alarm_configuration.groups]
@@ -747,20 +782,20 @@ class DiagralAPI:
747
782
  )
748
783
 
749
784
  _TIMESTAMP = str(int(time.time()))
750
- _HMAC = generate_hmac_signature(
785
+ _HMAC: str = generate_hmac_signature(
751
786
  timestamp=_TIMESTAMP,
752
787
  serial_id=self.serial_id,
753
788
  api_key=self.__apikey,
754
789
  secret_key=self.__secret_key,
755
790
  )
756
791
 
757
- _HEADERS = {
792
+ _HEADERS: dict[str, str] = {
758
793
  "X-PIN-CODE": str(self.__pincode),
759
794
  "X-HMAC": _HMAC,
760
795
  "X-TIMESTAMP": _TIMESTAMP,
761
796
  "X-APIKEY": self.__apikey,
762
797
  }
763
- data = {"groups": groups}
798
+ data: dict[str, list[int]] = {"groups": groups}
764
799
  response_data, *_ = await self._request(
765
800
  "POST",
766
801
  f"systems/{self.serial_id}/{action}",
@@ -854,14 +889,14 @@ class DiagralAPI:
854
889
  raise AuthenticationError("PIN code required to get system details")
855
890
 
856
891
  _TIMESTAMP = str(int(time.time()))
857
- _HMAC = generate_hmac_signature(
892
+ _HMAC: str = generate_hmac_signature(
858
893
  timestamp=_TIMESTAMP,
859
894
  serial_id=self.serial_id,
860
895
  api_key=self.__apikey,
861
896
  secret_key=self.__secret_key,
862
897
  )
863
898
 
864
- _HEADERS = {
899
+ _HEADERS: dict[str, str] = {
865
900
  "X-PIN-CODE": str(self.__pincode),
866
901
  "X-HMAC": _HMAC,
867
902
  "X-TIMESTAMP": _TIMESTAMP,
@@ -935,14 +970,14 @@ class DiagralAPI:
935
970
  )
936
971
 
937
972
  _TIMESTAMP = str(int(time.time()))
938
- _HMAC = generate_hmac_signature(
973
+ _HMAC: str = generate_hmac_signature(
939
974
  timestamp=_TIMESTAMP,
940
975
  serial_id=self.serial_id,
941
976
  api_key=self.__apikey,
942
977
  secret_key=self.__secret_key,
943
978
  )
944
979
 
945
- _HEADERS = {
980
+ _HEADERS: dict[str, str] = {
946
981
  "X-HMAC": _HMAC,
947
982
  "X-TIMESTAMP": _TIMESTAMP,
948
983
  "X-APIKEY": self.__apikey,
@@ -990,14 +1025,14 @@ class DiagralAPI:
990
1025
  )
991
1026
 
992
1027
  _TIMESTAMP = str(int(time.time()))
993
- _HMAC = generate_hmac_signature(
1028
+ _HMAC: str = generate_hmac_signature(
994
1029
  timestamp=_TIMESTAMP,
995
1030
  serial_id=self.serial_id,
996
1031
  api_key=self.__apikey,
997
1032
  secret_key=self.__secret_key,
998
1033
  )
999
1034
 
1000
- _HEADERS = {
1035
+ _HEADERS: dict[str, str] = {
1001
1036
  "X-HMAC": _HMAC,
1002
1037
  "X-TIMESTAMP": _TIMESTAMP,
1003
1038
  "X-APIKEY": self.__apikey,
@@ -1043,14 +1078,14 @@ class DiagralAPI:
1043
1078
  )
1044
1079
 
1045
1080
  _TIMESTAMP = str(int(time.time()))
1046
- _HMAC = generate_hmac_signature(
1081
+ _HMAC: str = generate_hmac_signature(
1047
1082
  timestamp=_TIMESTAMP,
1048
1083
  serial_id=self.serial_id,
1049
1084
  api_key=self.__apikey,
1050
1085
  secret_key=self.__secret_key,
1051
1086
  )
1052
1087
 
1053
- _HEADERS = {
1088
+ _HEADERS: dict[str, str] = {
1054
1089
  "X-HMAC": _HMAC,
1055
1090
  "X-TIMESTAMP": _TIMESTAMP,
1056
1091
  "X-APIKEY": self.__apikey,
@@ -1108,14 +1143,14 @@ class DiagralAPI:
1108
1143
  )
1109
1144
 
1110
1145
  _TIMESTAMP = str(int(time.time()))
1111
- _HMAC = generate_hmac_signature(
1146
+ _HMAC: str = generate_hmac_signature(
1112
1147
  timestamp=_TIMESTAMP,
1113
1148
  serial_id=self.serial_id,
1114
1149
  api_key=self.__apikey,
1115
1150
  secret_key=self.__secret_key,
1116
1151
  )
1117
1152
 
1118
- _HEADERS = {
1153
+ _HEADERS: dict[str, str] = {
1119
1154
  "X-HMAC": _HMAC,
1120
1155
  "X-TIMESTAMP": _TIMESTAMP,
1121
1156
  "X-APIKEY": self.__apikey,
@@ -1216,14 +1251,14 @@ class DiagralAPI:
1216
1251
  )
1217
1252
 
1218
1253
  _TIMESTAMP = str(int(time.time()))
1219
- _HMAC = generate_hmac_signature(
1254
+ _HMAC: str = generate_hmac_signature(
1220
1255
  timestamp=_TIMESTAMP,
1221
1256
  serial_id=self.serial_id,
1222
1257
  api_key=self.__apikey,
1223
1258
  secret_key=self.__secret_key,
1224
1259
  )
1225
1260
 
1226
- _HEADERS = {
1261
+ _HEADERS: dict[str, str] = {
1227
1262
  "X-HMAC": _HMAC,
1228
1263
  "X-TIMESTAMP": _TIMESTAMP,
1229
1264
  "X-APIKEY": self.__apikey,
pydiagral/exceptions.py CHANGED
@@ -10,8 +10,8 @@ class DiagralAPIError(Exception):
10
10
  :param message: The error message.
11
11
  :param status_code: The status code of the error, if any.
12
12
  """
13
- self.message = message
14
- self.status_code = status_code
13
+ self.message: str = message
14
+ self.status_code: int | None = status_code
15
15
  super().__init__(self.message)
16
16
 
17
17
 
pydiagral/models.py CHANGED
@@ -15,7 +15,7 @@ import re
15
15
  import types
16
16
  from typing import TypeVar, Union, get_args, get_origin, get_type_hints
17
17
 
18
- logger = logging.getLogger(__name__)
18
+ logger: logging.Logger = logging.getLogger(__name__)
19
19
 
20
20
 
21
21
  #######################################################
@@ -355,6 +355,30 @@ class ApiKeys(CamelCaseModel):
355
355
  )
356
356
 
357
357
 
358
+ @dataclass
359
+ class TryConnectResult(CamelCaseModel):
360
+ """A class representing the result of an API connection attempt.
361
+
362
+ This class is used to store the result of an API connection attempt
363
+ and the associated API keys if the connection was successful.
364
+
365
+ Attributes:
366
+ result (bool | None): Whether the connection attempt was successful. Defaults to False.
367
+ keys (ApiKeyWithSecret | None): The API keys associated with the successful connection. Defaults to None.
368
+
369
+ Example:
370
+ >>> result = TryConnectResult(result=True, keys=api_key_obj)
371
+ >>> print(result.result)
372
+ True
373
+ >>> print(result.keys)
374
+ ApiKeyWithSecret(api_key='abc123', api_secret='xyz789')
375
+
376
+ """
377
+
378
+ result: bool | None = False
379
+ keys: ApiKeyWithSecret | None = None
380
+
381
+
358
382
  #####################################
359
383
  # Data models for alarm configuration
360
384
  #####################################
pydiagral/utils.py CHANGED
@@ -10,5 +10,5 @@ def generate_hmac_signature(
10
10
  ) -> str:
11
11
  """Generate an HMAC signature for the given parameters."""
12
12
  timestamp = str(int(time.time()))
13
- message = f"{timestamp}.{serial_id}.{api_key}"
13
+ message: str = f"{timestamp}.{serial_id}.{api_key}"
14
14
  return hmac.new(secret_key.encode(), message.encode(), hashlib.sha256).hexdigest()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydiagral
3
- Version: 1.4.0
3
+ Version: 1.5.0b2
4
4
  Summary: A Python library for interacting with Diagral systems
5
5
  Project-URL: Homepage, https://github.com/mguyard/pydiagral
6
6
  Project-URL: Documentation, https://github.com/mguyard/pydiagral
@@ -685,7 +685,7 @@ License: GNU GENERAL PUBLIC LICENSE
685
685
  <https://www.gnu.org/licenses/why-not-lgpl.html>.
686
686
  License-File: LICENSE
687
687
  Keywords: diagral,home automation,python
688
- Classifier: Development Status :: 3 - Alpha
688
+ Classifier: Development Status :: 5 - Production/Stable
689
689
  Classifier: Intended Audience :: Developers
690
690
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
691
691
  Classifier: Operating System :: OS Independent
@@ -702,9 +702,6 @@ Description-Content-Type: text/markdown
702
702
  <p align="center">
703
703
  <img src="https://raw.githubusercontent.com/mguyard/pydiagral/main/docs/pydiagral-Logo.png" width="400" />
704
704
  </p>
705
- <p align="center">
706
- <h1 align="center">PyDiagral</h1>
707
- </p>
708
705
  <p align="center">
709
706
  A powerful and easy-to-use Python library for seamless integration with the Diagral alarm system.
710
707
  </p>
@@ -729,19 +726,25 @@ Description-Content-Type: text/markdown
729
726
  </p>
730
727
  <br /><br />
731
728
 
732
- # Documentation pydiagral
729
+ # pydiagral
733
730
 
734
731
  Welcome to the documentation for pydiagral, a Python library for interacting with the Diagral API.
735
732
 
736
- ## About pydiagral
733
+ ## 📍 About pydiagral
737
734
 
738
- pydiagral is an asynchronous Python interface for the Diagral alarm system. This library allows users to control and monitor their Diagral alarm system through the official API.
735
+ `pydiagral` is an asynchronous Python interface for the Diagral alarm system. This library allows users to control and monitor their Diagral alarm system through the official API.
739
736
 
740
- ## Requirement
737
+ > [!CAUTION]
738
+ >
739
+ > Please note that the Diagral alarm system is a security system, and it may be preferable not to connect it to any automation platform for security reasons.
740
+ > In no event shall the developer of [`pydiagral`](https://github.com/mguyard/pydiagral) library be held liable for any issues arising from the use of this [`pydiagral`](https://github.com/mguyard/pydiagral) library.
741
+ > The user installs and uses this integration at their own risk and with full knowledge of the potential implications.
742
+
743
+ ## ✅ Requirement
741
744
 
742
745
  To use this library, which leverages the Diagral APIs, you must have a Diagral box (DIAG56AAX). This box connects your Diagral alarm system to the internet, enabling interaction with the alarm system via the API. You can find more information about the Diagral box [here](https://www.diagral.fr/commande/box-alerte-et-pilotage).
743
746
 
744
- ## Key Features
747
+ ## 📦 Key Features
745
748
 
746
749
  The `DiagralAPI` class offers the following functionalities:
747
750
 
@@ -766,7 +769,7 @@ The `DiagralAPI` class offers the following functionalities:
766
769
  - Activate or Desactivate system (partially or globally)
767
770
  - Automatism actions
768
771
 
769
- ## Quick Start
772
+ ## 🚀 Quick Start
770
773
 
771
774
  To get started with pydiagral, follow these steps:
772
775
 
@@ -796,33 +799,72 @@ And run the [example_code.py](https://github.com/mguyard/pydiagral/blob/main/exa
796
799
  >
797
800
  > You can customize the actions performed by [example_code.py](https://github.com/mguyard/pydiagral/blob/main/example_code.py) by modifying the parameters in the code, as indicated by the `CUSTOMIZE THE TESTS` section title.
798
801
 
802
+ # 📖 Documentations
803
+
804
+ ## Package Documentation
805
+
806
+ Library documentation is available [here](https://mguyard.github.io/pydiagral/).
807
+
808
+ ### Package Structure
809
+
810
+ For detailed library documentation, please refer to the following sections:
811
+
812
+ - [API Reference](https://mguyard.github.io/pydiagral/api/): Comprehensive documentation of the DiagralAPI class and its methods
813
+ - [Data Models](https://mguyard.github.io/pydiagral/models/): Description of the data structures used
814
+ - [Exceptions](https://mguyard.github.io/pydiagral/exceptions/): List of package exceptions
815
+
799
816
  ## Diagral API Official documentation
800
817
 
801
818
  Official Diagral API is available [here](https://appv3.tt-monitor.com/emerald/redoc).
802
819
 
820
+ # 🙋 FAQ
821
+
803
822
  ## How to find Serial on DIAG56AAX
804
823
 
805
824
  The serial number can only be found with physical access to the box. You need to open it, and you will find a label with a QR Code.
825
+
806
826
  On this label, there is a 15-character code that represents the serial number of the box.
807
827
 
808
- ![How to find your Diagral Serial](docs/how-to-find-diagral-serial.png)
828
+ ![How to find your Diagral Serial](https://raw.githubusercontent.com/mguyard/pydiagral/main/docs/how-to-find-diagral-serial.png)
809
829
 
810
830
  > [!IMPORTANT]
811
831
  >
812
832
  > This code is necessary to use this library and Diagral API.
813
833
 
814
- ## API Structure
834
+ # 🤝 Contribution
835
+
836
+ Contributions are welcome! Here are several ways you can contribute:
837
+
838
+ - **Submit Pull Requests**: Review open PRs, and submit your own PRs.
839
+ - **Report Issues**: Submit bugs found or log feature requests.
815
840
 
816
- For detailed API documentation, please refer to the following sections:
841
+ <details closed>
842
+ <summary>Contributing Guidelines</summary>
817
843
 
818
- - [API Reference](https://XXXXXXXXXXXXXXXX): Comprehensive documentation of the DiagralAPI class and its methods
819
- - [Data Models](https://XXXXXXXXXXXXXXXX): Description of the data structures used
820
- - [Exceptions](https://XXXXXXXXXXXXXXXX): List of package exceptions
844
+ 1. **Fork the Repository**: Start by forking the project repository to your GitHub account.
845
+ 2. **Clone Locally**: Clone the forked repository to your local machine using a Git client.
846
+ ```sh
847
+ git clone https://github.com/mguyard/pydiagral
848
+ ```
849
+ 3. **Create a New Branch**: Always work on a new branch, giving it a descriptive name.
850
+ ```sh
851
+ git checkout -b new-feature-x
852
+ ```
853
+ 4. **Make Your Changes**: Develop and test your changes locally.
854
+ 5. **Commit Your Changes**: Commit your changes with a clear and concise message that follows the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) guidelines.
855
+ ```sh
856
+ git commit -m 'feat: Implemented new feature x.'
857
+ ```
858
+ 6. **Push to GitHub**: Push the changes to your forked repository.
859
+ ```sh
860
+ git push origin new-feature-x
861
+ ```
862
+ 7. **Submit a Pull Request**: Create a PR against the original project repository. Clearly describe the changes and their motivations.
821
863
 
822
- ## Contribution
864
+ Once your PR is reviewed and approved, it will be merged into the `beta` branch. After final testing, it will be merged into the `main` branch.
823
865
 
824
- Contributions to pydiagral are welcome! Please check our contribution guidelines for more information on how to participate in the development of this library.
866
+ </details>
825
867
 
826
- ## License
868
+ # 📄 License
827
869
 
828
870
  pydiagral is distributed under the GPL-v3 License. See the [LICENSE](https://github.com/mguyard/pydiagral/blob/main/LICENSE) file for more details.
@@ -0,0 +1,10 @@
1
+ pydiagral/__init__.py,sha256=4uM-RD2GQ6JYJkxu-D6wj3XpqfY5gN2hP8NF6WvRI9k,576
2
+ pydiagral/api.py,sha256=F00LgyVtayPtZOm9O5DNQ2gLCv36vpsK6gCJXxOZNRk,48736
3
+ pydiagral/constants.py,sha256=2B0TdKxQHA3cpIBxojo43bMW44wN9xKYsHbBRHWsaBk,119
4
+ pydiagral/exceptions.py,sha256=Q5wEpNtiykLs3Ck0W8r1IQAJek_omaQ3jpMOtiiwBUg,1030
5
+ pydiagral/models.py,sha256=vUjxuApjVaMtd7H6Iw5LarwABi30O4FfdObhZRbUNuc,54931
6
+ pydiagral/utils.py,sha256=-VxI-lNaC4bU1K4DSmWDhvbsS2bXv5FAGULGKBf1UMU,449
7
+ pydiagral-1.5.0b2.dist-info/METADATA,sha256=ngDa7KSkaHBpcc5EEqf0CS5IlpmQwNF_dvmPDQClfxs,48531
8
+ pydiagral-1.5.0b2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
+ pydiagral-1.5.0b2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
10
+ pydiagral-1.5.0b2.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- pydiagral/__init__.py,sha256=4uM-RD2GQ6JYJkxu-D6wj3XpqfY5gN2hP8NF6WvRI9k,576
2
- pydiagral/api.py,sha256=sX46Fct3Bwei5yKKQ5PnGDKl261BBqZ0CYMFUnXZ5Mw,46753
3
- pydiagral/constants.py,sha256=2B0TdKxQHA3cpIBxojo43bMW44wN9xKYsHbBRHWsaBk,119
4
- pydiagral/exceptions.py,sha256=PLo85XJ55q4_dzsyaMYXLIaX8Ws7HH6vDUWNWodzCpM,1013
5
- pydiagral/models.py,sha256=shnAf7ojh6y5fGp3l5RbGdp4qpuEyOZI-0ePrWAg2l0,54120
6
- pydiagral/utils.py,sha256=weccW18dseD_Ypwe4dKvNz6TlZPA7BQbf5l5qk3GXOg,444
7
- pydiagral-1.4.0.dist-info/METADATA,sha256=uomaH_YBT9vnk7pplGjfSA4vnxvxq9jBVAsE5Wc2zAc,46568
8
- pydiagral-1.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
- pydiagral-1.4.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
10
- pydiagral-1.4.0.dist-info/RECORD,,