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.
- PyNetioConf/ESPCore/__init__.py +5 -0
- PyNetioConf/ESPCore/esp_200_device.py +16 -0
- PyNetioConf/ESPCore/esp_300_device.py +16 -0
- PyNetioConf/ESPCore/esp_400_device.py +16 -0
- PyNetioConf/ESPCore/esp_500_device.py +1826 -0
- PyNetioConf/ESPCore/esp_520_device.py +348 -0
- PyNetioConf/ESPCore/esp_5beta_device.py +263 -0
- PyNetioConf/ESPCore/esp_api.py +383 -0
- PyNetioConf/ESPCore/esp_device.py +1516 -0
- PyNetioConf/ESPCore/esp_device_init.py +328 -0
- PyNetioConf/ESPCore/ws_api.py +391 -0
- PyNetioConf/NetioManager.py +183 -0
- PyNetioConf/__init__.py +1 -0
- PyNetioConf/constants.py +12 -0
- PyNetioConf/exceptions.py +56 -0
- PyNetioConf/netio_device.py +1727 -0
- pynetioconf-0.3.0b1.dist-info/METADATA +173 -0
- pynetioconf-0.3.0b1.dist-info/RECORD +20 -0
- pynetioconf-0.3.0b1.dist-info/WHEEL +4 -0
- pynetioconf-0.3.0b1.dist-info/licenses/LICENSE +21 -0
|
@@ -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
|