pydiagral 1.4.0__py3-none-any.whl → 1.5.0b1__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
@@ -41,7 +40,7 @@ from .models import (
41
40
  )
42
41
  from .utils import generate_hmac_signature
43
42
 
44
- _LOGGER = logging.getLogger(__name__)
43
+ _LOGGER: logging.Logger = logging.getLogger(__name__)
45
44
 
46
45
  # Minimum Python version: 3.10
47
46
 
@@ -84,17 +83,17 @@ class DiagralAPI:
84
83
  or not self.__is_valid_email(username)
85
84
  ):
86
85
  raise ConfigurationError("username must be a valid non-empty email address")
87
- self.username = username
86
+ self.username: str = username
88
87
 
89
88
  # Validate password
90
89
  if not password or not isinstance(password, str):
91
90
  raise ConfigurationError("password must be a non-empty string")
92
- self.__password = password
91
+ self.__password: str = password
93
92
 
94
93
  # Validate serial_id
95
94
  if not serial_id or not isinstance(serial_id, str):
96
95
  raise ConfigurationError("serial_id must be a non-empty string")
97
- self.serial_id = serial_id
96
+ self.serial_id: str = serial_id
98
97
 
99
98
  # Set apikey and secret_key
100
99
  self.__apikey = apikey
@@ -104,12 +103,11 @@ class DiagralAPI:
104
103
  if pincode is not None:
105
104
  if not isinstance(pincode, int):
106
105
  raise ConfigurationError("pincode must be an integer")
107
- self.__pincode = pincode
106
+ self.__pincode: int | None = pincode
108
107
 
109
108
  # Initialize session and access_token
110
109
  self.session: aiohttp.ClientSession | None = None
111
110
  self.__access_token: str | None = None
112
- self.access_token_expires: datetime | None = None
113
111
 
114
112
  # Set default values for other attributes
115
113
  self.alarm_configuration: AlarmConfiguration | None = None
@@ -120,7 +118,7 @@ class DiagralAPI:
120
118
  _LOGGER.info("Successfully initialized DiagralAPI session")
121
119
  return self
122
120
 
123
- async def __aexit__(self, exc_type, exc, tb):
121
+ async def __aexit__(self, exc_type, exc, tb) -> None:
124
122
  """Close the aiohttp ClientSession."""
125
123
  if self.session:
126
124
  await self.session.close()
@@ -156,8 +154,8 @@ class DiagralAPI:
156
154
  _LOGGER.error(error_msg)
157
155
  raise SessionError(error_msg)
158
156
 
