zscaler-sdk-python 0.10.4__tar.gz → 0.10.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/PKG-INFO +2 -2
  2. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/pyproject.toml +5 -5
  3. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/__init__.py +1 -1
  4. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zcc/__init__.py +115 -1
  5. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zcc/client.py +11 -6
  6. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zcc/devices.py +3 -1
  7. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/__init__.py +332 -62
  8. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/cloud_apps.py +2 -7
  9. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/pac_files.py +250 -44
  10. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/url_categories.py +77 -5
  11. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/policies.py +25 -16
  12. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/LICENSE.md +0 -0
  13. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/README.md +0 -0
  14. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/cache/__init__.py +0 -0
  15. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/cache/cache.py +0 -0
  16. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/cache/no_op_cache.py +0 -0
  17. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/cache/zscaler_cache.py +0 -0
  18. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/constants.py +0 -0
  19. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/errors/__init__.py +0 -0
  20. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/errors/error.py +0 -0
  21. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/errors/http_error.py +0 -0
  22. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/errors/zscaler_api_error.py +0 -0
  23. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/exceptions/__init__.py +0 -0
  24. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/exceptions/exceptions.py +0 -0
  25. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/logger.py +0 -0
  26. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/ratelimiter/__init__.py +0 -0
  27. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/ratelimiter/ratelimiter.py +0 -0
  28. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/user_agent.py +0 -0
  29. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/utils.py +0 -0
  30. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zcc/secrets.py +0 -0
  31. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zcon/__init__.py +0 -0
  32. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zcon/activation.py +0 -0
  33. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zcon/admin_and_role_management.py +0 -0
  34. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zcon/client.py +0 -0
  35. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zcon/ecgroups.py +0 -0
  36. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zcon/locations.py +0 -0
  37. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zcon/provisioning.py +0 -0
  38. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zdx/__init__.py +0 -0
  39. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zdx/admin.py +0 -0
  40. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zdx/alerts.py +0 -0
  41. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zdx/apps.py +0 -0
  42. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zdx/devices.py +0 -0
  43. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zdx/filters.py +0 -0
  44. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zdx/inventory.py +0 -0
  45. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zdx/troubleshooting.py +0 -0
  46. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zdx/users.py +0 -0
  47. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zdx/zdx_client.py +0 -0
  48. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/activate.py +0 -0
  49. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/admin_and_role_management.py +0 -0
  50. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/apptotal.py +0 -0
  51. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/audit_logs.py +0 -0
  52. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/authentication_settings.py +0 -0
  53. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/client.py +0 -0
  54. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/cloudappcontrol.py +0 -0
  55. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/device_management.py +0 -0
  56. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/dlp.py +0 -0
  57. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/errors.py +0 -0
  58. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/firewall.py +0 -0
  59. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/forwarding_control.py +0 -0
  60. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/isolation_profile.py +0 -0
  61. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/labels.py +0 -0
  62. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/locations.py +0 -0
  63. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/sandbox.py +0 -0
  64. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/security.py +0 -0
  65. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/ssl_inspection.py +0 -0
  66. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/traffic.py +0 -0
  67. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/url_filtering.py +0 -0
  68. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/users.py +0 -0
  69. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/web_dlp.py +0 -0
  70. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/workload_groups.py +0 -0
  71. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zia/zpa_gateway.py +0 -0
  72. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/README.md +0 -0
  73. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/__init__.py +0 -0
  74. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/app_segments.py +0 -0
  75. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/app_segments_inspection.py +0 -0
  76. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/app_segments_pra.py +0 -0
  77. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/authdomains.py +0 -0
  78. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/certificates.py +0 -0
  79. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/client.py +0 -0
  80. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/cloud_connector_groups.py +0 -0
  81. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/connectors.py +0 -0
  82. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/emergency_access.py +0 -0
  83. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/errors.py +0 -0
  84. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/idp.py +0 -0
  85. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/inspection.py +0 -0
  86. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/isolation.py +0 -0
  87. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/lss.py +0 -0
  88. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/machine_groups.py +0 -0
  89. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/microtenants.py +0 -0
  90. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/posture_profiles.py +0 -0
  91. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/privileged_remote_access.py +0 -0
  92. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/provisioning.py +0 -0
  93. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/saml_attributes.py +0 -0
  94. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/scim_attributes.py +0 -0
  95. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/scim_groups.py +0 -0
  96. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/segment_groups.py +0 -0
  97. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/server_groups.py +0 -0
  98. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/servers.py +0 -0
  99. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/service_edges.py +0 -0
  100. {zscaler_sdk_python-0.10.4 → zscaler_sdk_python-0.10.6}/zscaler/zpa/trusted_networks.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zscaler-sdk-python
3
- Version: 0.10.4
3
+ Version: 0.10.6
4
4
  Summary: Official Python SDK for the Zscaler Products (Beta)
5
5
  Home-page: https://github.com/zscaler/zscaler-sdk-python
6
6
  License: MIT
