Milesight-Gateway-API 0.0.1__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Stefan Knaak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.1
2
+ Name: Milesight-Gateway-API
3
+ Version: 0.0.1
4
+ Summary: A python client for interacting with Milesight gateway REST API
5
+ Home-page: https://github.com/corgan2222/Milesight-Gateway-API
6
+ Author: Stefan Knaak
7
+ Author-email: stefan.knaak@e-shelter.io
8
+ License: UNKNOWN
9
+ Platform: UNKNOWN
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+
17
+ # Milesight Gateway REST API Client
18
+
19
+ This is a Python client for interacting with the Milesight gateway API.
20
+
21
+ ## Installation
22
+
23
+ You can install this package via pip:
24
+
25
+ ```
26
+ pip install Milesight-Gateway-API
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ```python
32
+ from milesight_gateway_api import MilesightGatewayClient
33
+ client = MilesightGatewayClient(...)
34
+ ```
35
+
36
+
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ Milesight_Gateway_API.egg-info/PKG-INFO
5
+ Milesight_Gateway_API.egg-info/SOURCES.txt
6
+ Milesight_Gateway_API.egg-info/dependency_links.txt
7
+ Milesight_Gateway_API.egg-info/requires.txt
8
+ Milesight_Gateway_API.egg-info/top_level.txt
9
+ milesight_gateway_api/__init__.py
10
+ milesight_gateway_api/milesight_gateway_client.py
@@ -0,0 +1,3 @@
1
+ aiohttp
2
+ pycryptodome
3
+ urllib3
@@ -0,0 +1 @@
1
+ milesight_gateway_api
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.1
2
+ Name: Milesight-Gateway-API
3
+ Version: 0.0.1
4
+ Summary: A python client for interacting with Milesight gateway REST API
5
+ Home-page: https://github.com/corgan2222/Milesight-Gateway-API
6
+ Author: Stefan Knaak
7
+ Author-email: stefan.knaak@e-shelter.io
8
+ License: UNKNOWN
9
+ Platform: UNKNOWN
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+
17
+ # Milesight Gateway REST API Client
18
+
19
+ This is a Python client for interacting with the Milesight gateway API.
20
+
21
+ ## Installation
22
+
23
+ You can install this package via pip:
24
+
25
+ ```
26
+ pip install Milesight-Gateway-API
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ```python
32
+ from milesight_gateway_api import MilesightGatewayClient
33
+ client = MilesightGatewayClient(...)
34
+ ```
35
+
36
+
@@ -0,0 +1,18 @@
1
+ # Milesight Gateway REST API Client
2
+
3
+ This is a Python client for interacting with the Milesight gateway API.
4
+
5
+ ## Installation
6
+
7
+ You can install this package via pip:
8
+
9
+ ```
10
+ pip install Milesight-Gateway-API
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```python
16
+ from milesight_gateway_api import MilesightGatewayClient
17
+ client = MilesightGatewayClient(...)
18
+ ```
@@ -0,0 +1,2 @@
1
+ # milesight_gateway_api/__init__.py
2
+ from .milesight_gateway_client import MilesightGatewayClient # Import your main class here
@@ -0,0 +1,349 @@
1
+ import base64
2
+ import logging
3
+
4
+ import urllib3
5
+ from aiohttp import ClientSession, ClientTimeout
6
+ from aiohttp.client_exceptions import ClientError
7
+ from Crypto.Cipher import AES
8
+ from Crypto.Util.Padding import pad
9
+
10
+ # Disable insecure request warnings
11
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
12
+
13
+ # Setup logging
14
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
15
+
16
+ class MilesightGatewayClient:
17
+ def __init__(self, username, password, secret_key, iv, base_url, port):
18
+ self.username = username
19
+ self.password = password
20
+ self.secret_key = secret_key
21
+ self.iv = iv
22
+ self.url_endpoint_token = f"{base_url}:{port}/api/internal/login"
23
+ self.url_endpoint_devices = f"{base_url}:{port}/api/urdevices"
24
+ self.url_endpoint_packet_forwarder = f"{base_url}:{port}/api/packet-forwarder/network-servers"
25
+ self.url_endpoint_network_server_settings = f"{base_url}:{port}/api/network-server/settings"
26
+ self.url_endpoint_applications = f"{base_url}:{port}/api/urapplications?organizationID=0"
27
+ self.url_endpoint_data_transmission_integration = f"{base_url}:{port}/api/urapplications/{{}}/integrations/{{}}"
28
+ self.url_endpoint_get_payload_codecs = f"{base_url}:{port}/api/payloadcodecs"
29
+ self.url_endpoint_get_payload_codecs_short = f"{base_url}:{port}/api/payloadcodecs-short"
30
+ self.url_endpoint_get_payload_codecs_by_device = f"{base_url}:{port}/api/payloadcodecs/{{}}/device"
31
+ self.url_endpoint_get_payload_codecs_by_id = f"{base_url}:{port}/api/payloadcodecs/{{}}"
32
+ self.url_endpoint_get_profiles = f"{base_url}:{port}/api/urprofiles"
33
+ self.url_endpoint_get_gateway_fleet = f"{base_url}:{port}/api/gateways"
34
+ self.jwt_token = None
35
+ self.headers = None
36
+
37
+ def encrypt_password(self, plain_text_password):
38
+ """Encrypts the password using AES."""
39
+ try:
40
+ cipher = AES.new(self.secret_key, AES.MODE_CBC, self.iv)
41
+ encrypted_password = cipher.encrypt(pad(plain_text_password.encode('utf-8'), AES.block_size))
42
+ return base64.b64encode(encrypted_password).decode('utf-8')
43
+ except Exception as e:
44
+ logging.error(f"Error encrypting password: {e}")
45
+ raise
46
+
47
+ async def get_jwt_token(self, session: ClientSession):
48
+ """Asynchronously requests a JWT token from the gateway."""
49
+ encrypted_password = self.encrypt_password(self.password)
50
+ payload = {
51
+ 'username': self.username,
52
+ 'password': encrypted_password
53
+ }
54
+
55
+ try:
56
+ headers = {'Content-Type': 'application/json'}
57
+ async with session.post(self.url_endpoint_token, json=payload, headers=headers, ssl=False) as response:
58
+ response.raise_for_status()
59
+ data = await response.json()
60
+ self.jwt_token = data.get('jwt')
61
+ self.headers = {'Authorization': f'Bearer {self.jwt_token}'}
62
+ logging.debug("Successfully retrieved JWT token")
63
+ return self.jwt_token
64
+ except ClientError as e:
65
+ logging.error(f"Client error occurred: {e}")
66
+ raise
67
+ except Exception as e:
68
+ logging.error(f"Error during JWT token retrieval: {e}")
69
+ raise
70
+
71
+ async def get_device(self, session: ClientSession, search: str):
72
+ """Asynchronously fetches a device by search string."""
73
+ try:
74
+ async with session.get(f'{self.url_endpoint_devices}?search={search}', headers=self.headers, ssl=False) as response:
75
+ response.raise_for_status()
76
+ data = await response.json()
77
+ logging.debug(f"Successfully retrieved device for search: {search}")
78
+ return data.get('deviceResult', [])
79
+ except ClientError as e:
80
+ logging.error(f"Client error occurred while fetching device: {e}")
81
+ raise
82
+ except Exception as e:
83
+ logging.error(f"Error fetching device: {e}")
84
+ raise
85
+
86
+ async def get_all_devices(self, session: ClientSession, limit=10):
87
+ """Asynchronously fetches all devices using pagination."""
88
+ offset = 0
89
+ all_devices = []
90
+ dev_total_count = 0
91
+
92
+ while True:
93
+ try:
94
+ async with session.get(f'{self.url_endpoint_devices}?offset={offset}&limit={limit}&applicationID=0', headers=self.headers, ssl=False) as response:
95
+ response.raise_for_status()
96
+ data = await response.json()
97
+ devices = data.get('deviceResult', [])
98
+ dev_total_count = data.get('devTotalCount', 0)
99
+
100
+ if not devices:
101
+ break
102
+
103
+ all_devices.extend(devices)
104
+ offset += limit
105
+
106
+ if len(devices) < limit:
107
+ break
108
+
109
+ logging.debug(f"Fetched {len(devices)} devices (offset: {offset})")
110
+ except ClientError as e:
111
+ logging.error(f"Client error occurred while fetching devices: {e}")
112
+ break
113
+ except Exception as e:
114
+ logging.error(f"Error fetching devices: {e}")
115
+ break
116
+
117
+ return all_devices, dev_total_count
118
+
119
+ async def get_packet_forwarder_info(self, session: ClientSession):
120
+ """Asynchronously fetches packet forwarder information from the gateway."""
121
+ try:
122
+ async with session.get(self.url_endpoint_packet_forwarder, headers=self.headers, ssl=False) as response:
123
+ response.raise_for_status()
124
+ data = await response.json()
125
+ servs_count = len(data.get('servs', []))
126
+ logging.debug(f"Successfully retrieved packet forwarder info with {servs_count} servers.")
127
+ return data, servs_count
128
+ except ClientError as e:
129
+ logging.error(f"Client error occurred while fetching packet forwarder info: {e}")
130
+ raise
131
+ except Exception as e:
132
+ logging.error(f"Error fetching packet forwarder info: {e}")
133
+ raise
134
+
135
+ async def get_network_server_settings(self, session: ClientSession):
136
+ """Asynchronously fetches network server settings from the gateway."""
137
+ try:
138
+ async with session.get(self.url_endpoint_network_server_settings, headers=self.headers, ssl=False) as response:
139
+ response.raise_for_status()
140
+ data = await response.json()
141
+ logging.debug("Successfully retrieved network server settings.")
142
+ return data
143
+ except ClientError as e:
144
+ logging.error(f"Client error occurred while fetching network server settings: {e}")
145
+ raise
146
+ except Exception as e:
147
+ logging.error(f"Error fetching network server settings: {e}")
148
+ raise
149
+
150
+ async def get_all_applications(self, session: ClientSession, limit=2):
151
+ """Asynchronously fetches all applications using pagination."""
152
+ offset = 0
153
+ all_applications = []
154
+ total_count = 0
155
+
156
+ while True:
157
+ try:
158
+ async with session.get(f'{self.url_endpoint_applications}&offset={offset}&limit={limit}', headers=self.headers, ssl=False) as response:
159
+ response.raise_for_status()
160
+ data = await response.json()
161
+ applications = data.get('result', [])
162
+ total_count = data.get('totalCount', 0)
163
+
164
+ if not applications:
165
+ break
166
+
167
+ all_applications.extend(applications)
168
+ offset += limit
169
+
170
+ if len(applications) < limit:
171
+ break
172
+
173
+ logging.debug(f"Fetched {len(applications)} applications (offset: {offset})")
174
+ except ClientError as e:
175
+ logging.error(f"Client error occurred while fetching applications: {e}")
176
+ break
177
+ except Exception as e:
178
+ logging.error(f"Error fetching applications: {e}")
179
+ break
180
+
181
+ return all_applications, total_count
182
+
183
+ async def get_data_transmission_integration(self, session: ClientSession, app_id: str, data_transmission_type: str):
184
+ """Asynchronously fetches data transmission integration for a specific application and transmission type."""
185
+ try:
186
+ url = self.url_endpoint_data_transmission_integration.format(app_id, data_transmission_type)
187
+ async with session.get(url, headers=self.headers, ssl=False) as response:
188
+ response.raise_for_status()
189
+ data = await response.json()
190
+ logging.debug(f"Successfully retrieved data transmission integration for app ID {app_id} and type {data_transmission_type}.")
191
+ return data
192
+ except ClientError as e:
193
+ logging.error(f"Client error occurred while fetching data transmission integration: {e}")
194
+ raise
195
+ except Exception as e:
196
+ logging.error(f"Error fetching data transmission integration: {e}")
197
+ raise
198
+
199
+ async def get_payload_codecs(self, session: ClientSession, codec_type: str, limit=10, search: str = None):
200
+ """Asynchronously fetches payload codecs using pagination with an optional search parameter."""
201
+ offset = 0
202
+ all_codecs = []
203
+ total_count = 0
204
+
205
+ while True:
206
+ try:
207
+ # Build the URL with optional search parameter
208
+ url = f'{self.url_endpoint_get_payload_codecs}?type={codec_type}&offset={offset}&limit={limit}'
209
+ if search:
210
+ url += f'&search={search}'
211
+
212
+ async with session.get(url, headers=self.headers, ssl=False) as response:
213
+ response.raise_for_status()
214
+ data = await response.json()
215
+ codecs = data.get('result', [])
216
+ total_count = data.get('totalCount', 0)
217
+
218
+ if not codecs:
219
+ break
220
+
221
+ all_codecs.extend(codecs)
222
+ offset += limit
223
+
224
+ if len(codecs) < limit:
225
+ break
226
+
227
+ logging.debug(f"Fetched {len(codecs)} codecs (offset: {offset}, search: {search})")
228
+ except ClientError as e:
229
+ logging.error(f"Client error occurred while fetching payload codecs: {e}")
230
+ break
231
+ except Exception as e:
232
+ logging.error(f"Error fetching payload codecs: {e}")
233
+ break
234
+
235
+ return all_codecs, total_count
236
+
237
+ async def get_payload_codecs_short(self, session: ClientSession, codec_type: str):
238
+ """Asynchronously fetches payload codecs without pagination for a given type."""
239
+ try:
240
+ url = f'{self.url_endpoint_get_payload_codecs_short}?type={codec_type}'
241
+ async with session.get(url, headers=self.headers, ssl=False) as response:
242
+ response.raise_for_status()
243
+ data = await response.json()
244
+ codecs = data.get('result', [])
245
+ total_count = data.get('totalCount', 0)
246
+
247
+ logging.debug(f"Successfully retrieved {total_count} short payload codecs for type {codec_type}.")
248
+ return codecs, total_count
249
+ except ClientError as e:
250
+ logging.error(f"Client error occurred while fetching payload codecs short: {e}")
251
+ raise
252
+ except Exception as e:
253
+ logging.error(f"Error fetching payload codecs short: {e}")
254
+ raise
255
+
256
+ async def get_payload_codecs_by_device(self, session: ClientSession, dev_eui: str):
257
+ """Asynchronously fetches payload codecs for a specific device."""
258
+ try:
259
+ url = self.url_endpoint_get_payload_codecs_by_device.format(dev_eui)
260
+ async with session.get(url, headers=self.headers, ssl=False) as response:
261
+ response.raise_for_status()
262
+ data = await response.json()
263
+ logging.debug(f"Successfully retrieved payload codecs for device {dev_eui}.")
264
+ return data
265
+ except ClientError as e:
266
+ logging.error(f"Client error occurred while fetching payload codecs for device {dev_eui}: {e}")
267
+ raise
268
+ except Exception as e:
269
+ logging.error(f"Error fetching payload codecs for device {dev_eui}: {e}")
270
+ raise
271
+
272
+ async def get_payload_codecs_by_id(self, session: ClientSession, codec_id: str):
273
+ """Asynchronously fetches payload codecs for a specific codec ID."""
274
+ try:
275
+ url = self.url_endpoint_get_payload_codecs_by_id.format(codec_id)
276
+ async with session.get(url, headers=self.headers, ssl=False) as response:
277
+ response.raise_for_status()
278
+ data = await response.json()
279
+ logging.debug(f"Successfully retrieved payload codecs for ID {codec_id}.")
280
+ return data
281
+ except ClientError as e:
282
+ logging.error(f"Client error occurred while fetching payload codecs for ID {codec_id}: {e}")
283
+ raise
284
+ except Exception as e:
285
+ logging.error(f"Error fetching payload codecs for ID {codec_id}: {e}")
286
+ raise
287
+
288
+ async def get_profiles(self, session: ClientSession, organization_id: str, application_id: str, limit=10, profile_id: str = None):
289
+ """Asynchronously fetches profiles using pagination with optional profileID."""
290
+ offset = 0
291
+ all_profiles = []
292
+ total_count = 0
293
+
294
+ while True:
295
+ try:
296
+ # Build the URL with required organizationID and applicationID and optional profileID
297
+ url = f'{self.url_endpoint_get_profiles}?organizationID={organization_id}&applicationID={application_id}&offset={offset}&limit={limit}'
298
+ if profile_id:
299
+ url += f'&profileID={profile_id}'
300
+
301
+ async with session.get(url, headers=self.headers, ssl=False) as response:
302
+ response.raise_for_status()
303
+ data = await response.json()
304
+ profiles = data.get('result', [])
305
+ total_count = data.get('totalCount', 0)
306
+
307
+ if not profiles:
308
+ break
309
+
310
+ all_profiles.extend(profiles)
311
+ offset += limit
312
+
313
+ if len(profiles) < limit:
314
+ break
315
+
316
+ logging.debug(f"Fetched {len(profiles)} profiles (offset: {offset}, profileID: {profile_id})")
317
+ except ClientError as e:
318
+ logging.error(f"Client error occurred while fetching profiles: {e}")
319
+ break
320
+ except Exception as e:
321
+ logging.error(f"Error fetching profiles: {e}")
322
+ break
323
+
324
+ return all_profiles, total_count
325
+
326
+ async def get_gateway_fleet(self, session: ClientSession, organization_id: str, search: str = None):
327
+ """Asynchronously fetches the gateway fleet with optional search by name or gateway ID."""
328
+ offset = 0
329
+ limit = 20
330
+ try:
331
+ # Build the URL with required organizationID and optional search
332
+ url = f'{self.url_endpoint_get_gateway_fleet}?organizationID={organization_id}&offset={offset}&limit={limit}'
333
+ if search:
334
+ url += f'&search={search}'
335
+
336
+ async with session.get(url, headers=self.headers, ssl=False) as response:
337
+ response.raise_for_status()
338
+ data = await response.json()
339
+ gateways = data.get('result', [])
340
+ total_count = data.get('totalCount', 0)
341
+
342
+ logging.info(f"Successfully retrieved gateway fleet for organizationID {organization_id}, search: {search}.")
343
+ return gateways, total_count
344
+ except ClientError as e:
345
+ logging.error(f"Client error occurred while fetching gateway fleet: {e}")
346
+ raise
347
+ except Exception as e:
348
+ logging.error(f"Error fetching gateway fleet: {e}")
349
+ raise
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,24 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="Milesight-Gateway-API",
5
+ version="0.0.1",
6
+ packages=find_packages(), # Automatically find your package
7
+ install_requires=[ # Dependencies needed for your package
8
+ "aiohttp",
9
+ "pycryptodome",
10
+ "urllib3",
11
+ ],
12
+ author="Stefan Knaak",
13
+ author_email="stefan.knaak@e-shelter.io",
14
+ description="A python client for interacting with Milesight gateway REST API",
15
+ long_description=open('README.md').read(),
16
+ long_description_content_type="text/markdown",
17
+ url="https://github.com/corgan2222/Milesight-Gateway-API",
18
+ classifiers=[
19
+ "Programming Language :: Python :: 3",
20
+ "License :: OSI Approved :: MIT License", # Use appropriate license
21
+ "Operating System :: OS Independent",
22
+ ],
23
+ python_requires='>=3.9', # Minimum Python version requirement
24
+ )