PyNetioConf 0.3.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.
@@ -0,0 +1,383 @@
1
+ """
2
+ This module contains functions for sending requests to the device's API on EPS based devices.
3
+ """
4
+
5
+ import logging
6
+
7
+ import requests
8
+
9
+ from ..exceptions import CommunicationError, InvalidParameterValueError
10
+ from ..netio_device import NETIODevice
11
+ #from . import ESP200Device, ESP300Device, ESP400Device, ESP500Device
12
+
13
+ from typing import Any, TYPE_CHECKING, Optional
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ # def init_device(
19
+ # version: int,
20
+ # host: str,
21
+ # username: str,
22
+ # password: str,
23
+ # keep_alive: bool = True,
24
+ # netio_manager: Optional["NetioManager"] = None, # type: ignore # noqa
25
+ # use_https: bool = False,
26
+ # **kwargs: Any,
27
+ # ) -> NETIODevice:
28
+ # if netio_manager is None:
29
+ # raise InvalidParameterValueError
30
+ #
31
+ # if version == 2:
32
+ # netio_device = ESP200Device(
33
+ # host,
34
+ # username,
35
+ # password,
36
+ # "",
37
+ # "",
38
+ # keep_alive,
39
+ # netio_manager, # pyright: ignore[reportUnknownArgumentType]
40
+ # use_https,
41
+ # )
42
+ # return netio_device
43
+ #
44
+ # if version == 3:
45
+ # netio_device = ESP300Device(
46
+ # host,
47
+ # username,
48
+ # password,
49
+ # "",
50
+ # "",
51
+ # keep_alive,
52
+ # netio_manager, # pyright: ignore[reportUnknownArgumentType]
53
+ # use_https,
54
+ # )
55
+ # return netio_device
56
+ #
57
+ # if version == 4:
58
+ # netio_device = ESP400Device(
59
+ # host,
60
+ # username,
61
+ # password,
62
+ # "",
63
+ # "",
64
+ # keep_alive,
65
+ # netio_manager, # pyright: ignore[reportUnknownArgumentType]
66
+ # use_https,
67
+ # )
68
+ # return netio_device
69
+ #
70
+ # if version == 5:
71
+ # netio_device = ESP500Device(
72
+ # host,
73
+ # username,
74
+ # password,
75
+ # "",
76
+ # "",
77
+ # keep_alive,
78
+ # netio_manager, # pyright: ignore[reportUnknownArgumentType]
79
+ # use_https,
80
+ # )
81
+ # return netio_device
82
+ #
83
+ # raise InvalidParameterValueError(
84
+ # f"There is no firmware with major version {version} supported"
85
+ # )
86
+ #
87
+ #
88
+ def check_connectivity(fw_object: NETIODevice, timeout: int = 60) -> float:
89
+ try:
90
+ protocol = "https" if fw_object.use_https else "http"
91
+ except AttributeError:
92
+ protocol = "http"
93
+
94
+ try:
95
+ logger.debug(f"Checking connection health of {fw_object.host}.")
96
+ response = requests.get(
97
+ f"{protocol}://{fw_object.host}", timeout=timeout, verify=False
98
+ )
99
+ except:
100
+ logger.warn(f"Couldn't connect to device {fw_object.host}.")
101
+ return -1
102
+
103
+ logger.debug(
104
+ f"Response time of device {fw_object.host} is {response.elapsed.total_seconds() * 1000.0}ms."
105
+ )
106
+ return response.elapsed.total_seconds()
107
+
108
+
109
+ def send_request(
110
+ fw_object: NETIODevice,
111
+ command: str,
112
+ data: dict[str, Any] | None = None,
113
+ timeout: int = 60,
114
+ endpoint: str = "api",
115
+ close: bool = False,
116
+ ) -> requests.Response:
117
+ """
118
+ Send an HTTP request to the device's API and return a `requests.Response` object.
119
+
120
+ Parameters
121
+ ----------
122
+ fw_object: NETIODevice
123
+ The NETIODevice compatible object of the device.
124
+ command: str
125
+ API command to send to the device.
126
+ endpoint: str
127
+ The endpoint to send the request to, defaults to 'api'. This means the final request will be sent to
128
+ http://<device_ip>/<api>
129
+ data: dict
130
+ Dictionary containing data for the request, used to specify actions for the API command.
131
+ timeout: int
132
+ Timeout duration, if the device doesn't respond in this time, abandon the request.
133
+ close: bool
134
+ Close the session after the request is sent.
135
+
136
+ Returns
137
+ -------
138
+ A response from the api request in the `requests.Response` format.
139
+ """
140
+ try:
141
+ protocol = "https" if fw_object.use_https else "http"
142
+ except AttributeError:
143
+ protocol = "http"
144
+ netio_host = f"{protocol}://{fw_object.host}/{endpoint}"
145
+ if endpoint == "api":
146
+ json_payload = {
147
+ "sessionId": fw_object.session_id,
148
+ "action": command,
149
+ } # better session id?
150
+ else:
151
+ json_payload: dict[str, Any] = {}
152
+
153
+ if data is not None:
154
+ json_payload["data"] = data
155
+
156
+ session = requests.Session()
157
+
158
+ try:
159
+ logger.debug(f"Sending request to {netio_host} with payload {json_payload}")
160
+ response = session.post(
161
+ url=netio_host, json=json_payload, timeout=timeout, verify=False
162
+ )
163
+ except requests.exceptions.ConnectionError:
164
+ logger.error(f"Cannot connect to device {fw_object.host}")
165
+ raise CommunicationError("Cannot connect to device")
166
+
167
+ # handle errors and repeat request
168
+ if "errors" in response.json():
169
+ if response.json()["errors"][0]["code"] == 1000:
170
+ # session expired
171
+ logger.info("Session expired, logging in again")
172
+ fw_object.login(fw_object.username, fw_object.password)
173
+ logger.debug(f"Sending request to {netio_host} with payload {json_payload}")
174
+ response = session.post(
175
+ url=netio_host, json=json_payload, timeout=timeout, verify=False
176
+ )
177
+ else:
178
+ raise CommunicationError(
179
+ "Error in response: " + response.json()["errors"][0]["message"]
180
+ )
181
+
182
+ if close:
183
+ logger.debug(f"Closing session with {netio_host}")
184
+ session.close()
185
+ return response
186
+
187
+
188
+ def send_file(
189
+ fw_object: NETIODevice, url_path: str, file, timeout: int = 600
190
+ ) -> requests.Response:
191
+ """
192
+ Upload a file to the device on the specified URL path, returns the response of the request.
193
+
194
+ Parameters
195
+ ----------
196
+ fw_object: NETIODevice
197
+ The NETIODevice compatible object of the device.
198
+ url_path: str
199
+ The location on the device where the file will be uploaded
200
+ file
201
+ The file to be uploaded. Based on the size of the file you might change the timeout.
202
+ timeout: int
203
+ The maximum time for the request
204
+
205
+ Returns
206
+ -------
207
+ A response from the api request in the `requests.Response` format.
208
+ """
209
+ try:
210
+ protocol = "https" if fw_object.use_https else "http"
211
+ except AttributeError:
212
+ protocol = "http"
213
+ netio_host = f"{protocol}://{fw_object.host}{url_path}"
214
+
215
+ session = requests.Session()
216
+ session.cookies.update({"sessionId": fw_object.session_id})
217
+
218
+ try:
219
+ if url_path == "/upload/config":
220
+ logger.debug(f"Uploading config file to {netio_host}.")
221
+ response = session.post(
222
+ netio_host,
223
+ files={
224
+ "sessionId": (None, fw_object.session_id),
225
+ "data": ("config.json", file, "application/json"),
226
+ },
227
+ headers={"DNT": "1"},
228
+ verify=False,
229
+ )
230
+ elif url_path == "/upload/firmware":
231
+ logger.debug(f"Uploading firmware to {netio_host}")
232
+ response = session.post(
233
+ url=netio_host, files={"file": file}, timeout=timeout, verify=False
234
+ )
235
+ elif "upload/ssl/" in url_path:
236
+ cert_types = ["mqtt_client_cert", "mqtt_client_key", "srvkey", "srvcert", "mqtt_root_ca"]
237
+ if "mqtt_client_key" in url_path:
238
+ filetype = "application/x-iwork-keynote-sffkey"
239
+ elif any(cert in url_path for cert in cert_types):
240
+ filetype = "application/x-x509-ca-cert"
241
+ logger.debug(f"Uploading SSL certificate to {netio_host}")
242
+ response = session.post(
243
+ url=netio_host,
244
+ files={"file": ("file", file, filetype)},
245
+ headers={
246
+ "Content-Disposition": f'form-data; name="file"; filename="file"'
247
+ },
248
+ timeout=timeout,
249
+ verify=False,
250
+ )
251
+ else:
252
+ logger.debug(f"Uploading generic file to {netio_host}")
253
+ response = session.post(
254
+ url=netio_host, files={"file": file}, timeout=timeout, verify=False
255
+ )
256
+ except requests.exceptions.ConnectionError:
257
+ logger.error(f"Cannot connect to device {fw_object.host}")
258
+ raise CommunicationError("Cannot connect to device")
259
+
260
+ try:
261
+ response_status: str = response.json()["status"]
262
+ except KeyError:
263
+ response_status = "failed"
264
+ except requests.exceptions.JSONDecodeError:
265
+ response_status = "unknown"
266
+ logger.debug(f"File upload status: {response_status}")
267
+
268
+ if response_status == "failed":
269
+ logger.error(f"Upload failed, logging out, retrying.")
270
+ if check_connectivity(fw_object) != -1:
271
+ fw_object.login(fw_object.username, fw_object.password, logout=True)
272
+ else:
273
+ raise CommunicationError(
274
+ "Couldn't connect to device after a failed request."
275
+ )
276
+ try:
277
+ if url_path == "/upload/config":
278
+ logger.debug(f"Uploading config file to {netio_host}.")
279
+ response = session.post(
280
+ netio_host,
281
+ files={
282
+ "sessionId": fw_object.session_id,
283
+ "data": ("config.json", file, "application/json"),
284
+ },
285
+ headers={"DNT": "1"},
286
+ verify=False,
287
+ )
288
+ elif url_path == "/cfgimport":
289
+ logger.debug(f"Uploading config file to {netio_host}.")
290
+ response = session.post(
291
+ netio_host,
292
+ files={"file": ("config.json", file, "application/json")},
293
+ verify=False,
294
+ )
295
+ elif url_path == "/upload/firmware":
296
+ logger.debug(f"Uploading firmware to {netio_host}")
297
+ response = session.post(
298
+ url=netio_host, files={"file": file}, timeout=timeout, verify=False
299
+ )
300
+ elif "upload/ssl/" in url_path:
301
+ if "mqtt_client_key" in url_path:
302
+ filetype = "application/x-iwork-keynote-sffkey"
303
+ elif "mqtt_client_cert" in url_path or "mqtt_root_ca" in url_path:
304
+ filetype = "application/x-x509-ca-cert"
305
+ logger.debug(f"Uploading SSL certificate to {netio_host}")
306
+ response = session.post(
307
+ url=netio_host,
308
+ files={"file": ("file", file, filetype)},
309
+ headers={
310
+ "Content-Disposition": f'form-data; name="file"; filename="file"'
311
+ },
312
+ timeout=timeout,
313
+ verify=False,
314
+ )
315
+ else:
316
+ logger.debug(f"Uploading generic file to {netio_host}")
317
+ response = session.post(
318
+ url=netio_host, files={"file": file}, timeout=timeout, verify=False
319
+ )
320
+ except requests.exceptions.ConnectionError:
321
+ logger.error(f"Cannot connect to device {fw_object.host}.")
322
+ raise CommunicationError("Cannot connect to device.")
323
+ if response == "failed":
324
+ logger.error("Could not send file to device.")
325
+ raise CommunicationError("Cannot send file to device.")
326
+
327
+ return response
328
+
329
+
330
+ def get_file(
331
+ fw_object: NETIODevice,
332
+ url_path: str,
333
+ timeout: int = 600,
334
+ ) -> requests.Response:
335
+ """
336
+ Download a file from the device on the specified URL path, returns the response of the request.
337
+
338
+ Parameters
339
+ ----------
340
+ fw_object: NETIODevice
341
+ The NETIODevice compatible object of the device.
342
+ url_path: str
343
+ The location on the device where the file will be downloaded from
344
+ timeout: int
345
+ The maximum time for the request
346
+
347
+ Returns
348
+ -------
349
+ A response from the api request in the `requests.Response` format.
350
+ """
351
+ protocol = "https" if fw_object.use_https else "http"
352
+ netio_host = f"{protocol}://{fw_object.host}{url_path}"
353
+
354
+ session = requests.Session()
355
+
356
+ try:
357
+ logger.debug(f"Downloading file from {netio_host}")
358
+ response = session.get(
359
+ url=netio_host,
360
+ cookies={"sessionId": fw_object.session_id},
361
+ timeout=timeout,
362
+ verify=False,
363
+ )
364
+ except requests.exceptions.ConnectionError:
365
+ raise CommunicationError("Cannot connect to device")
366
+
367
+ if response == "failed":
368
+ logger.error(f"Request failed, logging out, retrying.")
369
+ fw_object.login(fw_object.username, fw_object.password, logout=True)
370
+ try:
371
+ logger.debug(f"Downloading file from {netio_host}")
372
+ response = session.get(
373
+ url=netio_host,
374
+ cookies={"sessionId": fw_object.session_id},
375
+ timeout=timeout,
376
+ verify=False,
377
+ )
378
+ except requests.exceptions.ConnectionError:
379
+ raise CommunicationError("Cannot connect to device")
380
+ if response == "failed":
381
+ raise CommunicationError("Cannot send file to device")
382
+
383
+ return response