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,1516 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The implementation of the NETIODevice class for ESP firmware based devices.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import atexit
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import sys
|
|
9
|
+
import threading
|
|
10
|
+
from time import sleep
|
|
11
|
+
|
|
12
|
+
if sys.version_info >= (3, 12):
|
|
13
|
+
from typing import IO, Any, AnyStr, Dict, List, Tuple, override
|
|
14
|
+
else:
|
|
15
|
+
from typing import IO, Any, AnyStr, Dict, List, Tuple
|
|
16
|
+
|
|
17
|
+
from typing_extensions import override
|
|
18
|
+
from xml.etree import ElementTree as ET
|
|
19
|
+
|
|
20
|
+
import requests
|
|
21
|
+
|
|
22
|
+
from ..exceptions import (
|
|
23
|
+
CommunicationError,
|
|
24
|
+
ElementNotFound,
|
|
25
|
+
FeatureNotSupported,
|
|
26
|
+
InvalidParameterValueError,
|
|
27
|
+
InvalidSocketIndex,
|
|
28
|
+
ProtocolNotEnabled,
|
|
29
|
+
)
|
|
30
|
+
from ..netio_device import NETIODevice
|
|
31
|
+
from ..NetioManager import NetioManager
|
|
32
|
+
from . import esp_api
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ESPDevice(NETIODevice):
|
|
36
|
+
"""
|
|
37
|
+
A base class for ESPDevices based on the 3.x.x firmware.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
host: str,
|
|
43
|
+
username: str,
|
|
44
|
+
password: str,
|
|
45
|
+
sn_number: str,
|
|
46
|
+
hostname: str,
|
|
47
|
+
keep_alive: bool,
|
|
48
|
+
netio_manager: NetioManager | None,
|
|
49
|
+
use_https: bool = False,
|
|
50
|
+
) -> None:
|
|
51
|
+
super().__init__(
|
|
52
|
+
host,
|
|
53
|
+
username,
|
|
54
|
+
password,
|
|
55
|
+
sn_number,
|
|
56
|
+
hostname,
|
|
57
|
+
keep_alive,
|
|
58
|
+
netio_manager,
|
|
59
|
+
use_https,
|
|
60
|
+
)
|
|
61
|
+
self.logger = logging.getLogger(__name__)
|
|
62
|
+
self.session_id = self.login(username, password)
|
|
63
|
+
self.supported_features = self.get_features()
|
|
64
|
+
self.output_count: int = self.supported_features["outputCount"]
|
|
65
|
+
self.user_permissions = self.get_current_user()["privileges"]
|
|
66
|
+
if keep_alive:
|
|
67
|
+
self._ka_thread = threading.Timer(120, self._keep_alive)
|
|
68
|
+
self._ka_thread.daemon = True
|
|
69
|
+
self._ka_thread.start()
|
|
70
|
+
atexit.register(self._cleanup)
|
|
71
|
+
|
|
72
|
+
# region Session
|
|
73
|
+
|
|
74
|
+
def login(self, username: str, password: str, logout: bool = False) -> str:
|
|
75
|
+
if logout:
|
|
76
|
+
self.logger.debug("Logging out of the current session.")
|
|
77
|
+
self.logout()
|
|
78
|
+
|
|
79
|
+
response = esp_api.send_request(
|
|
80
|
+
self, "login", {"username": username, "password": password}, timeout=10
|
|
81
|
+
)
|
|
82
|
+
if "data" in response.json():
|
|
83
|
+
self.session_id = response.json()["data"]["sessionId"]
|
|
84
|
+
self.logger.debug(
|
|
85
|
+
f"Logged in to device {self.host} with session ID {self.session_id}"
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
raise CommunicationError("Cannot login to device")
|
|
89
|
+
return self.session_id
|
|
90
|
+
|
|
91
|
+
def logout(self) -> None:
|
|
92
|
+
if self.session_id == "":
|
|
93
|
+
self.logger.debug(
|
|
94
|
+
f"Device {self.host} is not logged in, ignoring logout request."
|
|
95
|
+
)
|
|
96
|
+
pass
|
|
97
|
+
esp_api.send_request(self, "logout", {})
|
|
98
|
+
self.session_id = ""
|
|
99
|
+
self.logger.debug(f"Logged out of device {self.host}")
|
|
100
|
+
|
|
101
|
+
def ping(self) -> bool:
|
|
102
|
+
response = esp_api.send_request(self, "ping", {})
|
|
103
|
+
status = "OK" if response.status_code == 200 else response.json()
|
|
104
|
+
self.logger.debug(
|
|
105
|
+
f"Pinging device {self.host}, response: {status}. This should be empty if ok."
|
|
106
|
+
)
|
|
107
|
+
if response.status_code == 200:
|
|
108
|
+
return True
|
|
109
|
+
else:
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
def _keep_alive(self) -> None:
|
|
113
|
+
self.logger.debug(f"Sending keep-alive to device {self.host}")
|
|
114
|
+
self._ka_thread = threading.Timer(120, self._keep_alive)
|
|
115
|
+
self._ka_thread.daemon = True
|
|
116
|
+
self._ka_thread.start()
|
|
117
|
+
self.ping()
|
|
118
|
+
|
|
119
|
+
# endregion
|
|
120
|
+
|
|
121
|
+
# region Device Information
|
|
122
|
+
|
|
123
|
+
def get_version(self) -> str:
|
|
124
|
+
response = esp_api.send_request(self, "getVersion", {}).json()["data"][
|
|
125
|
+
"version"
|
|
126
|
+
]
|
|
127
|
+
response = response.split(" ")[0]
|
|
128
|
+
self.logger.debug(f"Received device version: {response}")
|
|
129
|
+
return response
|
|
130
|
+
|
|
131
|
+
def get_version_detailed(self) -> str:
|
|
132
|
+
response = esp_api.send_request(self, "getVersion").json()["data"]
|
|
133
|
+
self.logger.debug(f"Received device version information: {response}")
|
|
134
|
+
return response
|
|
135
|
+
|
|
136
|
+
def get_features(self) -> dict[str, Any]:
|
|
137
|
+
response = esp_api.send_request(self, "getFeatures").json()["data"]
|
|
138
|
+
self.logger.debug(f"Received device supported features: {response}")
|
|
139
|
+
return response
|
|
140
|
+
|
|
141
|
+
# endregion
|
|
142
|
+
|
|
143
|
+
# region Sockets
|
|
144
|
+
# region Socket Control
|
|
145
|
+
|
|
146
|
+
def get_output_state(self, output_id: int) -> bool:
|
|
147
|
+
self._check_socket_index(output_id)
|
|
148
|
+
return self.get_output_states()[output_id - 1][1]
|
|
149
|
+
|
|
150
|
+
def set_output(self, output_id: int, state: bool) -> None:
|
|
151
|
+
if "can_control_outputs" not in self.user_permissions:
|
|
152
|
+
raise PermissionError(
|
|
153
|
+
"You don't have permission to control outputs on this device."
|
|
154
|
+
)
|
|
155
|
+
self._check_socket_index(output_id)
|
|
156
|
+
esp_api.send_request(
|
|
157
|
+
self, "setOutputState", {"output": output_id, "value": state}
|
|
158
|
+
)
|
|
159
|
+
self.logger.debug(
|
|
160
|
+
f"Setting output {output_id} on device {self.host} to {state}."
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def set_outputs_unified(self, state: bool) -> None:
|
|
164
|
+
for output in range(1, self.output_count + 1):
|
|
165
|
+
self.set_output(output, state)
|
|
166
|
+
sleep(0.1)
|
|
167
|
+
|
|
168
|
+
def rename_output(self, output_id: int, output_name: str) -> None:
|
|
169
|
+
if "can_alter_outputs" not in self.user_permissions:
|
|
170
|
+
raise PermissionError(
|
|
171
|
+
"You don't have permission to alter outputs on this device."
|
|
172
|
+
)
|
|
173
|
+
self._check_socket_index(output_id)
|
|
174
|
+
response = esp_api.send_request(self, "getOutputDetailList")
|
|
175
|
+
json_data = response.json()["data"][output_id - 1]
|
|
176
|
+
interrupt_delay = json_data["interruptDelay"]
|
|
177
|
+
default = json_data["default"]
|
|
178
|
+
pui = json_data["powerUpInterval"]
|
|
179
|
+
request_data = {
|
|
180
|
+
"id": output_id,
|
|
181
|
+
"name": output_name,
|
|
182
|
+
"resetDelay": interrupt_delay,
|
|
183
|
+
"default": default,
|
|
184
|
+
"powerUpInterval": pui,
|
|
185
|
+
}
|
|
186
|
+
self.logger.debug(
|
|
187
|
+
f"Renaming output {output_id} to {output_name} on device {self.host}"
|
|
188
|
+
)
|
|
189
|
+
esp_api.send_request(self, "setOutputBase", request_data)
|
|
190
|
+
|
|
191
|
+
def reset_output(self, output_id: int) -> None:
|
|
192
|
+
if "can_control_outputs" not in self.user_permissions:
|
|
193
|
+
raise PermissionError(
|
|
194
|
+
"You don't have permission to control outputs on this device."
|
|
195
|
+
)
|
|
196
|
+
self._check_socket_index(output_id)
|
|
197
|
+
esp_api.send_request(self, "resetOutputState", {"output": output_id})
|
|
198
|
+
self.logger.debug(f"Resetting output {output_id} on device {self.host}.")
|
|
199
|
+
|
|
200
|
+
def set_output_schedule(
|
|
201
|
+
self, output_id: int, schedule_id: int, enabled: bool | None = None
|
|
202
|
+
) -> None:
|
|
203
|
+
if enabled is None:
|
|
204
|
+
enabled = True
|
|
205
|
+
if "can_control_outputs" not in self.user_permissions:
|
|
206
|
+
raise PermissionError(
|
|
207
|
+
"You don't have permission to control outputs on this device."
|
|
208
|
+
)
|
|
209
|
+
self._check_socket_index(output_id)
|
|
210
|
+
request_data = {
|
|
211
|
+
"id": output_id,
|
|
212
|
+
"scheduleId": f"{schedule_id}",
|
|
213
|
+
"enable": enabled,
|
|
214
|
+
}
|
|
215
|
+
esp_api.send_request(self, "setOutputSchedule", request_data)
|
|
216
|
+
self.logger.debug(
|
|
217
|
+
f"Setting schedule {schedule_id} on output {output_id}, netio_device: {self.host}."
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def get_output_schedule(self, output_id: int) -> dict[str, Any]:
|
|
221
|
+
output_data = self.get_output_data(output_id)
|
|
222
|
+
schedule = output_data["schedule"]
|
|
223
|
+
return schedule
|
|
224
|
+
|
|
225
|
+
def get_output_schedule_id(self, output_id: int) -> int:
|
|
226
|
+
output_schedule = self.get_output_schedule(output_id)
|
|
227
|
+
return output_schedule["id"]
|
|
228
|
+
|
|
229
|
+
def set_output_schedule_state(self, output_id: int, schedule_enabled: bool) -> None:
|
|
230
|
+
self.logger.debug(
|
|
231
|
+
f"Setting schedule state {schedule_enabled} on output {output_id} to {schedule_enabled}, "
|
|
232
|
+
f"device {self.host}."
|
|
233
|
+
)
|
|
234
|
+
current_output_schedule = self.get_output_schedule_id(output_id)
|
|
235
|
+
self.set_output_schedule(
|
|
236
|
+
current_output_schedule, current_output_schedule, schedule_enabled
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
def set_output_schedule_by_name(
|
|
240
|
+
self, output_id: int, schedule_name: str, enabled: bool | None = None
|
|
241
|
+
) -> None:
|
|
242
|
+
schedule_id = self.get_schedule_id(schedule_name)
|
|
243
|
+
self.set_output_schedule(output_id, schedule_id, enabled)
|
|
244
|
+
|
|
245
|
+
# endregion
|
|
246
|
+
|
|
247
|
+
# region Socket Information
|
|
248
|
+
|
|
249
|
+
def get_output_data(self, output_id: int) -> dict[str, Any]:
|
|
250
|
+
self._check_socket_index(output_id)
|
|
251
|
+
output_info = self.get_outputs_data()[output_id - 1]
|
|
252
|
+
return output_info
|
|
253
|
+
|
|
254
|
+
def get_outputs_data(self) -> list[dict[str, Any]]:
|
|
255
|
+
output_data = esp_api.send_request(self, "getOutputDetailList").json()["data"]
|
|
256
|
+
self.logger.debug(
|
|
257
|
+
f"Received output list form device {self.host}: {output_data}"
|
|
258
|
+
)
|
|
259
|
+
return output_data
|
|
260
|
+
|
|
261
|
+
def get_output_states(self) -> list[tuple[int, bool]]:
|
|
262
|
+
outputs = self.get_outputs_data()
|
|
263
|
+
value_list = [(output["outputId"], output["on"]) for output in outputs]
|
|
264
|
+
self.logger.debug(
|
|
265
|
+
f"Received socket states from device {self.host}: {value_list}"
|
|
266
|
+
)
|
|
267
|
+
return value_list
|
|
268
|
+
|
|
269
|
+
# endregion
|
|
270
|
+
|
|
271
|
+
# endregion
|
|
272
|
+
|
|
273
|
+
# region Device Configuration
|
|
274
|
+
# region Network
|
|
275
|
+
|
|
276
|
+
def get_wifi_settings(self) -> dict[str, Any]:
|
|
277
|
+
if self.supported_features["wifi"] == "no":
|
|
278
|
+
raise FeatureNotSupported("Wi-Fi is not supported on this device.")
|
|
279
|
+
response = esp_api.send_request(self, "getWifiSettings")
|
|
280
|
+
self.logger.debug(f"Wi-Fi settings on {self.host}: {response.json()['data']}")
|
|
281
|
+
return response.json()["data"]
|
|
282
|
+
|
|
283
|
+
def set_wifi_settings(self, ssid: str, password: str) -> None:
|
|
284
|
+
if self.supported_features["wifi"] == "no":
|
|
285
|
+
raise FeatureNotSupported("Wi-Fi is not supported on this device.")
|
|
286
|
+
request_data = {"ssid": ssid, "password": password}
|
|
287
|
+
response = esp_api.send_request(self, "setWifiSettings", request_data)
|
|
288
|
+
if response.status_code == 200:
|
|
289
|
+
self.logger.debug(f"Successfully connected to {ssid} on device {self.host}")
|
|
290
|
+
else:
|
|
291
|
+
self.logger.warning(f"Unable to connect to {ssid} on device {self.host}")
|
|
292
|
+
|
|
293
|
+
def set_wifi_static_address(
|
|
294
|
+
self,
|
|
295
|
+
address: str | None = None,
|
|
296
|
+
net_mask: str | None = None,
|
|
297
|
+
gateway: str | None = None,
|
|
298
|
+
dns_server: str | None = None,
|
|
299
|
+
hostname: str | None = None,
|
|
300
|
+
) -> None:
|
|
301
|
+
self._require_arguments(
|
|
302
|
+
"set_wifi_static_address",
|
|
303
|
+
address=address,
|
|
304
|
+
net_mask=net_mask,
|
|
305
|
+
gateway=gateway,
|
|
306
|
+
dns_server=dns_server,
|
|
307
|
+
hostname=hostname,
|
|
308
|
+
)
|
|
309
|
+
if self.supported_features["wifi"] == "no":
|
|
310
|
+
raise FeatureNotSupported("Wi-Fi is not supported on this device.")
|
|
311
|
+
mac_address = str(self.get_wifi_settings()["mac"])
|
|
312
|
+
request_data = {
|
|
313
|
+
"networkMode": "manual",
|
|
314
|
+
"mac": mac_address,
|
|
315
|
+
"status": "Connected",
|
|
316
|
+
"ipAddress": address,
|
|
317
|
+
"netMask": net_mask,
|
|
318
|
+
"gateway": gateway,
|
|
319
|
+
"dnsServer": dns_server,
|
|
320
|
+
"hostname": hostname,
|
|
321
|
+
}
|
|
322
|
+
response = esp_api.send_request(self, "setNetworkWifi", request_data)
|
|
323
|
+
self.logger.debug(
|
|
324
|
+
f"Setting static address on device {self.host} to {address}, full info: {request_data}"
|
|
325
|
+
)
|
|
326
|
+
if response.status_code == 200:
|
|
327
|
+
self.logger.debug(f"Successfully set static address on device {self.host}")
|
|
328
|
+
else:
|
|
329
|
+
self.logger.debug(f"Unable to set static address on device {self.host}")
|
|
330
|
+
|
|
331
|
+
# endregion
|
|
332
|
+
|
|
333
|
+
def import_config(self, file, **kwargs: Any) -> None:
|
|
334
|
+
if "can_alter_settings" not in self.user_permissions:
|
|
335
|
+
raise PermissionError(
|
|
336
|
+
"You don't have permission to alter settings on this device."
|
|
337
|
+
)
|
|
338
|
+
if self._ka_thread:
|
|
339
|
+
self._ka_thread.cancel()
|
|
340
|
+
self._ka_thread.join()
|
|
341
|
+
data = json.load(file)
|
|
342
|
+
encoded_data = json.dumps(data).encode("utf-8")
|
|
343
|
+
esp_api.send_file(self, "/upload/config", encoded_data)
|
|
344
|
+
self.logger.info(
|
|
345
|
+
f"Imported configuration from {file.name}, device {self.host} is restarting..."
|
|
346
|
+
)
|
|
347
|
+
sleep(kwargs.get("sleep_time", 15))
|
|
348
|
+
username = kwargs.get("username", self.username)
|
|
349
|
+
password = kwargs.get("password", self.password)
|
|
350
|
+
|
|
351
|
+
if kwargs.get("login", True):
|
|
352
|
+
self.login(username, password)
|
|
353
|
+
if self._ka_thread:
|
|
354
|
+
self._keep_alive()
|
|
355
|
+
|
|
356
|
+
def export_config(self, save_file: str | None = None) -> dict[str, Any]:
|
|
357
|
+
response = esp_api.get_file(self, "/files/config.json")
|
|
358
|
+
self.logger.debug(
|
|
359
|
+
f"Exported configuration from device {self.host}, response: {response.json()}"
|
|
360
|
+
)
|
|
361
|
+
if save_file is not None:
|
|
362
|
+
with open(save_file, "w") as f:
|
|
363
|
+
f.write(response.text)
|
|
364
|
+
self.logger.debug(f"Saved configuration to {save_file}")
|
|
365
|
+
return response.json()
|
|
366
|
+
|
|
367
|
+
def update_firmware(self, file) -> NETIODevice:
|
|
368
|
+
if "can_alter_settings" not in self.user_permissions:
|
|
369
|
+
raise PermissionError(
|
|
370
|
+
"You don't have permission to alter settings on this device."
|
|
371
|
+
)
|
|
372
|
+
if self._ka_thread:
|
|
373
|
+
self._ka_thread.cancel()
|
|
374
|
+
self._ka_thread.join()
|
|
375
|
+
|
|
376
|
+
pre_reconnect_wait = 20
|
|
377
|
+
if self.supported_features["wifi"] == "yes":
|
|
378
|
+
wifi_settings = self.get_wifi_settings()
|
|
379
|
+
if (
|
|
380
|
+
wifi_settings["mode"] == "client"
|
|
381
|
+
and wifi_settings["client"]["status"] == "Connected"
|
|
382
|
+
):
|
|
383
|
+
pre_reconnect_wait = 50
|
|
384
|
+
|
|
385
|
+
if esp_api.check_connectivity(self) > (pre_reconnect_wait / 10.0):
|
|
386
|
+
pre_reconnect_wait = pre_reconnect_wait * 2
|
|
387
|
+
|
|
388
|
+
_ = esp_api.send_request(self, "prepFwUpgrade")
|
|
389
|
+
_ = esp_api.send_file(self, "/upload/firmware", file)
|
|
390
|
+
try:
|
|
391
|
+
_ = esp_api.send_request(self, "startUpgrade", close=True)
|
|
392
|
+
except CommunicationError:
|
|
393
|
+
self.logger.warn(
|
|
394
|
+
f"Device {self.host} couldn't verify firmware update process beginning, this should be harmless if the device connects, waiting for connection."
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
self.logger.debug(
|
|
398
|
+
f"Uploaded firmware {file.name}, device {self.host} might be unresponsive for a while."
|
|
399
|
+
)
|
|
400
|
+
sleep(pre_reconnect_wait)
|
|
401
|
+
|
|
402
|
+
device_response_time = esp_api.check_connectivity(self)
|
|
403
|
+
retry_limit = 3 if device_response_time == -1 else 0
|
|
404
|
+
for _ in range(0, retry_limit):
|
|
405
|
+
device_response_time = esp_api.check_connectivity(self)
|
|
406
|
+
|
|
407
|
+
if device_response_time == -1:
|
|
408
|
+
raise CommunicationError(
|
|
409
|
+
"Device couldn't establish connection after firmware update."
|
|
410
|
+
)
|
|
411
|
+
updated_instance = self.netio_manager.update_device(self)
|
|
412
|
+
|
|
413
|
+
return updated_instance
|
|
414
|
+
|
|
415
|
+
def get_system_info(self) -> dict[str, Any]:
|
|
416
|
+
action = "getSystemInfo"
|
|
417
|
+
self.logger.debug(f"Getting system info for device {self.host}")
|
|
418
|
+
response = esp_api.send_request(self, action)
|
|
419
|
+
return response.json()["data"]
|
|
420
|
+
|
|
421
|
+
def get_uptime(self) -> int:
|
|
422
|
+
system_info = self.get_system_info()
|
|
423
|
+
self.logger.debug(f"Uptime for device {self.host}")
|
|
424
|
+
return system_info["uptime"]
|
|
425
|
+
|
|
426
|
+
def reset_power_consumption_counters(self) -> None:
|
|
427
|
+
action = "resetOutputConsumption"
|
|
428
|
+
self.logger.debug(
|
|
429
|
+
f"Resetting power consumption counters for device {self.host}"
|
|
430
|
+
)
|
|
431
|
+
esp_api.send_request(self, action)
|
|
432
|
+
|
|
433
|
+
def reset_output_consumption_counter(self, output_id: int) -> None:
|
|
434
|
+
# TODO: Implement reset_output_consumption_counter for legacy ESP devices
|
|
435
|
+
raise NotImplementedError(
|
|
436
|
+
"reset_output_consumption_counter is not implemented for legacy ESP devices"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
def set_system_settings(
|
|
440
|
+
self,
|
|
441
|
+
device_name: str | None = None,
|
|
442
|
+
port: int | None = None,
|
|
443
|
+
periodic_restart: bool | None = None,
|
|
444
|
+
restart_period: int | None = None,
|
|
445
|
+
) -> None:
|
|
446
|
+
device_system_info = esp_api.send_request(self, "getSystemInfo")
|
|
447
|
+
info_json_data = device_system_info.json()["data"]
|
|
448
|
+
device_name = (
|
|
449
|
+
device_name if device_name is not None else info_json_data["deviceName"]
|
|
450
|
+
)
|
|
451
|
+
port = port if port is not None else info_json_data["port"]
|
|
452
|
+
pr_enable = (
|
|
453
|
+
periodic_restart
|
|
454
|
+
if periodic_restart is not None
|
|
455
|
+
else info_json_data["periodicRestart"]["enable"]
|
|
456
|
+
)
|
|
457
|
+
pr_period = (
|
|
458
|
+
restart_period
|
|
459
|
+
if restart_period is not None
|
|
460
|
+
else info_json_data["periodicRestart"]["period"]
|
|
461
|
+
)
|
|
462
|
+
request_data = {
|
|
463
|
+
"deviceName": device_name,
|
|
464
|
+
"port": port,
|
|
465
|
+
"periodicRestart": {"enable": pr_enable, "period": pr_period},
|
|
466
|
+
}
|
|
467
|
+
self.logger.debug(
|
|
468
|
+
f"Setting system settings on device {device_name}, host {self.host} to {request_data}"
|
|
469
|
+
)
|
|
470
|
+
esp_api.send_request(self, "setSystemConfig", request_data)
|
|
471
|
+
|
|
472
|
+
def rename_device(self, device_name: str) -> None:
|
|
473
|
+
response = esp_api.send_request(self, "getSystemInfo")
|
|
474
|
+
json_data = response.json()["data"]
|
|
475
|
+
port = json_data["port"]
|
|
476
|
+
pr_enable = json_data["periodicRestart"]["enable"]
|
|
477
|
+
pr_period = json_data["periodicRestart"]["period"]
|
|
478
|
+
request_data = {
|
|
479
|
+
"deviceName": device_name,
|
|
480
|
+
"port": port,
|
|
481
|
+
"periodicRestart": {"enable": pr_enable, "period": pr_period},
|
|
482
|
+
}
|
|
483
|
+
self.logger.debug(f"Renaming device to {device_name} on url {self.host}")
|
|
484
|
+
esp_api.send_request(self, "setSystemConfig", request_data)
|
|
485
|
+
|
|
486
|
+
def set_periodic_restart(
|
|
487
|
+
self, enable: bool | None = None, restart_period: int | None = None
|
|
488
|
+
) -> None:
|
|
489
|
+
self.set_system_settings(periodic_restart=enable, restart_period=restart_period)
|
|
490
|
+
|
|
491
|
+
def locate(self) -> None:
|
|
492
|
+
self.logger.debug(f"Blinking LED on device {self.host}")
|
|
493
|
+
esp_api.send_request(self, "locate")
|
|
494
|
+
|
|
495
|
+
# endregion
|
|
496
|
+
|
|
497
|
+
# region User Management
|
|
498
|
+
|
|
499
|
+
def get_current_user(self) -> dict[str, Any]:
|
|
500
|
+
response = esp_api.send_request(self, "getCurrentUser")
|
|
501
|
+
self.logger.debug(
|
|
502
|
+
f"Received current user information: {response.json()['data']} from device {self.host}"
|
|
503
|
+
)
|
|
504
|
+
return response.json()["data"]
|
|
505
|
+
|
|
506
|
+
def get_user_privileges(self, username: str) -> list[str]:
|
|
507
|
+
device_users = self.get_users()
|
|
508
|
+
self.logger.debug(
|
|
509
|
+
f"Checking for user privileges of user {username} on device {self.host}"
|
|
510
|
+
)
|
|
511
|
+
for user in device_users:
|
|
512
|
+
if user["username"] == username:
|
|
513
|
+
return user["permissions"]
|
|
514
|
+
self.logger.info(
|
|
515
|
+
f"Couldn't find the specified user {username} in the user list."
|
|
516
|
+
)
|
|
517
|
+
raise ElementNotFound
|
|
518
|
+
|
|
519
|
+
def get_users(self) -> dict[str, Any]:
|
|
520
|
+
action = "getUserList"
|
|
521
|
+
return esp_api.send_request(self, action).json()["data"]
|
|
522
|
+
|
|
523
|
+
def change_password(self, new_password: str) -> None:
|
|
524
|
+
self.change_user_password(new_password, self.username, self.password)
|
|
525
|
+
|
|
526
|
+
def change_user_password(
|
|
527
|
+
self,
|
|
528
|
+
username: str,
|
|
529
|
+
old_password: str,
|
|
530
|
+
new_password: str,
|
|
531
|
+
) -> None:
|
|
532
|
+
current_user = esp_api.send_request(self, "getCurrentUser").json()
|
|
533
|
+
if "can_alter_users" not in current_user["data"]["privileges"]:
|
|
534
|
+
raise PermissionError(
|
|
535
|
+
"You don't have permission to manage users on this device."
|
|
536
|
+
)
|
|
537
|
+
request_data = {
|
|
538
|
+
"username": username,
|
|
539
|
+
"password": {"old": old_password, "new": new_password},
|
|
540
|
+
}
|
|
541
|
+
self.logger.debug(
|
|
542
|
+
f"Changing password of user {username} to {new_password} on device {self.host}"
|
|
543
|
+
)
|
|
544
|
+
esp_api.send_request(self, "setUser", request_data)
|
|
545
|
+
if username == current_user["data"]["username"]:
|
|
546
|
+
self.password = new_password
|
|
547
|
+
|
|
548
|
+
def create_user(
|
|
549
|
+
self, username: str, password: str, privileges: list[str] | None = None
|
|
550
|
+
) -> None:
|
|
551
|
+
if "can_alter_users" not in self.user_permissions:
|
|
552
|
+
raise PermissionError(
|
|
553
|
+
"You don't have permission to manage users on this device."
|
|
554
|
+
)
|
|
555
|
+
possible_priviledges = (
|
|
556
|
+
"can_login",
|
|
557
|
+
"can_alter_users",
|
|
558
|
+
"can_alter_settings",
|
|
559
|
+
"can_use_tunnels",
|
|
560
|
+
"can_browse_logs",
|
|
561
|
+
"can_alter_outputs",
|
|
562
|
+
"can_control_outputs",
|
|
563
|
+
"can_view_settings",
|
|
564
|
+
"can_alter_rules",
|
|
565
|
+
)
|
|
566
|
+
if privileges is None:
|
|
567
|
+
privileges = ["can_login"]
|
|
568
|
+
for privilege in privileges:
|
|
569
|
+
if privilege not in possible_priviledges:
|
|
570
|
+
raise ValueError(
|
|
571
|
+
f"List of priviledges contains an invalid value: {privileges}"
|
|
572
|
+
)
|
|
573
|
+
action = "addUser"
|
|
574
|
+
request_data = {
|
|
575
|
+
"username": username,
|
|
576
|
+
"password": password,
|
|
577
|
+
"permissions": privileges,
|
|
578
|
+
}
|
|
579
|
+
self.logger.debug(f"Creating user {username} on device {self.host}")
|
|
580
|
+
esp_api.send_request(self, action, request_data)
|
|
581
|
+
|
|
582
|
+
def remove_user(self, username: str) -> None:
|
|
583
|
+
requst_data = {"username": username}
|
|
584
|
+
action = "deleteUser"
|
|
585
|
+
self.logger.debug(f"Removing user {username} on device {self.host}")
|
|
586
|
+
esp_api.send_request(self, action, requst_data)
|
|
587
|
+
|
|
588
|
+
# endregion
|
|
589
|
+
|
|
590
|
+
# region Protocols
|
|
591
|
+
|
|
592
|
+
def get_active_protocols(self) -> list[int]:
|
|
593
|
+
response = esp_api.send_request(self, "getActiveProtocols")
|
|
594
|
+
self.logger.debug(
|
|
595
|
+
f"Received active protocols: {response.json()['data']['protocols']} from device {self.host}"
|
|
596
|
+
)
|
|
597
|
+
return response.json()["data"]["protocols"]
|
|
598
|
+
|
|
599
|
+
def get_supported_protocols(self) -> list[int]:
|
|
600
|
+
response = esp_api.send_request(self, "getSupportedProtocols")
|
|
601
|
+
protocols = response.json()["data"]["protocols"]
|
|
602
|
+
self.logger.debug(
|
|
603
|
+
f"Received supported protocols: {protocols} from device {self.host}"
|
|
604
|
+
)
|
|
605
|
+
return protocols
|
|
606
|
+
|
|
607
|
+
# region NETIO Cloud
|
|
608
|
+
|
|
609
|
+
def set_cloud_state(self, state: bool) -> None:
|
|
610
|
+
string_state = "true" if state else "false"
|
|
611
|
+
|
|
612
|
+
response = esp_api.send_request(
|
|
613
|
+
self, "setProtocol", {"id": 111, "action": "set", "enable": string_state}
|
|
614
|
+
)
|
|
615
|
+
self.logger.debug(
|
|
616
|
+
f"Setting cloud state of device {self.host} to {state}, response: {response.json()}"
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
def get_cloud_state(self) -> dict[str, Any]:
|
|
620
|
+
response = esp_api.send_request(
|
|
621
|
+
self, "getProtocol", {"id": 111, "action": "get"}
|
|
622
|
+
)
|
|
623
|
+
self.logger.debug(
|
|
624
|
+
f"Received cloud state: {response.json()['data']} from device {self.host}"
|
|
625
|
+
)
|
|
626
|
+
return response.json()
|
|
627
|
+
|
|
628
|
+
def set_on_premise(self, url: str) -> None:
|
|
629
|
+
response = esp_api.send_request(
|
|
630
|
+
self, "setProtocol", {"id": 111, "action": "setOnPremis", "server": url}
|
|
631
|
+
)
|
|
632
|
+
self.logger.debug(
|
|
633
|
+
f"Setting on-premise server of device {self.host} to {url}, response: {response.json()}"
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
def register_to_cloud(self, token: str) -> None:
|
|
637
|
+
response = esp_api.send_request(
|
|
638
|
+
self, "setProtocol", {"id": 111, "action": "register", "token": token}
|
|
639
|
+
)
|
|
640
|
+
self.logger.debug(
|
|
641
|
+
f"Registering device {self.host} to cloud using token {token}, response: {response.json()}"
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
# endregion
|
|
645
|
+
|
|
646
|
+
# region URLAPI
|
|
647
|
+
|
|
648
|
+
def set_urlapi_state(
|
|
649
|
+
self,
|
|
650
|
+
protocol_enabled: bool | None = None,
|
|
651
|
+
write_enable: bool | None = None,
|
|
652
|
+
write_password: str | None = None,
|
|
653
|
+
) -> None:
|
|
654
|
+
self._require_arguments(
|
|
655
|
+
"set_urlapi_state",
|
|
656
|
+
protocol_enabled=protocol_enabled,
|
|
657
|
+
write_enable=write_enable,
|
|
658
|
+
write_password=write_password,
|
|
659
|
+
)
|
|
660
|
+
json_data = {
|
|
661
|
+
"enable": protocol_enabled,
|
|
662
|
+
"write": {"enable": write_enable, "password": write_password},
|
|
663
|
+
"id": 105,
|
|
664
|
+
}
|
|
665
|
+
response = esp_api.send_request(self, "setProtocol", data=json_data)
|
|
666
|
+
self.logger.debug(
|
|
667
|
+
f"Setting URL API state on device {self.host} to {protocol_enabled}, response: {response.json()}"
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
# endregion
|
|
671
|
+
|
|
672
|
+
# region Modbus
|
|
673
|
+
|
|
674
|
+
def get_modbus_state(self) -> dict[str, Any]:
|
|
675
|
+
response = esp_api.send_request(
|
|
676
|
+
self, "getProtocol", {"id": 107, "action": "getStatus"}
|
|
677
|
+
)
|
|
678
|
+
self.logger.debug(
|
|
679
|
+
f"Received Modbus M2M state: {response.json()['data']} from device {self.host}"
|
|
680
|
+
)
|
|
681
|
+
return response.json()["data"]
|
|
682
|
+
|
|
683
|
+
def set_modbus_state(
|
|
684
|
+
self,
|
|
685
|
+
protocol_enabled: bool | None = None,
|
|
686
|
+
port: int | None = None,
|
|
687
|
+
ip_filter_enabled: bool | None = None,
|
|
688
|
+
ip_from: str | None = None,
|
|
689
|
+
ip_to: str | None = None,
|
|
690
|
+
) -> None:
|
|
691
|
+
self._require_arguments("set_modbus_state", protocol_enabled=protocol_enabled)
|
|
692
|
+
port = 502 if port is None else port
|
|
693
|
+
ip_filter_enabled = False if ip_filter_enabled is None else ip_filter_enabled
|
|
694
|
+
ip_from = "" if ip_from is None else ip_from
|
|
695
|
+
ip_to = "" if ip_to is None else ip_to
|
|
696
|
+
modbus_data = self.get_modbus_state()
|
|
697
|
+
json_data = {"enable": protocol_enabled, "port": port, "id": 107}
|
|
698
|
+
if ip_filter_enabled:
|
|
699
|
+
if ip_from == "":
|
|
700
|
+
ip_from = modbus_data["ipFilter"]["ipFrom"]
|
|
701
|
+
if ip_to == "":
|
|
702
|
+
ip_to = modbus_data["ipFilter"]["ipTo"]
|
|
703
|
+
json_data["ipFilter"] = {
|
|
704
|
+
"enable": ip_filter_enabled,
|
|
705
|
+
"ipFrom": ip_from,
|
|
706
|
+
"ipTo": ip_to,
|
|
707
|
+
} # noqa
|
|
708
|
+
else:
|
|
709
|
+
json_data["ipFilter"] = {"enable": ip_filter_enabled}
|
|
710
|
+
response = esp_api.send_request(self, "setProtocol", data=json_data)
|
|
711
|
+
self.logger.debug(
|
|
712
|
+
f"Setting Modbus M2M state on device {self.host} to {protocol_enabled}, response: {response.json()}"
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
# endregion
|
|
716
|
+
|
|
717
|
+
# region JSON
|
|
718
|
+
|
|
719
|
+
def get_json_api_state(self) -> dict[str, Any]:
|
|
720
|
+
response = esp_api.send_request(
|
|
721
|
+
self, "getProtocol", {"id": 104, "action": "getStatus"}
|
|
722
|
+
)
|
|
723
|
+
self.logger.debug(
|
|
724
|
+
f"Received JSON API state: {response.json()['data']} from device {self.host}"
|
|
725
|
+
)
|
|
726
|
+
return response.json()["data"]
|
|
727
|
+
|
|
728
|
+
def set_json_api_state(
|
|
729
|
+
self,
|
|
730
|
+
protocol_enabled: bool | None = None,
|
|
731
|
+
read_enable: bool | None = None,
|
|
732
|
+
write_enable: bool | None = None,
|
|
733
|
+
read_auth: tuple[str, str] | None = None,
|
|
734
|
+
write_auth: tuple[str, str] | None = None,
|
|
735
|
+
) -> None:
|
|
736
|
+
self._require_arguments("set_json_api_state", protocol_enabled=protocol_enabled)
|
|
737
|
+
# TODO: Make the parameters optional, so only a specific setting can be changed.
|
|
738
|
+
current_config = self.get_json_api_state()
|
|
739
|
+
|
|
740
|
+
if read_enable is None:
|
|
741
|
+
read_enable = current_config["read"]["enable"]
|
|
742
|
+
if write_enable is None:
|
|
743
|
+
write_enable = current_config["write"]["enable"]
|
|
744
|
+
if read_auth is None:
|
|
745
|
+
read_auth = (
|
|
746
|
+
current_config["read"]["username"],
|
|
747
|
+
current_config["read"]["password"],
|
|
748
|
+
)
|
|
749
|
+
if write_auth is None:
|
|
750
|
+
write_auth = (
|
|
751
|
+
current_config["write"]["username"],
|
|
752
|
+
current_config["write"]["password"],
|
|
753
|
+
)
|
|
754
|
+
|
|
755
|
+
request_data = {
|
|
756
|
+
"enable": protocol_enabled,
|
|
757
|
+
"read": {
|
|
758
|
+
"enable": read_enable,
|
|
759
|
+
"username": read_auth[0],
|
|
760
|
+
"password": read_auth[1],
|
|
761
|
+
},
|
|
762
|
+
"write": {
|
|
763
|
+
"enable": write_enable,
|
|
764
|
+
"username": write_auth[0],
|
|
765
|
+
"password": write_auth[1],
|
|
766
|
+
},
|
|
767
|
+
"id": 104,
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
response = esp_api.send_request(self, "setProtocol", request_data)
|
|
771
|
+
self.logger.debug(
|
|
772
|
+
f"Setting JSON API state on device {self.host} to {protocol_enabled}, full info: {request_data}"
|
|
773
|
+
)
|
|
774
|
+
if response.status_code == 200:
|
|
775
|
+
ap = self.get_active_protocols()
|
|
776
|
+
if (protocol_enabled and 104 in ap) or (
|
|
777
|
+
not protocol_enabled and 104 not in ap
|
|
778
|
+
):
|
|
779
|
+
self.logger.debug(
|
|
780
|
+
f"Successfully set JSON API state on device {self.host}"
|
|
781
|
+
)
|
|
782
|
+
else:
|
|
783
|
+
self.logger.debug(f"Unable to set JSON API state on device {self.host}")
|
|
784
|
+
|
|
785
|
+
def get_json(self, json_auth: tuple[str, str]) -> dict[str, Any]:
|
|
786
|
+
if 104 not in self.get_active_protocols():
|
|
787
|
+
raise ProtocolNotEnabled("JSON API is not enabled on the device.")
|
|
788
|
+
try:
|
|
789
|
+
response = requests.get(
|
|
790
|
+
"http://" + self.host + "/netio.json", auth=json_auth, timeout=10
|
|
791
|
+
)
|
|
792
|
+
if response.status_code == 200:
|
|
793
|
+
self.logger.debug(
|
|
794
|
+
f"Received JSON information from device {self.host}: {response.json()}"
|
|
795
|
+
)
|
|
796
|
+
return response.json()
|
|
797
|
+
else:
|
|
798
|
+
raise requests.ConnectionError
|
|
799
|
+
except (requests.ConnectionError, requests.ReadTimeout) as e:
|
|
800
|
+
self.logger.critical(f"Unable to reach host {self.host}")
|
|
801
|
+
raise e
|
|
802
|
+
|
|
803
|
+
# endregion
|
|
804
|
+
|
|
805
|
+
def get_telnet_api_state(self) -> dict[str, Any]:
|
|
806
|
+
action = "getProtocol"
|
|
807
|
+
response = esp_api.send_request(
|
|
808
|
+
self, action, {"id": 106, "action": "getStatus"}
|
|
809
|
+
)
|
|
810
|
+
self.logger.debug(
|
|
811
|
+
f"Received telnet API state: {response.json()['data']} from device {self.host}"
|
|
812
|
+
)
|
|
813
|
+
return response.json()["data"]
|
|
814
|
+
|
|
815
|
+
def set_telnet_api_state(
|
|
816
|
+
self,
|
|
817
|
+
protocol_enabled: bool | None = None,
|
|
818
|
+
port: int | None = None,
|
|
819
|
+
read_enabled: bool | None = None,
|
|
820
|
+
read_auth: tuple[str, str] | None = None,
|
|
821
|
+
write_enabled: bool | None = None,
|
|
822
|
+
write_auth: tuple[str, str] | None = None,
|
|
823
|
+
) -> None:
|
|
824
|
+
self._require_arguments(
|
|
825
|
+
"set_telnet_api_state", protocol_enabled=protocol_enabled
|
|
826
|
+
)
|
|
827
|
+
action = "setProtocol"
|
|
828
|
+
current_state = self.get_telnet_api_state()
|
|
829
|
+
request_data = {
|
|
830
|
+
"enable": protocol_enabled,
|
|
831
|
+
"port": port if port else current_state["port"],
|
|
832
|
+
"read": {
|
|
833
|
+
"enable": read_enabled
|
|
834
|
+
if read_enabled
|
|
835
|
+
else current_state["read"]["enable"],
|
|
836
|
+
"username": read_auth[0]
|
|
837
|
+
if read_auth
|
|
838
|
+
else current_state["read"]["username"],
|
|
839
|
+
"password": read_auth[1]
|
|
840
|
+
if read_auth
|
|
841
|
+
else current_state["read"]["password"],
|
|
842
|
+
},
|
|
843
|
+
"write": {
|
|
844
|
+
"enable": write_enabled
|
|
845
|
+
if write_enabled
|
|
846
|
+
else current_state["write"]["enable"],
|
|
847
|
+
"username": write_auth[0]
|
|
848
|
+
if write_auth
|
|
849
|
+
else current_state["write"]["username"],
|
|
850
|
+
"password": write_auth[1]
|
|
851
|
+
if write_auth
|
|
852
|
+
else current_state["write"]["password"],
|
|
853
|
+
},
|
|
854
|
+
"id": 106,
|
|
855
|
+
}
|
|
856
|
+
self.logger.debug(
|
|
857
|
+
f"Setting telnet API state on device {self.host} to new settings: {request_data}"
|
|
858
|
+
)
|
|
859
|
+
response = esp_api.send_request(self, action, request_data)
|
|
860
|
+
if response.status_code == 200:
|
|
861
|
+
ap = self.get_active_protocols()
|
|
862
|
+
if (protocol_enabled and 106 in ap) or (
|
|
863
|
+
not protocol_enabled and 106 not in ap
|
|
864
|
+
):
|
|
865
|
+
self.logger.debug(
|
|
866
|
+
f"Successfully set new telnet state on device {self.host}"
|
|
867
|
+
)
|
|
868
|
+
else:
|
|
869
|
+
self.logger.debug(
|
|
870
|
+
f"Unable to set telnet state on device {self.host}, check debug log."
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
def get_netio_push_api_state(self) -> dict[str, Any]:
|
|
874
|
+
action = "getProtocol"
|
|
875
|
+
response = esp_api.send_request(
|
|
876
|
+
self, action, {"id": 109, "action": "getStatus"}
|
|
877
|
+
)
|
|
878
|
+
self.logger.debug(
|
|
879
|
+
f"Received Netio Push API state: {response.json()['data']} from device {self.host}"
|
|
880
|
+
)
|
|
881
|
+
return response.json()["data"]
|
|
882
|
+
|
|
883
|
+
def set_netio_push_api_state(
|
|
884
|
+
self,
|
|
885
|
+
protocol_enabled: bool | None = None,
|
|
886
|
+
url: str | None = None,
|
|
887
|
+
push_protocol: str | None = None,
|
|
888
|
+
delta: int | None = None,
|
|
889
|
+
period: int | None = None,
|
|
890
|
+
) -> None:
|
|
891
|
+
self._require_arguments(
|
|
892
|
+
"set_netio_push_api_state", protocol_enabled=protocol_enabled
|
|
893
|
+
)
|
|
894
|
+
action = "setProtocol"
|
|
895
|
+
current_state = self.get_netio_push_api_state()
|
|
896
|
+
request_data = {
|
|
897
|
+
"enable": protocol_enabled,
|
|
898
|
+
"url": url if url else current_state["url"],
|
|
899
|
+
"pushProtocol": push_protocol
|
|
900
|
+
if push_protocol
|
|
901
|
+
else current_state["pushProtocol"],
|
|
902
|
+
"delta": delta if delta else current_state["delta"],
|
|
903
|
+
"period": period if period else current_state["period"],
|
|
904
|
+
"value": "current",
|
|
905
|
+
"id": 109,
|
|
906
|
+
}
|
|
907
|
+
self.logger.debug(
|
|
908
|
+
f"Setting Netio Push API state on device {self.host} to settings: {request_data}"
|
|
909
|
+
)
|
|
910
|
+
response = esp_api.send_request(self, action, request_data)
|
|
911
|
+
if response.status_code == 200:
|
|
912
|
+
ap = self.get_active_protocols()
|
|
913
|
+
if (protocol_enabled and 109 in ap) or (
|
|
914
|
+
not protocol_enabled and 109 not in ap
|
|
915
|
+
):
|
|
916
|
+
self.logger.debug(
|
|
917
|
+
f"Successfully set new Netio Push API state on device {self.host}"
|
|
918
|
+
)
|
|
919
|
+
else:
|
|
920
|
+
self.logger.debug(
|
|
921
|
+
f"Unable to set the Netio Push API state on device {self.host}, check debug log."
|
|
922
|
+
)
|
|
923
|
+
|
|
924
|
+
def netio_push_api_push_now(self) -> None:
|
|
925
|
+
action = "pushNow"
|
|
926
|
+
ap = self.get_active_protocols()
|
|
927
|
+
if 109 not in ap:
|
|
928
|
+
self.logger.info(
|
|
929
|
+
f"Tried to push on device with Push protocol disabled: {self.host}"
|
|
930
|
+
)
|
|
931
|
+
raise ProtocolNotEnabled(
|
|
932
|
+
f"Netio Push API is not enabled on the device. Current protocols: {ap}"
|
|
933
|
+
)
|
|
934
|
+
self.logger.debug(f"Pushing Netio Push API now on device {self.host}")
|
|
935
|
+
esp_api.send_request(self, action)
|
|
936
|
+
|
|
937
|
+
def get_snmp_api_state(self) -> dict[str, Any]:
|
|
938
|
+
action = "getProtocol"
|
|
939
|
+
response = esp_api.send_request(
|
|
940
|
+
self, action, {"id": 110, "action": "getStatus"}
|
|
941
|
+
)
|
|
942
|
+
self.logger.debug(
|
|
943
|
+
f"Received SNMP API state: {response.json()['data']} from device {self.host}"
|
|
944
|
+
)
|
|
945
|
+
return response.json()["data"]
|
|
946
|
+
|
|
947
|
+
def _set_snmp_api_state(
|
|
948
|
+
self,
|
|
949
|
+
protocol_enabled: bool,
|
|
950
|
+
version: str,
|
|
951
|
+
location: str | None = None,
|
|
952
|
+
community_read: str | None = None,
|
|
953
|
+
community_write: str | None = None,
|
|
954
|
+
security_name: str | None = None,
|
|
955
|
+
security_level: str | None = None,
|
|
956
|
+
auth_protocol: str | None = None,
|
|
957
|
+
auth_key: str | None = None,
|
|
958
|
+
priv_protocol: str | None = None,
|
|
959
|
+
priv_key: str | None = None,
|
|
960
|
+
) -> None:
|
|
961
|
+
action = "setProtocol"
|
|
962
|
+
current_state = self.get_snmp_api_state()
|
|
963
|
+
if version == "v1,2c":
|
|
964
|
+
request_data = {
|
|
965
|
+
"enable": protocol_enabled,
|
|
966
|
+
"id": 110,
|
|
967
|
+
"version": "v1-2",
|
|
968
|
+
"location": location if location else current_state["location"],
|
|
969
|
+
"communityRead": community_read
|
|
970
|
+
if community_read
|
|
971
|
+
else current_state["communityRead"],
|
|
972
|
+
"communityWrite": community_write
|
|
973
|
+
if community_write
|
|
974
|
+
else current_state["communityWrite"],
|
|
975
|
+
}
|
|
976
|
+
_ = esp_api.send_request(self, action, request_data)
|
|
977
|
+
elif version == "v3":
|
|
978
|
+
request_data = {
|
|
979
|
+
"enable": protocol_enabled,
|
|
980
|
+
"id": 110,
|
|
981
|
+
"version": "v3",
|
|
982
|
+
"location": location if location else current_state["location"],
|
|
983
|
+
"communityRead": community_read
|
|
984
|
+
if community_read
|
|
985
|
+
else current_state["communityRead"],
|
|
986
|
+
"communityWrite": community_write
|
|
987
|
+
if community_write
|
|
988
|
+
else current_state["communityWrite"],
|
|
989
|
+
}
|
|
990
|
+
security_level = (
|
|
991
|
+
security_level
|
|
992
|
+
if security_level
|
|
993
|
+
else current_state["snmpV3Users"][0]["securityLevel"]
|
|
994
|
+
)
|
|
995
|
+
user_object = {
|
|
996
|
+
"username": security_name
|
|
997
|
+
if security_name
|
|
998
|
+
else current_state["snmpV3Users"][0]["username"],
|
|
999
|
+
"accessLevel": "rw",
|
|
1000
|
+
"securityLevel": security_level,
|
|
1001
|
+
}
|
|
1002
|
+
if security_level == "authNoPriv" or security_level == "authPriv":
|
|
1003
|
+
print(current_state["snmpV3Users"])
|
|
1004
|
+
user_object["authAlgo"] = (
|
|
1005
|
+
auth_protocol
|
|
1006
|
+
if auth_protocol
|
|
1007
|
+
else current_state["snmpV3Users"][0]["authAlgo"]
|
|
1008
|
+
)
|
|
1009
|
+
user_object["authKey"] = (
|
|
1010
|
+
auth_key if auth_key else current_state["snmpV3Users"][0]["authKey"]
|
|
1011
|
+
)
|
|
1012
|
+
if security_level == "authPriv":
|
|
1013
|
+
user_object["encryptAlgo"] = (
|
|
1014
|
+
priv_protocol
|
|
1015
|
+
if priv_protocol
|
|
1016
|
+
else current_state["snmpV3Users"][0]["encryptAlgo"]
|
|
1017
|
+
)
|
|
1018
|
+
user_object["encryptKey"] = (
|
|
1019
|
+
priv_key
|
|
1020
|
+
if priv_key
|
|
1021
|
+
else current_state["snmpV3Users"][0]["encryptKey"]
|
|
1022
|
+
)
|
|
1023
|
+
request_data["snmpV3Users"] = [user_object]
|
|
1024
|
+
_ = esp_api.send_request(self, action, request_data)
|
|
1025
|
+
else:
|
|
1026
|
+
raise InvalidParameterValueError
|
|
1027
|
+
|
|
1028
|
+
def set_snmp_v1_2_api_state(
|
|
1029
|
+
self,
|
|
1030
|
+
protocol_enabled: bool | None = None,
|
|
1031
|
+
location: str | None = None,
|
|
1032
|
+
community_read: str | None = None,
|
|
1033
|
+
community_write: str | None = None,
|
|
1034
|
+
) -> None:
|
|
1035
|
+
self._require_arguments(
|
|
1036
|
+
"set_snmp_v1_2_api_state", protocol_enabled=protocol_enabled
|
|
1037
|
+
)
|
|
1038
|
+
self.logger.debug(f"Setting SNMP v1,2c API state on device {self.host}")
|
|
1039
|
+
if not protocol_enabled:
|
|
1040
|
+
self.logger.debug(
|
|
1041
|
+
"The protocol is being disabled, the device will restart."
|
|
1042
|
+
)
|
|
1043
|
+
self._set_snmp_api_state(
|
|
1044
|
+
protocol_enabled, "v1,2c", location, community_read, community_write
|
|
1045
|
+
)
|
|
1046
|
+
|
|
1047
|
+
def set_snmp_v3_api_state(
|
|
1048
|
+
self,
|
|
1049
|
+
protocol_enabled: bool | None = None,
|
|
1050
|
+
location: str | None = None,
|
|
1051
|
+
security_name: str | None = None,
|
|
1052
|
+
security_level: str | None = None,
|
|
1053
|
+
auth_protocol: str | None = None,
|
|
1054
|
+
auth_key: str | None = None,
|
|
1055
|
+
priv_protocol: str | None = None,
|
|
1056
|
+
priv_key: str | None = None,
|
|
1057
|
+
) -> None:
|
|
1058
|
+
self._require_arguments(
|
|
1059
|
+
"set_snmp_v3_api_state", protocol_enabled=protocol_enabled
|
|
1060
|
+
)
|
|
1061
|
+
self.logger.debug(f"Setting SNMP v3 API state on device {self.host}")
|
|
1062
|
+
if not protocol_enabled:
|
|
1063
|
+
self.logger.debug(
|
|
1064
|
+
"The protocol is being disabled, the device will restart."
|
|
1065
|
+
)
|
|
1066
|
+
self._set_snmp_api_state(
|
|
1067
|
+
protocol_enabled,
|
|
1068
|
+
"v3",
|
|
1069
|
+
location,
|
|
1070
|
+
None,
|
|
1071
|
+
None,
|
|
1072
|
+
security_name,
|
|
1073
|
+
security_level,
|
|
1074
|
+
auth_protocol,
|
|
1075
|
+
auth_key,
|
|
1076
|
+
priv_protocol,
|
|
1077
|
+
priv_key,
|
|
1078
|
+
)
|
|
1079
|
+
|
|
1080
|
+
# region XML
|
|
1081
|
+
|
|
1082
|
+
def set_xml_api_state(
|
|
1083
|
+
self,
|
|
1084
|
+
protocol_enabled: bool | None = None,
|
|
1085
|
+
read_enable: bool | None = None,
|
|
1086
|
+
write_enable: bool | None = None,
|
|
1087
|
+
read_auth: tuple[str, str] | None = None,
|
|
1088
|
+
write_auth: tuple[str, str] | None = None,
|
|
1089
|
+
) -> None:
|
|
1090
|
+
self._require_arguments(
|
|
1091
|
+
"set_xml_api_state",
|
|
1092
|
+
protocol_enabled=protocol_enabled,
|
|
1093
|
+
read_enable=read_enable,
|
|
1094
|
+
write_enable=write_enable,
|
|
1095
|
+
read_auth=read_auth,
|
|
1096
|
+
write_auth=write_auth,
|
|
1097
|
+
)
|
|
1098
|
+
# TODO: Make the parameters optional, so only a specific setting can be changed.
|
|
1099
|
+
request_data = {
|
|
1100
|
+
"enable": protocol_enabled,
|
|
1101
|
+
"read": {
|
|
1102
|
+
"enable": read_enable,
|
|
1103
|
+
"username": read_auth[0],
|
|
1104
|
+
"password": read_auth[1],
|
|
1105
|
+
},
|
|
1106
|
+
"write": {
|
|
1107
|
+
"enable": write_enable,
|
|
1108
|
+
"username": write_auth[0],
|
|
1109
|
+
"password": write_auth[1],
|
|
1110
|
+
},
|
|
1111
|
+
"id": 103,
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
response = esp_api.send_request(self, "setProtocol", request_data)
|
|
1115
|
+
self.logger.debug(
|
|
1116
|
+
f"Setting XML API state on device {self.host} to {protocol_enabled}, full info: {request_data}"
|
|
1117
|
+
)
|
|
1118
|
+
if response.status_code == 200:
|
|
1119
|
+
ap = self.get_active_protocols()
|
|
1120
|
+
if 103 in ap:
|
|
1121
|
+
self.logger.debug(
|
|
1122
|
+
f"Successfully set XML API state on device {self.host}"
|
|
1123
|
+
)
|
|
1124
|
+
else:
|
|
1125
|
+
self.logger.debug(f"Unable to set XML API state on device {self.host}")
|
|
1126
|
+
|
|
1127
|
+
def get_xml(self, xml_auth: tuple[str, str]) -> ET.Element:
|
|
1128
|
+
if 103 not in self.get_active_protocols():
|
|
1129
|
+
raise ProtocolNotEnabled("XML API is not enabled on the device.")
|
|
1130
|
+
try:
|
|
1131
|
+
response = requests.get(
|
|
1132
|
+
"http://" + self.host + "/netio.xml", auth=xml_auth, timeout=10
|
|
1133
|
+
)
|
|
1134
|
+
if response.status_code == 200:
|
|
1135
|
+
self.logger.debug(
|
|
1136
|
+
f"Received XML information from device {self.host}: {response.content}"
|
|
1137
|
+
)
|
|
1138
|
+
return ET.fromstring(response.content)
|
|
1139
|
+
else:
|
|
1140
|
+
self.logger.critical(f"Unable to reach host {self.host}")
|
|
1141
|
+
raise CommunicationError(f"Return code: {response.status_code}")
|
|
1142
|
+
except requests.ConnectionError as e:
|
|
1143
|
+
raise e
|
|
1144
|
+
|
|
1145
|
+
# endregion
|
|
1146
|
+
|
|
1147
|
+
# endregion
|
|
1148
|
+
|
|
1149
|
+
def get_rules(self) -> list[dict[str, Any]]:
|
|
1150
|
+
action = "getRules"
|
|
1151
|
+
self.logger.debug(f"Getting a rule list from device {self.host}")
|
|
1152
|
+
response = esp_api.send_request(self, action, {})
|
|
1153
|
+
rule_list = response.json()["data"]
|
|
1154
|
+
return rule_list
|
|
1155
|
+
|
|
1156
|
+
def get_enabled_rules(self) -> list[dict[str, Any]]:
|
|
1157
|
+
rule_list = self.get_rules()
|
|
1158
|
+
enabled_rules = []
|
|
1159
|
+
self.logger.debug(f"Filtering for enabled rules from device {self.host}")
|
|
1160
|
+
for rule in rule_list:
|
|
1161
|
+
if rule["enabled"]:
|
|
1162
|
+
enabled_rules.append(rule)
|
|
1163
|
+
return enabled_rules
|
|
1164
|
+
|
|
1165
|
+
def get_disabled_rules(self) -> list[dict[str, Any]]:
|
|
1166
|
+
rule_list = self.get_rules()
|
|
1167
|
+
disabled_rules = []
|
|
1168
|
+
self.logger.debug(f"Filtering for disabled rules from device {self.host}")
|
|
1169
|
+
for rule in rule_list:
|
|
1170
|
+
if not rule["enabled"]:
|
|
1171
|
+
disabled_rules.append(rule)
|
|
1172
|
+
return disabled_rules
|
|
1173
|
+
|
|
1174
|
+
def get_rule_by_name(self, rule_name: str) -> dict[str, Any]:
|
|
1175
|
+
rule_list = self.get_rules()
|
|
1176
|
+
self.logger.debug(f"Filtering for rule {rule_name} from device {self.host}")
|
|
1177
|
+
for rule in rule_list:
|
|
1178
|
+
if rule["name"] == rule_name:
|
|
1179
|
+
return rule
|
|
1180
|
+
self.logger.info(f"Unable to find rule {rule_name} on device {self.host}")
|
|
1181
|
+
raise ElementNotFound
|
|
1182
|
+
|
|
1183
|
+
def get_watchdogs(self) -> list[dict[str, Any]]:
|
|
1184
|
+
action = "getWatchdogs"
|
|
1185
|
+
self.logger.debug(f"Getting a watchdog list from device {self.host}")
|
|
1186
|
+
response = esp_api.send_request(self, action, {})
|
|
1187
|
+
watchdog_list = response.json()["data"]
|
|
1188
|
+
return watchdog_list
|
|
1189
|
+
|
|
1190
|
+
def get_enabled_watchdogs(self) -> list[dict[str, Any]]:
|
|
1191
|
+
watchdogs = self.get_watchdogs()
|
|
1192
|
+
enabled_watchdogs = []
|
|
1193
|
+
self.logger.debug(f"Filtering for enabled watchdogs from device {self.host}")
|
|
1194
|
+
for watchdog in watchdogs:
|
|
1195
|
+
if watchdog["enabled"]:
|
|
1196
|
+
enabled_watchdogs.append(watchdog)
|
|
1197
|
+
return enabled_watchdogs
|
|
1198
|
+
|
|
1199
|
+
def get_disabled_watchdogs(self) -> list[dict[str, Any]]:
|
|
1200
|
+
watchdogs = self.get_watchdogs()
|
|
1201
|
+
disabled_watchdogs = []
|
|
1202
|
+
self.logger.debug(f"Filtering for disabled watchdogs from device {self.host}")
|
|
1203
|
+
for watchdog in watchdogs:
|
|
1204
|
+
if not watchdog["enabled"]:
|
|
1205
|
+
disabled_watchdogs.append(watchdog)
|
|
1206
|
+
return disabled_watchdogs
|
|
1207
|
+
|
|
1208
|
+
def get_watchdog_by_name(self, watchdog_name: str) -> dict[str, Any]:
|
|
1209
|
+
watchdogs = self.get_watchdogs()
|
|
1210
|
+
self.logger.debug(
|
|
1211
|
+
f"Filtering for watchdog {watchdog_name} on device {self.host}"
|
|
1212
|
+
)
|
|
1213
|
+
for watchdog in watchdogs:
|
|
1214
|
+
if watchdog["name"] == watchdog_name:
|
|
1215
|
+
return watchdog
|
|
1216
|
+
self.logger.info(
|
|
1217
|
+
f"Unable to find watchdog {watchdog_name} on device {self.host}"
|
|
1218
|
+
)
|
|
1219
|
+
raise ElementNotFound
|
|
1220
|
+
|
|
1221
|
+
def get_schedules(self) -> list[dict[str, Any]]:
|
|
1222
|
+
action = "getScheduleList"
|
|
1223
|
+
self.logger.debug(f"Getting a schedule list from device {self.host}")
|
|
1224
|
+
response = esp_api.send_request(self, action, {})
|
|
1225
|
+
schedule_list = response.json()["data"]
|
|
1226
|
+
return schedule_list
|
|
1227
|
+
|
|
1228
|
+
def get_schedule_by_name(self, schedule_name: str) -> dict[str, Any]:
|
|
1229
|
+
scheduels = self.get_schedules()
|
|
1230
|
+
self.logger.debug(
|
|
1231
|
+
f"Filtering for schedule {schedule_name} on device {self.host}"
|
|
1232
|
+
)
|
|
1233
|
+
for schedule in scheduels:
|
|
1234
|
+
if schedule["name"] == schedule_name:
|
|
1235
|
+
return schedule
|
|
1236
|
+
self.logger.info(
|
|
1237
|
+
f"Unable to find schedule {schedule_name} on device {self.host}"
|
|
1238
|
+
)
|
|
1239
|
+
raise ElementNotFound
|
|
1240
|
+
|
|
1241
|
+
def get_schedule_id(self, schedule_name: str) -> int:
|
|
1242
|
+
schedule = self.get_schedule_by_name(schedule_name)
|
|
1243
|
+
return schedule["id"]
|
|
1244
|
+
|
|
1245
|
+
def get_schedule_names(self) -> list[str]:
|
|
1246
|
+
schedules = self.get_schedules()
|
|
1247
|
+
schedule_names = []
|
|
1248
|
+
self.logger.debug(f"Filtering for schedule names on device {self.host}")
|
|
1249
|
+
for schedule in schedules:
|
|
1250
|
+
schedule_names.append(schedule["name"])
|
|
1251
|
+
return schedule_names
|
|
1252
|
+
|
|
1253
|
+
def get_active_schedules(self) -> list[dict[str, Any]]:
|
|
1254
|
+
schedules = self.get_schedules()
|
|
1255
|
+
active_schedules = []
|
|
1256
|
+
self.logger.debug(f"Filtering for active schedules on device {self.host}")
|
|
1257
|
+
for schedule in schedules:
|
|
1258
|
+
if schedule["active"]:
|
|
1259
|
+
active_schedules.append(schedule)
|
|
1260
|
+
return active_schedules
|
|
1261
|
+
|
|
1262
|
+
def create_schedule(self, name: str, intervals: list[dict[str, Any]]) -> None:
|
|
1263
|
+
raise NotImplementedError(
|
|
1264
|
+
"create_schedule is only available for devices 5.x.x and up, please update your device."
|
|
1265
|
+
)
|
|
1266
|
+
|
|
1267
|
+
def delete_schedule(self, schedule_id: int) -> None:
|
|
1268
|
+
action = "deleteSchedule"
|
|
1269
|
+
self.logger.debug(f"Deleting schedule {schedule_id} from device {self.host}")
|
|
1270
|
+
try:
|
|
1271
|
+
esp_api.send_request(self, action, {"id": schedule_id})
|
|
1272
|
+
except CommunicationError as e:
|
|
1273
|
+
if "Invalid parameter" in str(e):
|
|
1274
|
+
raise ElementNotFound
|
|
1275
|
+
|
|
1276
|
+
def delete_schedule_by_name(self, schedule_name: str) -> None:
|
|
1277
|
+
schedule_id = self.get_schedule_id(schedule_name)
|
|
1278
|
+
self.delete_schedule(schedule_id)
|
|
1279
|
+
|
|
1280
|
+
def get_system_log(self) -> list[dict[str, Any]]:
|
|
1281
|
+
action = "loadSystemLog"
|
|
1282
|
+
self.logger.debug(f"Getting system log from device {self.host}")
|
|
1283
|
+
response = esp_api.send_request(self, action, {})
|
|
1284
|
+
return response.json()["data"]
|
|
1285
|
+
|
|
1286
|
+
def clear_system_log(self) -> None:
|
|
1287
|
+
action = "clearUserLog"
|
|
1288
|
+
self.logger.debug(f"Clearing system log from device {self.host}")
|
|
1289
|
+
_ = esp_api.send_request(self, action, {})
|
|
1290
|
+
|
|
1291
|
+
def get_pabs(self) -> list[dict[str, Any]]:
|
|
1292
|
+
action = "getPABList"
|
|
1293
|
+
self.logger.debug(f"Getting PABs from device {self.host}")
|
|
1294
|
+
response = esp_api.send_request(self, action, {})
|
|
1295
|
+
pab_list = response.json()["data"]
|
|
1296
|
+
return pab_list
|
|
1297
|
+
|
|
1298
|
+
def get_enabled_pabs(self) -> list[dict[str, Any]]:
|
|
1299
|
+
pab_list = self.get_pabs()
|
|
1300
|
+
enabled_pabs = []
|
|
1301
|
+
self.logger.debug(f"Filtering for enabled PABs from device {self.host}")
|
|
1302
|
+
for pab in pab_list:
|
|
1303
|
+
if pab["enabled"]:
|
|
1304
|
+
enabled_pabs.append(pab)
|
|
1305
|
+
return enabled_pabs
|
|
1306
|
+
|
|
1307
|
+
def get_disabled_pabs(self) -> list[dict[str, Any]]:
|
|
1308
|
+
pab_list = self.get_pabs()
|
|
1309
|
+
disabled_pabs = []
|
|
1310
|
+
self.logger.debug(f"Filtering for disabled PABs from device {self.host}")
|
|
1311
|
+
for pab in pab_list:
|
|
1312
|
+
if not pab["enabled"]:
|
|
1313
|
+
disabled_pabs.append(pab)
|
|
1314
|
+
return disabled_pabs
|
|
1315
|
+
|
|
1316
|
+
def get_pab_by_name(self, pab_name: str) -> dict[str, Any]:
|
|
1317
|
+
pabs = self.get_pabs()
|
|
1318
|
+
self.logger.debug(f"Filtering for PAB {pab_name} on device {self.host}")
|
|
1319
|
+
for pab in pabs:
|
|
1320
|
+
if pab["name"] == pab_name:
|
|
1321
|
+
return pab
|
|
1322
|
+
raise ElementNotFound
|
|
1323
|
+
|
|
1324
|
+
def delete_pab_by_name(self, pab_name: str) -> None:
|
|
1325
|
+
action = "deletePAB"
|
|
1326
|
+
request_data = {"name": pab_name}
|
|
1327
|
+
self.logger.debug(f"Deleting PAB {pab_name} from device {self.host}")
|
|
1328
|
+
esp_api.send_request(self, action, request_data)
|
|
1329
|
+
|
|
1330
|
+
# region Private Functions
|
|
1331
|
+
def _check_socket_index(self, socket_index: int) -> None:
|
|
1332
|
+
if socket_index < 1 or socket_index > self.output_count:
|
|
1333
|
+
raise InvalidSocketIndex(
|
|
1334
|
+
f"You tried to access socket with id {socket_index},"
|
|
1335
|
+
f" the device supports <1;{self.output_count}>."
|
|
1336
|
+
)
|
|
1337
|
+
|
|
1338
|
+
def _require_arguments(self, method_name: str, **arguments: Any) -> None:
|
|
1339
|
+
# Pre-5.0.0 can't keep these values from the device, so they stay required.
|
|
1340
|
+
missing = [name for name, value in arguments.items() if value is None]
|
|
1341
|
+
if missing:
|
|
1342
|
+
raise InvalidParameterValueError(
|
|
1343
|
+
f"{method_name} requires {', '.join(missing)} "
|
|
1344
|
+
"on firmware older than 5.0.0"
|
|
1345
|
+
)
|
|
1346
|
+
|
|
1347
|
+
def _cleanup(self) -> None:
|
|
1348
|
+
try:
|
|
1349
|
+
if self._ka_thread:
|
|
1350
|
+
self._ka_thread.cancel()
|
|
1351
|
+
self._ka_thread.join()
|
|
1352
|
+
self.logger.debug("Disabled keep-alive thread.")
|
|
1353
|
+
except AttributeError:
|
|
1354
|
+
pass # No need to clean up a non existant thread
|
|
1355
|
+
if self.session_id != "" or self.session_id != "TODO AUTH":
|
|
1356
|
+
self.logout()
|
|
1357
|
+
|
|
1358
|
+
# endregion
|
|
1359
|
+
|
|
1360
|
+
def get_mqttflex_state(self) -> dict[str, Any]:
|
|
1361
|
+
raise FeatureNotSupported(
|
|
1362
|
+
"MQTT with certificates is only supported on firmware 5.0.0 and newer."
|
|
1363
|
+
)
|
|
1364
|
+
|
|
1365
|
+
def set_mqttflex_state(
|
|
1366
|
+
self, state: bool | None = None, config: dict | None = None
|
|
1367
|
+
) -> None:
|
|
1368
|
+
raise FeatureNotSupported(
|
|
1369
|
+
"MQTT with certificates is only supported on firmware 5.0.0 and newer."
|
|
1370
|
+
)
|
|
1371
|
+
|
|
1372
|
+
def upload_mqtt_ca_certificate(self, ca: str) -> None:
|
|
1373
|
+
raise FeatureNotSupported(
|
|
1374
|
+
"MQTT with certificates is only supported on firmware 5.0.0 and newer."
|
|
1375
|
+
)
|
|
1376
|
+
|
|
1377
|
+
def upload_mqtt_client_certificate(self, cert: str) -> None:
|
|
1378
|
+
raise FeatureNotSupported(
|
|
1379
|
+
"MQTT with certificates is only supported on firmware 5.0.0 and newer."
|
|
1380
|
+
)
|
|
1381
|
+
|
|
1382
|
+
def upload_mqtt_client_key(self, key: str) -> None:
|
|
1383
|
+
raise FeatureNotSupported(
|
|
1384
|
+
"MQTT with certificates is only supported on firmware 5.0.0 and newer."
|
|
1385
|
+
)
|
|
1386
|
+
|
|
1387
|
+
def upload_https_certificate(self, certfile: str) -> None:
|
|
1388
|
+
raise FeatureNotSupported(
|
|
1389
|
+
"HTTPS with certificates is only supported on firmware 5.0.0 and newer."
|
|
1390
|
+
)
|
|
1391
|
+
|
|
1392
|
+
def upload_https_private_key(self, keyfile: str) -> None:
|
|
1393
|
+
raise FeatureNotSupported(
|
|
1394
|
+
"HTTPS with certificates is only supported on firmware 5.0.0 and newer."
|
|
1395
|
+
)
|
|
1396
|
+
|
|
1397
|
+
@override
|
|
1398
|
+
def get_version_revision(self) -> str:
|
|
1399
|
+
response = esp_api.send_request(self, "getVersion").json()["data"]["revision"]
|
|
1400
|
+
self.logger.debug(f"Received device version information: {response}")
|
|
1401
|
+
return response
|
|
1402
|
+
|
|
1403
|
+
@override
|
|
1404
|
+
def get_input_list(self) -> dict[str, Any]:
|
|
1405
|
+
raise NotImplementedError(
|
|
1406
|
+
"Input commands are only available for devices 5.x.x and up, please update your device."
|
|
1407
|
+
)
|
|
1408
|
+
|
|
1409
|
+
@override
|
|
1410
|
+
def get_input_data(self, input_id: int) -> dict[str, Any]:
|
|
1411
|
+
raise NotImplementedError(
|
|
1412
|
+
"Input commands are only available for devices 5.x.x and up, please update your device."
|
|
1413
|
+
)
|
|
1414
|
+
|
|
1415
|
+
@override
|
|
1416
|
+
def get_system_datetime(self) -> dict[str, Any]:
|
|
1417
|
+
raise NotImplementedError(
|
|
1418
|
+
"get_system_datetime is only available for devices 5.x.x and up, please update your device."
|
|
1419
|
+
)
|
|
1420
|
+
|
|
1421
|
+
@override
|
|
1422
|
+
def set_system_datetime(
|
|
1423
|
+
self,
|
|
1424
|
+
ntp_enabled: bool | None = None,
|
|
1425
|
+
ntp_server: str | None = None,
|
|
1426
|
+
timezone: str | None = None,
|
|
1427
|
+
time: int | None = None,
|
|
1428
|
+
) -> None:
|
|
1429
|
+
raise NotImplementedError(
|
|
1430
|
+
"set_system_datetime is only available for devices 5.x.x and up, please update your device."
|
|
1431
|
+
)
|
|
1432
|
+
|
|
1433
|
+
@override
|
|
1434
|
+
def system_reset(self) -> None:
|
|
1435
|
+
raise NotImplementedError(
|
|
1436
|
+
"system_reset is only available for devices 5.x.x and up, please update your device."
|
|
1437
|
+
)
|
|
1438
|
+
|
|
1439
|
+
@override
|
|
1440
|
+
def get_ethernet_settings(self) -> dict[str, Any]:
|
|
1441
|
+
raise NotImplementedError(
|
|
1442
|
+
"get_ethernet_settings is only available for devices 5.x.x and up, please update your device."
|
|
1443
|
+
)
|
|
1444
|
+
|
|
1445
|
+
@override
|
|
1446
|
+
def set_ethernet_settings(
|
|
1447
|
+
self,
|
|
1448
|
+
network_mode: str | None = None,
|
|
1449
|
+
ip: str | None = None,
|
|
1450
|
+
netmask: str | None = None,
|
|
1451
|
+
gateway: str | None = None,
|
|
1452
|
+
dns: str | None = None,
|
|
1453
|
+
) -> None:
|
|
1454
|
+
raise NotImplementedError(
|
|
1455
|
+
"set_ethernet_settings is only available for devices 5.x.x and up, please update your device."
|
|
1456
|
+
)
|
|
1457
|
+
|
|
1458
|
+
@override
|
|
1459
|
+
def get_ethernet_status(self) -> dict[str, Any]:
|
|
1460
|
+
raise NotImplementedError(
|
|
1461
|
+
"get_ethernet_status is only available for devices 5.x.x and up, please update your device."
|
|
1462
|
+
)
|
|
1463
|
+
|
|
1464
|
+
@override
|
|
1465
|
+
def get_wifi_status(self) -> dict[str, Any]:
|
|
1466
|
+
raise NotImplementedError(
|
|
1467
|
+
"get_wifi_status is only available for devices 5.x.x and up, please update your device."
|
|
1468
|
+
)
|
|
1469
|
+
|
|
1470
|
+
@override
|
|
1471
|
+
def get_server_settings(self) -> dict[str, Any]:
|
|
1472
|
+
raise NotImplementedError(
|
|
1473
|
+
"get_server_settings is only available for devices 5.x.x and up, please update your device."
|
|
1474
|
+
)
|
|
1475
|
+
|
|
1476
|
+
@override
|
|
1477
|
+
def set_server_settings(
|
|
1478
|
+
self,
|
|
1479
|
+
http_enable: bool | None = None,
|
|
1480
|
+
http_port: int | None = None,
|
|
1481
|
+
https_enable: bool | None = None,
|
|
1482
|
+
https_port: int | None = None,
|
|
1483
|
+
) -> None:
|
|
1484
|
+
raise NotImplementedError(
|
|
1485
|
+
"set_server_settings is only available for devices 5.x.x and up, please update your device."
|
|
1486
|
+
)
|
|
1487
|
+
|
|
1488
|
+
@override
|
|
1489
|
+
def get_global_measurement(self) -> dict[str, Any]:
|
|
1490
|
+
raise NotImplementedError(
|
|
1491
|
+
"get_global_measurement is only available for devices 5.x.x and up, please update your device."
|
|
1492
|
+
)
|
|
1493
|
+
|
|
1494
|
+
@override
|
|
1495
|
+
def get_outputs_list(self) -> list[dict[str, Any]]:
|
|
1496
|
+
raise NotImplementedError(
|
|
1497
|
+
"get_outputs_list is only available for devices 5.x.x and up, please update your device."
|
|
1498
|
+
)
|
|
1499
|
+
|
|
1500
|
+
@override
|
|
1501
|
+
def get_firmware_updates_list(self) -> list[dict[str, Any]]:
|
|
1502
|
+
raise NotImplementedError(
|
|
1503
|
+
"get_firmware_updates_list is only available for devices 5.x.x and up, please update your device."
|
|
1504
|
+
)
|
|
1505
|
+
|
|
1506
|
+
@override
|
|
1507
|
+
def get_system_lock(self) -> dict[str, Any]:
|
|
1508
|
+
raise NotImplementedError(
|
|
1509
|
+
"get_system_lock is only available for devices 5.x.x and up, please update your device."
|
|
1510
|
+
)
|
|
1511
|
+
|
|
1512
|
+
@override
|
|
1513
|
+
def get_system_notifications(self) -> list[dict[str, Any]]:
|
|
1514
|
+
raise NotImplementedError(
|
|
1515
|
+
"get_system_notifications is only available for devices 5.x.x and up, please update your device."
|
|
1516
|
+
)
|