1 """aioasuswrt and pyasuswrt bridge classes."""
3 from __future__
import annotations
5 from abc
import ABC, abstractmethod
6 from collections
import namedtuple
7 from collections.abc
import Awaitable, Callable, Coroutine
8 from datetime
import datetime
11 from typing
import Any, cast
13 from aioasuswrt.asuswrt
import AsusWrt
as AsusWrtLegacy
14 from aiohttp
import ClientSession
15 from pyasuswrt
import AsusWrtError, AsusWrtHttp
16 from pyasuswrt.exceptions
import AsusWrtNotAvailableInfoError
49 SENSORS_TEMPERATURES_LEGACY,
53 SENSORS_TYPE_BYTES =
"sensors_bytes"
54 SENSORS_TYPE_COUNT =
"sensors_count"
55 SENSORS_TYPE_CPU =
"sensors_cpu"
56 SENSORS_TYPE_LOAD_AVG =
"sensors_load_avg"
57 SENSORS_TYPE_MEMORY =
"sensors_memory"
58 SENSORS_TYPE_RATES =
"sensors_rates"
59 SENSORS_TYPE_TEMPERATURES =
"sensors_temperatures"
60 SENSORS_TYPE_UPTIME =
"sensors_uptime"
62 WrtDevice = namedtuple(
"WrtDevice", [
"ip",
"name",
"connected_to"])
64 _LOGGER = logging.getLogger(__name__)
66 type _FuncType[_T] = Callable[[_T], Awaitable[list[Any] | tuple[Any] | dict[str, Any]]]
67 type _ReturnFuncType[_T] = Callable[[_T], Coroutine[Any, Any, dict[str, Any]]]
70 def handle_errors_and_zip[_AsusWrtBridgeT: AsusWrtBridge](
71 exceptions: type[Exception] | tuple[type[Exception], ...], keys: list[str] |
None
72 ) -> Callable[[_FuncType[_AsusWrtBridgeT]], _ReturnFuncType[_AsusWrtBridgeT]]:
73 """Run library methods and zip results or manage exceptions."""
75 def _handle_errors_and_zip(
76 func: _FuncType[_AsusWrtBridgeT],
77 ) -> _ReturnFuncType[_AsusWrtBridgeT]:
78 """Run library methods and zip results or manage exceptions."""
80 @functools.wraps(func)
81 async
def _wrapper(self: _AsusWrtBridgeT) -> dict[str, Any]:
83 data = await func(self)
84 except exceptions
as exc:
88 if not isinstance(data, dict):
92 if isinstance(data, dict):
93 return dict(zip(keys,
list(data.values()), strict=
False))
94 if not isinstance(data, (list, tuple)):
96 return dict(zip(keys, data, strict=
False))
100 return _handle_errors_and_zip
104 """The Base Bridge abstract class."""
108 hass: HomeAssistant, conf: dict[str, Any], options: dict[str, Any] |
None =
None
110 """Get Bridge instance."""
111 if conf[CONF_PROTOCOL]
in (PROTOCOL_HTTPS, PROTOCOL_HTTP):
117 """Initialize Bridge."""
119 self._firmware: str |
None =
None
120 self._label_mac: str |
None =
None
121 self._model: str |
None =
None
125 """Return hostname."""
126 return self.
_host_host
130 """Return firmware information."""
131 return self._firmware
135 """Return label mac information."""
136 return self._label_mac
140 """Return model information."""
146 """Get connected status."""
150 """Connect to the device."""
154 """Disconnect to the device."""
158 """Get list of connected devices."""
162 """Return a dictionary of available sensors for this bridge."""
165 class AsusWrtLegacyBridge(AsusWrtBridge):
166 """The Bridge that use legacy library."""
169 self, conf: dict[str, Any], options: dict[str, Any] |
None =
None
171 """Initialize Bridge."""
173 self.
_protocol_protocol: str = conf[CONF_PROTOCOL]
174 self._api: AsusWrtLegacy = self.
_get_api_get_api(conf, options)
178 conf: dict[str, Any], options: dict[str, Any] |
None =
None
180 """Get the AsusWrtLegacy API."""
183 return AsusWrtLegacy(
186 conf[CONF_PROTOCOL] == PROTOCOL_TELNET,
188 conf.get(CONF_PASSWORD,
""),
189 conf.get(CONF_SSH_KEY,
""),
191 opt.get(CONF_REQUIRE_IP,
True),
192 interface=opt.get(CONF_INTERFACE, DEFAULT_INTERFACE),
193 dnsmasq=opt.get(CONF_DNSMASQ, DEFAULT_DNSMASQ),
198 """Get connected status."""
199 return cast(bool, self._api.is_connected)
202 """Connect to the device."""
203 await self._api.connection.async_connect()
210 if self.
_model_model
is None:
214 """Disconnect to the device."""
215 if self._api
is not None and self.
_protocol_protocol == PROTOCOL_TELNET:
216 self._api.connection.disconnect()
219 """Get list of connected devices."""
223 for mac, dev
in api_devices.items()
227 """Get AsusWrt router info from nvram."""
230 info = await self._api.async_get_nvram(info_type)
231 except OSError
as exc:
233 "Error calling method async_get_nvram(%s): %s", info_type, exc
239 """Get label mac information."""
241 if label_mac
and "label_mac" in label_mac:
245 """Get firmware information."""
247 if firmware
and "firmver" in firmware:
248 firmver: str = firmware[
"firmver"]
249 if "buildno" in firmware:
250 firmver += f
" (build {firmware['buildno']})"
254 """Get model information."""
256 if model
and "model" in model:
260 """Return a dictionary of available sensors for this bridge."""
263 SENSORS_TYPE_BYTES: {
264 KEY_SENSORS: SENSORS_BYTES,
267 SENSORS_TYPE_LOAD_AVG: {
268 KEY_SENSORS: SENSORS_LOAD_AVG,
271 SENSORS_TYPE_RATES: {
272 KEY_SENSORS: SENSORS_RATES,
275 SENSORS_TYPE_TEMPERATURES: {
276 KEY_SENSORS: sensors_temperatures,
282 """Check which temperature information is available on the router."""
283 availability = await self._api.async_find_temperature_commands()
284 return [SENSORS_TEMPERATURES_LEGACY[i]
for i
in range(3)
if availability[i]]
286 @handle_errors_and_zip((IndexError, OSError, ValueError), SENSORS_BYTES)
288 """Fetch byte information from the router."""
289 return await self._api.async_get_bytes_total()
291 @handle_errors_and_zip((IndexError, OSError, ValueError), SENSORS_RATES)
293 """Fetch rates information from the router."""
294 return await self._api.async_get_current_transfer_rates()
296 @handle_errors_and_zip((IndexError, OSError, ValueError), SENSORS_LOAD_AVG)
298 """Fetch load average information from the router."""
299 return await self._api.async_get_loadavg()
301 @handle_errors_and_zip((OSError, ValueError),
None)
303 """Fetch temperatures information from the router."""
304 return await self._api.async_get_temperature()
308 """The Bridge that use HTTP library."""
310 def __init__(self, conf: dict[str, Any], session: ClientSession) ->
None:
311 """Initialize Bridge that use HTTP library."""
313 self._api: AsusWrtHttp = self.
_get_api_get_api(conf, session)
316 def _get_api(conf: dict[str, Any], session: ClientSession) -> AsusWrtHttp:
317 """Get the AsusWrtHttp API."""
321 conf.get(CONF_PASSWORD,
""),
322 use_https=conf[CONF_PROTOCOL] == PROTOCOL_HTTPS,
323 port=conf.get(CONF_PORT),
329 """Get connected status."""
330 return cast(bool, self._api.is_connected)
333 """Connect to the device."""
337 if mac := self._api.mac:
343 """Disconnect to the device."""
347 """Get list of connected devices."""
351 for mac, dev
in api_devices.items()
355 """Return a dictionary of available sensors for this bridge."""
360 SENSORS_TYPE_BYTES: {
361 KEY_SENSORS: SENSORS_BYTES,
365 KEY_SENSORS: sensors_cpu,
368 SENSORS_TYPE_LOAD_AVG: {
369 KEY_SENSORS: sensors_loadavg,
372 SENSORS_TYPE_MEMORY: {
373 KEY_SENSORS: SENSORS_MEMORY,
376 SENSORS_TYPE_RATES: {
377 KEY_SENSORS: SENSORS_RATES,
380 SENSORS_TYPE_UPTIME: {
381 KEY_SENSORS: SENSORS_UPTIME,
384 SENSORS_TYPE_TEMPERATURES: {
385 KEY_SENSORS: sensors_temperatures,
391 """Check which cpu information is available on the router."""
393 available_cpu = await self._api.async_get_cpu_usage()
394 available_sensors = [t
for t
in SENSORS_CPU
if t
in available_cpu]
395 except AsusWrtError
as exc:
398 "Failed checking cpu sensor availability for ASUS router"
405 return available_sensors
408 """Check which temperature information is available on the router."""
410 available_temps = await self._api.async_get_temperatures()
411 available_sensors = [
412 t
for t
in SENSORS_TEMPERATURES
if t
in available_temps
414 except AsusWrtError
as exc:
417 "Failed checking temperature sensor availability for ASUS router"
424 return available_sensors
427 """Check if load avg is available on the router."""
429 await self._api.async_get_loadavg()
430 except AsusWrtNotAvailableInfoError:
434 return SENSORS_LOAD_AVG
436 @handle_errors_and_zip(AsusWrtError, SENSORS_BYTES)
438 """Fetch byte information from the router."""
439 return await self._api.async_get_traffic_bytes()
441 @handle_errors_and_zip(AsusWrtError, SENSORS_RATES)
443 """Fetch rates information from the router."""
444 return await self._api.async_get_traffic_rates()
446 @handle_errors_and_zip(AsusWrtError, SENSORS_LOAD_AVG)
448 """Fetch cpu load avg information from the router."""
449 return await self._api.async_get_loadavg()
451 @handle_errors_and_zip(AsusWrtError, None)
453 """Fetch temperatures information from the router."""
454 return await self._api.async_get_temperatures()
456 @handle_errors_and_zip(AsusWrtError, None)
458 """Fetch cpu information from the router."""
459 return await self._api.async_get_cpu_usage()
461 @handle_errors_and_zip(AsusWrtError, None)
463 """Fetch memory information from the router."""
464 return await self._api.async_get_memory_usage()
467 """Fetch uptime from the router."""
469 uptimes = await self._api.async_get_uptime()
470 except AsusWrtError
as exc:
473 last_boot = datetime.fromisoformat(uptimes[
"last_boot"])
474 uptime = uptimes[
"uptime"]
476 return dict(zip(SENSORS_UPTIME, [last_boot, uptime], strict=
False))
dict[str, WrtDevice] async_get_connected_devices(self)
dict[str, dict[str, Any]] async_get_available_sensors(self)
AsusWrtBridge get_bridge(HomeAssistant hass, dict[str, Any] conf, dict[str, Any]|None options=None)
None async_disconnect(self)
None __init__(self, str host)
Any _get_memory_usage(self)
Any _get_temperatures(self)
list[str] _get_available_cpu_sensors(self)
None async_disconnect(self)
None __init__(self, dict[str, Any] conf, ClientSession session)
list[str] _get_available_temperature_sensors(self)
dict[str, dict[str, Any]] async_get_available_sensors(self)
dict[str, WrtDevice] async_get_connected_devices(self)
AsusWrtHttp _get_api(dict[str, Any] conf, ClientSession session)
dict[str, Any] _get_uptime(self)
list[str] _get_loadavg_sensors_availability(self)
None async_disconnect(self)
dict[str, Any] _get_nvram_info(self, str info_type)
None __init__(self, dict[str, Any] conf, dict[str, Any]|None options=None)
dict[str, dict[str, Any]] async_get_available_sensors(self)
None _get_label_mac(self)
dict[str, WrtDevice] async_get_connected_devices(self)
list[str] _get_available_temperature_sensors(self)
Any _get_temperatures(self)
AsusWrtLegacy _get_api(dict[str, Any] conf, dict[str, Any]|None options=None)
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)