@@ -22,7 +22,6 @@ Classifier: Topic :: Security
22
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
23
  Provides-Extra: dev
24
24
  Requires-Dist: aenum ; extra == "dev"
25
- Requires-Dist: aiohttp (>=3.11.11)
26
25
  Requires-Dist: arrow
27
26
  Requires-Dist: black (>=24.3.0) ; extra == "dev"
28
27
  Requires-Dist: certifi (>=2024.2.2)
@@ -31,6 +30,7 @@ Requires-Dist: cryptography (>=43.0.0)
31
30
  Requires-Dist: flake8
32
31
  Requires-Dist: flatdict
33
32
  Requires-Dist: idna (>=3.10)
33
+ Requires-Dist: jinja2 (>=3.1.6)
34
34
  Requires-Dist: okta (>=2.9.7)
35
35
  Requires-Dist: pycryptodomex (>=3.20.0)
36
36
  Requires-Dist: pydash (>=8.0.3) ; extra == "dev"
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "zscaler-sdk-python"
3
- version = "0.10.4"
3
+ version = "0.10.6"
4
4
  description = "Official Python SDK for the Zscaler Products (Beta)"
5
5
  authors = ["Zscaler, Inc. <devrel@zscaler.com>"]
6
6
  license = "MIT"
@@ -51,16 +51,16 @@ pytz = ">=2024.2"
51
51
  black = ">=24.3.0"
52
52
  cryptography = ">=43.0.0"
53
53
  okta = ">=2.9.7"
54
- aiohttp = ">=3.11.11"
54
+ jinja2 = ">=3.1.6"
55
55
 
56
- [tool.poetry.dev-dependencies]
56
+ [tool.poetry.group.dev.dependencies]
57
57
  black = ">=24.3.0"
58
58
  pytest = ">=8.3.1"
59
- pytest-asyncio = "^0.23.8"
59
+ pytest-asyncio = "^0.25.3"
60
60
  pytest-mock = "*"
61
61
  pytest-recording = "^0.13.2"
62
62
  pytest-cov = "*"
63
- pyfakefs = ">=5.6.0"
63
+ pyfakefs = ">=5.7.4"
64
64
  isort = "*"
65
65
  wheel = ">=0.45.1"
66
66
  sphinx = "^7.4.7"
@@ -29,7 +29,7 @@ __license__ = "MIT"
29
29
  __contributors__ = [
30
30
  "William Guilherme",
31
31
  ]
32
- __version__ = "0.10.4"
32
+ __version__ = "0.10.6"
33
33
 
34
34
  from zscaler.zdx import ZDXClientHelper # noqa
35
35
  from zscaler.zia import ZIAClientHelper # noqa
@@ -5,7 +5,7 @@ import uuid
5
5
  import time
6
6
  import requests
7
7
  from datetime import datetime, timedelta
8
-
8
+ from box import Box, BoxList
9
9
  from zscaler import __version__
10
10
  from zscaler.user_agent import UserAgent