159
- url = f"{BASE_URL}/{API_VERSION}/{endpoint}"
160
- headers = kwargs.pop("headers", {})
157
+ url: str = f"{BASE_URL}/{API_VERSION}/{endpoint}"
158
+ headers: Any = kwargs.pop("headers", {})
161
159
  _LOGGER.debug(
162
160
  "Sending %s request to %s with headers %s and data %s",
163
161
  method,
@@ -170,7 +168,7 @@ class DiagralAPI:
170
168
  async with self.session.request(
171
169
  method, url, headers=headers, timeout=timeout, **kwargs
172
170
  ) as response:
173
- response_data = await response.json()
171
+ response_data: Any = await response.json()
174
172
  if response.status == 400:
175
173
  response = HTTPErrorResponse(**response_data)
176
174
  raise DiagralAPIError(
@@ -236,13 +234,13 @@ class DiagralAPI:
236
234
  raise SessionError(error_msg)
237
235
 
238
236
  _LOGGER.debug("Attempting to login to Diagral API")
239
- _DATA = {"username": self.username, "password": self.__password}
237
+ _DATA: dict[str, str] = {"username": self.username, "password": self.__password}
240
238
  try:
241
239
  response_data, *_ = await self._request(
242
240
  "POST", "users/authenticate/login?vendor=DIAGRAL", json=_DATA
243
241
  )
244
242
  _LOGGER.debug("Login Response data: %s", response_data)
245
- login_response = LoginResponse.from_dict(response_data)
243
+ login_response: LoginResponse = LoginResponse.from_dict(response_data)
246
244
  _LOGGER.debug("Login response: %s", login_response)
247
245
 
248
246
  self.__access_token = login_response.access_token
@@ -253,16 +251,14 @@ class DiagralAPI:
253
251
 
254
252
  _LOGGER.info("Successfully logged in to Diagral API")
255
253
  except DiagralAPIError as e:
256
- error_msg = f"Failed to login : {e!s}"
254
+ error_msg: str = f"Failed to login : {e!s}"
257
255
  _LOGGER.error(error_msg)
258
256
  raise AuthenticationError(error_msg) from e
259
257
 
260
258
  async def set_apikey(self) -> ApiKeyWithSecret:
261
259
  """Asynchronously set the API key for the Diagral API.
262
260
 
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.
261
+ It sends a request to create a new API key using the current access token.
266
262
  If the API key is successfully created, it verifies the API key to ensure its validity.
267
263
 
268
264
  Returns:
@@ -276,16 +272,8 @@ class DiagralAPI:
276
272
  if not self.__access_token:
277
273
  await self.login()
278
274
 
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 = {
275
+ _DATA: dict[str, str] = {"serial_id": self.serial_id}
276
+ _HEADERS: dict[str, str] = {
289
277
  "Authorization": f"Bearer {self.__access_token}",
290
278
  }
291
279
 
@@ -293,7 +281,9 @@ class DiagralAPI:
293
281
  response_data, *_ = await self._request(
294
282
  "POST", "users/api_key", json=_DATA, headers=_HEADERS
295
283
  )
296
- set_apikey_response = ApiKeyWithSecret.from_dict(response_data)
284
+ set_apikey_response: ApiKeyWithSecret = ApiKeyWithSecret.from_dict(
285
+ response_data
286
+ )
297
287
  self.__apikey = set_apikey_response.api_key
298
288
  if not self.__apikey:
299
289
  error_msg = "API key not found in response"
@@ -317,7 +307,7 @@ class DiagralAPI:
317
307
  self.__apikey = None
318
308
  raise
319
309
  except DiagralAPIError as e:
320
- error_msg = f"Failed to create API key: {e!s}"
310
+ error_msg: str = f"Failed to create API key: {e!s}"
321
311
  _LOGGER.error(error_msg)
322
312
  raise AuthenticationError(error_msg) from e
323
313
 
@@ -341,7 +331,7 @@ class DiagralAPI:
341
331
 
342
332
  """
343
333
 
344
- apikey_to_validate = apikey or self.__apikey
334
+ apikey_to_validate: str = apikey or self.__apikey
345
335
 
346
336
  if not apikey_to_validate:
347
337
  _LOGGER.warning("No API key provided to validate")
@@ -350,7 +340,7 @@ class DiagralAPI:
350
340
  if not self.__access_token:
351
341
  await self.login()
352
342
 
353
- _HEADERS = {
343
+ _HEADERS: dict[str, str] = {
354
344
  "Authorization": f"Bearer {self.__access_token}",
355
345
  }
356
346
  response_data, *_ = await self._request(
@@ -358,7 +348,7 @@ class DiagralAPI:
358
348
  f"users/systems/{self.serial_id}/api_keys",
359
349
  headers=_HEADERS,
360
350
  )
361
- validate_apikey_response = ApiKeys.from_dict(response_data)
351
+ validate_apikey_response: ApiKeys = ApiKeys.from_dict(response_data)
362
352
  is_valid = any(
363
353
  key_info.api_key == apikey_to_validate
364
354
  for key_info in validate_apikey_response.api_keys
@@ -387,7 +377,7 @@ class DiagralAPI:
387
377
 
388
378
  """
389
379
 
390
- apikey_to_delete = apikey or self.__apikey
380
+ apikey_to_delete: str = apikey or self.__apikey
391
381
 
392
382
  if not apikey_to_delete:
393
383
  raise AuthenticationError("An API key is required to delete it")
@@ -395,7 +385,7 @@ class DiagralAPI:
395
385
  if not self.__access_token:
396
386
  await self.login()
397
387
 
398
- _HEADERS = {
388
+ _HEADERS: dict[str, str] = {
399
389
  "Authorization": f"Bearer {self.__access_token}",
400
390
  }
401
391
  await self._request(
@@ -409,6 +399,50 @@ class DiagralAPI:
409
399
  self.__apikey = None
410
400
  self.__secret_key = None
411
401
 
402
+ async def try_connection(self, ephemeral: bool = True) -> bool:
403
+ """Establish a connection with the Diagral system.
404
+
405
+ This method tries to connect to the Diagral API by:
406
+ 1. Checking if API keys are provided
407
+ 2. If not, generating temporary API keys
408
+ 3. Validating the connection by checking system status
409
+ 4. Optionally cleaning up temporary keys if requested
410
+
411
+ Args:
412
+ ephemeral (bool, optional): If True and using temporary API keys,
413
+ deletes them after validation. Defaults to True.
414
+
415
+ Returns:
416
+ bool: True if connection is successful
417
+
418
+ Raises:
419
+ DiagralAPIError: If connection fails or system status check fails
420
+
421
+ """
422
+
423
+ api_keys_provided = bool(self.__apikey and self.__secret_key)
424
+ _LOGGER.warning("API keys provided: %s", api_keys_provided)
425
+ try:
426
+ if not api_keys_provided:
427
+ api_key_response: ApiKeyWithSecret = await self.set_apikey()
428
+ _LOGGER.debug(
429
+ "TEST CONNECTION - Successfully created temporary API key : %s",
430
+ api_key_response,
431
+ )
432
+ if await self.validate_apikey(apikey=api_key_response.api_key):
433
+ _LOGGER.debug(
434
+ "TEST CONNECTION - Successfully validated temporary API key"
435
+ )
436
+ self.__apikey: str = api_key_response.api_key
437
+ self.__secret_key: str = api_key_response.secret_key
438
+
439
+ await self.get_system_status()
440
+ if ephemeral and not api_keys_provided:
441
+ await self.delete_apikey(apikey=self.__apikey)
442
+ except DiagralAPIError as e:
443
+ raise DiagralAPIError(f"Failed to connect to the system: {e}") from e
444
+ return True
445
+
412
446
  async def get_configuration(self) -> None:
413
447
  """Asynchronously retrieve the configuration of the Diagral system.
414
448
 
@@ -433,14 +467,14 @@ class DiagralAPI:
433
467
  )
434
468
 
435
469
  _TIMESTAMP = str(int(time.time()))
436
- _HMAC = generate_hmac_signature(
470
+ _HMAC: str = generate_hmac_signature(
437
471
  timestamp=_TIMESTAMP,
438
472
  serial_id=self.serial_id,
439
473
  api_key=self.__apikey,
440
474
  secret_key=self.__secret_key,
441
475
  )
442
476
 
443
- _HEADERS = {
477
+ _HEADERS: dict[str, str] = {
444
478
  "X-HMAC": _HMAC,
445
479
  "X-TIMESTAMP": _TIMESTAMP,
446
480
  "X-APIKEY": self.__apikey,
@@ -475,13 +509,13 @@ class DiagralAPI:
475
509
  if not self.alarm_configuration:
476
510
  raise ConfigurationError("Failed to retrieve alarm configuration")
477
511
 
478
- device_types = sorted(
512
+ device_types: list[str] = sorted(
479
513
  ["cameras", "commands", "sensors", "sirens", "transmitters"]
480
514
  )
481
515
  devices_infos = {}
482
516
  for device_type in device_types:
483
517
  _LOGGER.debug("Retrieving devices information for %s", device_type)
484
- devices = getattr(self.alarm_configuration, device_type, None)
518
+ devices: Any | None = getattr(self.alarm_configuration, device_type, None)
485
519
  if devices is not None:
486
520
  devices_infos[device_type] = [
487
521
  {"index": device.index, "label": device.label} for device in devices
@@ -515,14 +549,14 @@ class DiagralAPI:
515
549
  raise AuthenticationError("PIN code required to get system details")
516
550
 
517
551
  _TIMESTAMP = str(int(time.time()))
518
- _HMAC = generate_hmac_signature(
552
+ _HMAC: str = generate_hmac_signature(
519
553
  timestamp=_TIMESTAMP,
520
554
  serial_id=self.serial_id,
521
555
  api_key=self.__apikey,
522
556
  secret_key=self.__secret_key,
523
557
  )
524
558
 
525
- _HEADERS = {
559
+ _HEADERS: dict[str, str] = {
526
560
  "X-PIN-CODE": str(self.__pincode),
527
561
  "X-HMAC": _HMAC,
528
562
  "X-TIMESTAMP": _TIMESTAMP,
@@ -558,14 +592,14 @@ class DiagralAPI:
558
592
  raise AuthenticationError("PIN code required to get system details")
559
593
 
560
594
  _TIMESTAMP = str(int(time.time()))
561
- _HMAC = generate_hmac_signature(
595
+ _HMAC: str = generate_hmac_signature(
562
596
  timestamp=_TIMESTAMP,
563
597
  serial_id=self.serial_id,
564
598
  api_key=self.__apikey,
565
599
  secret_key=self.__secret_key,
566
600
  )
567
601
 
568
- _HEADERS = {
602
+ _HEADERS: dict[str, str] = {
569
603
  "X-PIN-CODE": str(self.__pincode),
570
604
  "X-HMAC": _HMAC,
571
605
  "X-TIMESTAMP": _TIMESTAMP,
@@ -612,14 +646,14 @@ class DiagralAPI:
612
646
  raise AuthenticationError(f"PIN code required to do system action {action}")
613
647
 
614
648
  _TIMESTAMP = str(int(time.time()))
615
- _HMAC = generate_hmac_signature(
649
+ _HMAC: str = generate_hmac_signature(
616
650
  timestamp=_TIMESTAMP,
617
651
  serial_id=self.serial_id,
618
652
  api_key=self.__apikey,
619
653
  secret_key=self.__secret_key,
620
654
  )
621
655
 
622
- _HEADERS = {
656
+ _HEADERS: dict[str, str] = {
623
657
  "X-PIN-CODE": str(self.__pincode),
624
658
  "X-HMAC": _HMAC,
625
659
  "X-TIMESTAMP": _TIMESTAMP,
@@ -736,7 +770,7 @@ class DiagralAPI:
736
770
  await self.get_configuration()
737
771
 
738
772
  # Check if the groups are valid
739
- invalid_groups = [
773
+ invalid_groups: list[int] = [
740
774
  group
741
775
  for group in groups
742
776
  if group not in [g.index for g in self.alarm_configuration.groups]
@@ -747,20 +781,20 @@ class DiagralAPI:
747
781
  )
748
782
 
749
783
  _TIMESTAMP = str(int(time.time()))
750
- _HMAC = generate_hmac_signature(
784
+ _HMAC: str = generate_hmac_signature(
751
785
  timestamp=_TIMESTAMP,
752
786
  serial_id=self.serial_id,
753
787
  api_key=self.__apikey,
754
788
  secret_key=self.__secret_key,
755
789
  )
756
790
 
757
- _HEADERS = {
791
+ _HEADERS: dict[str, str] = {
758
792
  "X-PIN-CODE": str(self.__pincode),
759
793
  "X-HMAC": _HMAC,
760
794
  "X-TIMESTAMP": _TIMESTAMP,
761
795
  "X-APIKEY": self.__apikey,
762
796
  }
763
- data = {"groups": groups}
797
+ data: dict[str, list[int]] = {"groups": groups}
764
798
  response_data, *_ = await self._request(
765
799
  "POST",
766
800
  f"systems/{self.serial_id}/{action}",
@@ -854,14 +888,14 @@ class DiagralAPI:
854
888
  raise AuthenticationError("PIN code required to get system details")
855
889
 
856
890
  _TIMESTAMP = str(int(time.time()))
857
- _HMAC = generate_hmac_signature(
891
+ _HMAC: str = generate_hmac_signature(
858
892
  timestamp=_TIMESTAMP,
859
893
  serial_id=self.serial_id,
860
894
  api_key=self.__apikey,
861
895
  secret_key=self.__secret_key,
862
896
  )
863
897
 
864
- _HEADERS = {
898
+ _HEADERS: dict[str, str] = {
865
899
  "X-PIN-CODE": str(self.__pincode),
866
900
  "X-HMAC": _HMAC,
867
901
  "X-TIMESTAMP": _TIMESTAMP,
@@ -935,14 +969,14 @@ class DiagralAPI:
935
969
  )
936
970
 
937
971
  _TIMESTAMP = str(int(time.time()))
938
- _HMAC = generate_hmac_signature(
972
+ _HMAC: str = generate_hmac_signature(
939
973
  timestamp=_TIMESTAMP,
940
974
  serial_id=self.serial_id,
941
975
  api_key=self.__apikey,
942
976
  secret_key=self.__secret_key,
943
977
  )
944
978
 
945
- _HEADERS = {
979
+ _HEADERS: dict[str, str] = {
946
980
  "X-HMAC": _HMAC,
947
981
  "X-TIMESTAMP": _TIMESTAMP,
948
982
  "X-APIKEY": self.__apikey,
@@ -990,14 +1024,14 @@ class DiagralAPI:
990
1024
  )
991
1025
 
992
1026
  _TIMESTAMP = str(int(time.time()))
993
- _HMAC = generate_hmac_signature(
1027
+ _HMAC: str = generate_hmac_signature(
994
1028
  timestamp=_TIMESTAMP,
995
1029
  serial_id=self.serial_id,
996
1030
  api_key=self.__apikey,
997
1031
  secret_key=self.__secret_key,
998
1032
  )
999
1033
 
1000
- _HEADERS = {
1034
+ _HEADERS: dict[str, str] = {
1001
1035
  "X-HMAC": _HMAC,
1002
1036
  "X-TIMESTAMP": _TIMESTAMP,
1003
1037
  "X-APIKEY": self.__apikey,
@@ -1043,14 +1077,14 @@ class DiagralAPI:
1043
1077
  )
1044
1078
 
1045
1079
  _TIMESTAMP = str(int(time.time()))
1046
- _HMAC = generate_hmac_signature(
1080
+ _HMAC: str = generate_hmac_signature(
1047
1081
  timestamp=_TIMESTAMP,
1048
1082
  serial_id=self.serial_id,
1049
1083
  api_key=self.__apikey,
1050
1084
  secret_key=self.__secret_key,
1051
1085
  )
1052
1086
 
1053
- _HEADERS = {
1087
+ _HEADERS: dict[str, str] = {
1054
1088
  "X-HMAC": _HMAC,
1055
1089
  "X-TIMESTAMP": _TIMESTAMP,
1056
1090
  "X-APIKEY": self.__apikey,
@@ -1108,14 +1142,14 @@ class DiagralAPI:
1108
1142
  )
1109
1143
 
1110
1144
  _TIMESTAMP = str(int(time.time()))
1111
- _HMAC = generate_hmac_signature(
1145
+ _HMAC: str = generate_hmac_signature(
1112
1146
  timestamp=_TIMESTAMP,
1113
1147
  serial_id=self.serial_id,
1114
1148
  api_key=self.__apikey,
1115
1149
  secret_key=self.__secret_key,
1116
1150
  )
1117
1151
 
1118
- _HEADERS = {
1152
+ _HEADERS: dict[str, str] = {
1119
1153
  "X-HMAC": _HMAC,
1120
1154
  "X-TIMESTAMP": _TIMESTAMP,
1121
1155
  "X-APIKEY": self.__apikey,
@@ -1216,14 +1250,14 @@ class DiagralAPI:
1216
1250
  )
1217
1251
 
1218
1252
  _TIMESTAMP = str(int(time.time()))
1219
- _HMAC = generate_hmac_signature(
1253
+ _HMAC: str = generate_hmac_signature(
1220
1254
  timestamp=_TIMESTAMP,
1221
1255
  serial_id=self.serial_id,
1222
1256
  api_key=self.__apikey,
1223
1257
  secret_key=self.__secret_key,
1224
1258
  )
1225
1259
 
1226
- _HEADERS = {
1260
+ _HEADERS: dict[str, str] = {
1227
1261
  "X-HMAC": _HMAC,
1228
1262
  "X-TIMESTAMP": _TIMESTAMP,
1229
1263
  "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
  #######################################################
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.0b1
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=LEDHEdf5wRd7QB7flsk3hnb2X_sf1325sD9DPDkBOhM,48587
3
+ pydiagral/constants.py,sha256=2B0TdKxQHA3cpIBxojo43bMW44wN9xKYsHbBRHWsaBk,119
4
+ pydiagral/exceptions.py,sha256=Q5wEpNtiykLs3Ck0W8r1IQAJek_omaQ3jpMOtiiwBUg,1030
5
+ pydiagral/models.py,sha256=AKJd9ywPhislF5R685XNLvEpmJouFlkxWzz1pJXY4tA,54136
6
+ pydiagral/utils.py,sha256=-VxI-lNaC4bU1K4DSmWDhvbsS2bXv5FAGULGKBf1UMU,449
7
+ pydiagral-1.5.0b1.dist-info/METADATA,sha256=CudZesyW4jeyR1tR-N7K5tXVt4no-2nzY7voKEgaecc,48531
8
+ pydiagral-1.5.0b1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
+ pydiagral-1.5.0b1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
10
+ pydiagral-1.5.0b1.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,,