11
11
  from zscaler.utils import (
@@ -14,7 +14,9 @@ from zscaler.utils import (
14
14
  format_json_response,
15
15
  is_token_expired,
16
16
  retry_with_backoff,
17
+ convert_keys_to_snake
17
18
  )
19
+ from zscaler.ratelimiter.ratelimiter import RateLimiter
18
20
  from zscaler.logger import setup_logging
19
21
  from .devices import DevicesAPI
20
22
  from .secrets import SecretsAPI
@@ -74,6 +76,14 @@ class ZCCClientHelper(ZCCClient):
74
76
  self.url = f"https://api-mobile.{self._env_cloud}.net/papi/public/v1"
75
77
 
76
78
  self.user_agent = UserAgent().get_user_agent_string() # Ensure this returns a string
79
+ # Initialize rate limiter
80
+ # You may want to adjust these parameters as per your rate limit configuration
81
+ self.rate_limiter = RateLimiter(
82
+ get_limit=2, # Adjust as per actual limit
83
+ post_put_delete_limit=2, # Adjust as per actual limit
84
+ get_freq=2, # Adjust as per actual frequency (in seconds)
85
+ post_put_delete_freq=2, # Adjust as per actual frequency (in seconds)
86
+ )
77
87
  self.auth_token = None
78
88
  self.headers = {}
79
89
  self.refreshToken()
@@ -263,3 +273,107 @@ class ZCCClientHelper(ZCCClient):
263
273
  def secrets(self):
264
274
  """The interface object for the :ref:`ZCC Secrets interface <zcc-secrets>`."""
265
275
  return SecretsAPI(self)
276
+
277
+ def get_paginated_data(
278
+ self,
279
+ path=None,
280
+ expected_status_code=200,
281
+ page=None,
282
+ pagesize=None,
283
+ search=None,
284
+ ):
285
+ """
286
+ Fetches paginated data from the API based on specified parameters and handles pagination.
287
+
288
+ Args:
289
+ path (str): The API endpoint path to send requests to.
290
+ expected_status_code (int): The expected HTTP status code for a successful request.
291
+ page (int, optional): Specific page number to fetch (1-based).
292
+ pagesize (int, optional): Number of items per page, default is 100, max is 10,000.
293
+ search (str, optional): Search query to filter the results.
294
+
295
+ Returns:
296
+ tuple: A tuple containing:
297
+ - BoxList: A list of fetched items wrapped in a BoxList for easy access.
298
+ - str: An error message if any occurred during the data fetching process.
299
+ """
300
+ logger = logging.getLogger(__name__)
301
+
302
+ ERROR_MESSAGES = {
303
+ "UNEXPECTED_STATUS": "Unexpected status code {status_code} received for page {page}.",
304
+ "EMPTY_RESULTS": "No results found for page {page}.",
305
+ }
306
+
307
+ # ✅ Ensure 'pageSize' is always converted to camelCase
308
+ params = {
309
+ "page": page if page is not None else 1, # Default is 1-based page
310
+ "pageSize": pagesize if pagesize is not None else 100, # Default is 100, max is 10,000
311
+ }
312
+
313
+ # Add optional filters to the params if provided
314
+ if search:
315
+ params["search"] = search
316
+
317
+ # If the user specifies a single page, fetch only that page
318
+ if page is not None:
319
+ response = self.send("GET", path=path, params=params)
320
+ if response.status_code != expected_status_code:
321
+ error_msg = ERROR_MESSAGES["UNEXPECTED_STATUS"].format(status_code=response.status_code, page=params["page"])
322
+ logger.error(error_msg)
323
+ return BoxList([]), error_msg
324
+
325
+ response_data = response.json()
326
+ if not isinstance(response_data, list):
327
+ error_msg = ERROR_MESSAGES["EMPTY_RESULTS"].format(page=params["page"])
328
+ logger.warn(error_msg)
329
+ return BoxList([]), error_msg
330
+
331
+ data = convert_keys_to_snake(response_data)
332
+ return BoxList(data), None
333
+
334
+ # If no page is specified, iterate through pages to fetch all items
335
+ ret_data = []
336
+
337
+ try:
338
+ while True:
339
+ should_wait, delay = self.rate_limiter.wait("GET")
340
+ if should_wait:
341
+ time.sleep(delay)
342
+
343
+ # Send the request to the API
344
+ response = self.send("GET", path=path, params=params)
345
+
346
+ # Check for unexpected status code
347
+ if response.status_code != expected_status_code:
348
+ error_msg = ERROR_MESSAGES["UNEXPECTED_STATUS"].format(
349
+ status_code=response.status_code, page=params["page"]
350
+ )
351
+ logger.error(error_msg)
352
+ return BoxList([]), error_msg
353
+
354
+ # Parse the response as a flat list of items
355
+ response_data = response.json()
356
+ if not isinstance(response_data, list):
357
+ error_msg = ERROR_MESSAGES["EMPTY_RESULTS"].format(page=params["page"])
358
+ logger.warn(error_msg)
359
+ return BoxList([]), error_msg
360
+
361
+ data = convert_keys_to_snake(response_data)
362
+ ret_data.extend(data)
363
+
364
+ # Stop if fewer items than pageSize are returned (indicating last page)
365
+ if len(data) < params["pageSize"]:
366
+ break
367
+
368
+ # Move to the next page
369
+ params["page"] += 1
370
+
371
+ finally:
372
+ time.sleep(2) # Ensure a delay between requests regardless of outcome
373
+
374
+ if not ret_data:
375
+ error_msg = ERROR_MESSAGES["EMPTY_RESULTS"].format(page=params["page"])
376
+ logger.warn(error_msg)
377
+ return BoxList([]), error_msg
378
+
379
+ return BoxList(ret_data), None
@@ -36,13 +36,18 @@ class ZCCClient:
36
36
  pass
37
37
 
38
38
  def get_paginated_data(
39
+ # self,
40
+ # path=None,
41
+ # params=None,
42
+ # search=None,
43
+ # search_field="name",
44
+ # page=None,
45
+ # pagesize=20,
39
46
  self,
40
- path=None,
41
- params=None,
42
- search=None,
43
- search_field="name",
44
- page=None,
45
- pagesize=20,
47
+ path: str = None,
48
+ data_key_name: str = None,
49
+ data_per_page: int = 500,
50
+ expected_status_code=200,
46
51
  ):
47
52
  """
48
53
  Send a GET request to the ZCC API to fetch all pages of a resources.
@@ -148,7 +148,9 @@ class DevicesAPI:
148
148
  else:
149
149
  raise ValueError("Invalid os_type specified. Check the Zscaler documentation for valid os_type options.")
150
150
 
151
- return self.rest.get("getDevices", **payload)
151
+ list, _ = self.rest.get_paginated_data(path="getDevices", **payload)
152
+ return list
153
+ # return self.rest.get("getDevices", **payload)
152
154
 
153
155
  def remove_devices(self, force: bool = False, **kwargs):
154
156
  """