2019-04-03 15:40:03 +00:00
|
|
|
"""Asuswrt status sensors."""
|
2020-11-24 03:50:57 +00:00
|
|
|
from datetime import timedelta
|
|
|
|
import enum
|
2018-11-07 17:32:13 +00:00
|
|
|
import logging
|
2020-11-24 03:50:57 +00:00
|
|
|
from typing import Any, Dict, List, Optional
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2020-04-15 15:42:01 +00:00
|
|
|
from aioasuswrt.asuswrt import AsusWrt
|
|
|
|
|
2021-02-13 18:17:06 +00:00
|
|
|
from homeassistant.config_entries import ConfigEntry
|
|
|
|
from homeassistant.const import CONF_NAME, DATA_GIGABYTES, DATA_RATE_MEGABITS_PER_SECOND
|
|
|
|
from homeassistant.helpers.typing import HomeAssistantType
|
2020-11-24 03:50:57 +00:00
|
|
|
from homeassistant.helpers.update_coordinator import (
|
|
|
|
CoordinatorEntity,
|
|
|
|
DataUpdateCoordinator,
|
|
|
|
)
|
2019-03-21 05:56:46 +00:00
|
|
|
|
2021-02-13 18:17:06 +00:00
|
|
|
from .const import DATA_ASUSWRT, DOMAIN, SENSOR_TYPES
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2020-05-14 21:06:33 +00:00
|
|
|
UPLOAD_ICON = "mdi:upload-network"
|
|
|
|
DOWNLOAD_ICON = "mdi:download-network"
|
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
2018-11-07 17:32:13 +00:00
|
|
|
|
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
@enum.unique
|
|
|
|
class _SensorTypes(enum.Enum):
|
|
|
|
DEVICES = "devices"
|
|
|
|
UPLOAD = "upload"
|
|
|
|
DOWNLOAD = "download"
|
|
|
|
DOWNLOAD_SPEED = "download_speed"
|
|
|
|
UPLOAD_SPEED = "upload_speed"
|
2018-11-07 17:32:13 +00:00
|
|
|
|
|
|
|
@property
|
2020-12-07 11:46:53 +00:00
|
|
|
def unit_of_measurement(self) -> Optional[str]:
|
2020-11-24 03:50:57 +00:00
|
|
|
"""Return a string with the unit of the sensortype."""
|
|
|
|
if self in (_SensorTypes.UPLOAD, _SensorTypes.DOWNLOAD):
|
|
|
|
return DATA_GIGABYTES
|
|
|
|
if self in (_SensorTypes.UPLOAD_SPEED, _SensorTypes.DOWNLOAD_SPEED):
|
|
|
|
return DATA_RATE_MEGABITS_PER_SECOND
|
2021-02-13 18:17:06 +00:00
|
|
|
if self == _SensorTypes.DEVICES:
|
|
|
|
return "devices"
|
2020-11-24 03:50:57 +00:00
|
|
|
return None
|
2018-11-07 17:32:13 +00:00
|
|
|
|
|
|
|
@property
|
2020-11-24 03:50:57 +00:00
|
|
|
def icon(self) -> Optional[str]:
|
|
|
|
"""Return the expected icon for the sensortype."""
|
|
|
|
if self in (_SensorTypes.UPLOAD, _SensorTypes.UPLOAD_SPEED):
|
|
|
|
return UPLOAD_ICON
|
|
|
|
if self in (_SensorTypes.DOWNLOAD, _SensorTypes.DOWNLOAD_SPEED):
|
|
|
|
return DOWNLOAD_ICON
|
|
|
|
return None
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
@property
|
|
|
|
def sensor_name(self) -> Optional[str]:
|
|
|
|
"""Return the name of the sensor."""
|
|
|
|
if self is _SensorTypes.DEVICES:
|
|
|
|
return "Asuswrt Devices Connected"
|
|
|
|
if self is _SensorTypes.UPLOAD:
|
|
|
|
return "Asuswrt Upload"
|
|
|
|
if self is _SensorTypes.DOWNLOAD:
|
|
|
|
return "Asuswrt Download"
|
|
|
|
if self is _SensorTypes.UPLOAD_SPEED:
|
|
|
|
return "Asuswrt Upload Speed"
|
|
|
|
if self is _SensorTypes.DOWNLOAD_SPEED:
|
|
|
|
return "Asuswrt Download Speed"
|
|
|
|
return None
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2020-05-14 21:06:33 +00:00
|
|
|
@property
|
2020-11-24 03:50:57 +00:00
|
|
|
def is_speed(self) -> bool:
|
|
|
|
"""Return True if the type is an upload/download speed."""
|
|
|
|
return self in (_SensorTypes.UPLOAD_SPEED, _SensorTypes.DOWNLOAD_SPEED)
|
2020-05-14 21:06:33 +00:00
|
|
|
|
2018-11-07 17:32:13 +00:00
|
|
|
@property
|
2020-11-24 03:50:57 +00:00
|
|
|
def is_size(self) -> bool:
|
|
|
|
"""Return True if the type is the total upload/download size."""
|
|
|
|
return self in (_SensorTypes.UPLOAD, _SensorTypes.DOWNLOAD)
|
2018-11-07 17:32:13 +00:00
|
|
|
|
|
|
|
|
2021-02-13 18:17:06 +00:00
|
|
|
class _SensorInfo:
|
|
|
|
"""Class handling sensor information."""
|
|
|
|
|
|
|
|
def __init__(self, sensor_type: _SensorTypes):
|
|
|
|
"""Initialize the handler class."""
|
|
|
|
self.type = sensor_type
|
|
|
|
self.enabled = False
|
|
|
|
|
|
|
|
|
|
|
|
async def async_setup_entry(
|
|
|
|
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
|
|
|
|
) -> None:
|
2020-11-24 03:50:57 +00:00
|
|
|
"""Set up the asuswrt sensors."""
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2021-02-13 18:17:06 +00:00
|
|
|
router = hass.data[DOMAIN][entry.entry_id][DATA_ASUSWRT]
|
|
|
|
api: AsusWrt = router.api
|
|
|
|
device_name = entry.data.get(CONF_NAME, "AsusWRT")
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
# Let's discover the valid sensor types.
|
2021-02-13 18:17:06 +00:00
|
|
|
sensors = [_SensorInfo(_SensorTypes(x)) for x in SENSOR_TYPES]
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
data_handler = AsuswrtDataHandler(sensors, api)
|
|
|
|
coordinator = DataUpdateCoordinator(
|
|
|
|
hass,
|
|
|
|
_LOGGER,
|
|
|
|
name="sensor",
|
|
|
|
update_method=data_handler.update_data,
|
|
|
|
# Polling interval. Will only be polled if there are subscribers.
|
|
|
|
update_interval=timedelta(seconds=30),
|
|
|
|
)
|
2020-05-14 21:06:33 +00:00
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
await coordinator.async_refresh()
|
2021-02-13 18:17:06 +00:00
|
|
|
async_add_entities(
|
|
|
|
[AsuswrtSensor(coordinator, data_handler, device_name, x.type) for x in sensors]
|
|
|
|
)
|
2018-11-07 17:32:13 +00:00
|
|
|
|
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
class AsuswrtDataHandler:
|
|
|
|
"""Class handling the API updates."""
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2021-02-13 18:17:06 +00:00
|
|
|
def __init__(self, sensors: List[_SensorInfo], api: AsusWrt):
|
2020-11-24 03:50:57 +00:00
|
|
|
"""Initialize the handler class."""
|
|
|
|
self._api = api
|
|
|
|
self._sensors = sensors
|
|
|
|
self._connected = True
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2021-02-13 18:17:06 +00:00
|
|
|
def enable_sensor(self, sensor_type: _SensorTypes):
|
|
|
|
"""Enable a specific sensor type."""
|
|
|
|
for index, sensor in enumerate(self._sensors):
|
|
|
|
if sensor.type == sensor_type:
|
|
|
|
self._sensors[index].enabled = True
|
|
|
|
return
|
|
|
|
|
|
|
|
def disable_sensor(self, sensor_type: _SensorTypes):
|
|
|
|
"""Disable a specific sensor type."""
|
|
|
|
for index, sensor in enumerate(self._sensors):
|
|
|
|
if sensor.type == sensor_type:
|
|
|
|
self._sensors[index].enabled = False
|
|
|
|
return
|
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
async def update_data(self) -> Dict[_SensorTypes, Any]:
|
|
|
|
"""Fetch the relevant data from the router."""
|
|
|
|
ret_dict: Dict[_SensorTypes, Any] = {}
|
|
|
|
try:
|
2021-02-13 18:17:06 +00:00
|
|
|
if _SensorTypes.DEVICES in [x.type for x in self._sensors if x.enabled]:
|
2020-11-24 03:50:57 +00:00
|
|
|
# Let's check the nr of devices.
|
|
|
|
devices = await self._api.async_get_connected_devices()
|
|
|
|
ret_dict[_SensorTypes.DEVICES] = len(devices)
|
|
|
|
|
2021-02-13 18:17:06 +00:00
|
|
|
if any(x.type.is_speed for x in self._sensors if x.enabled):
|
2020-11-24 03:50:57 +00:00
|
|
|
# Let's check the upload and download speed
|
|
|
|
speed = await self._api.async_get_current_transfer_rates()
|
|
|
|
ret_dict[_SensorTypes.DOWNLOAD_SPEED] = round(speed[0] / 125000, 2)
|
|
|
|
ret_dict[_SensorTypes.UPLOAD_SPEED] = round(speed[1] / 125000, 2)
|
|
|
|
|
2021-02-13 18:17:06 +00:00
|
|
|
if any(x.type.is_size for x in self._sensors if x.enabled):
|
2020-11-24 03:50:57 +00:00
|
|
|
rates = await self._api.async_get_bytes_total()
|
|
|
|
ret_dict[_SensorTypes.DOWNLOAD] = round(rates[0] / 1000000000, 1)
|
|
|
|
ret_dict[_SensorTypes.UPLOAD] = round(rates[1] / 1000000000, 1)
|
|
|
|
|
|
|
|
if not self._connected:
|
|
|
|
# Log a successful reconnect
|
|
|
|
self._connected = True
|
|
|
|
_LOGGER.warning("Successfully reconnected to ASUS router")
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
except OSError as err:
|
|
|
|
if self._connected:
|
|
|
|
# Log the first time connection was lost
|
|
|
|
_LOGGER.warning("Lost connection to router error due to: '%s'", err)
|
|
|
|
self._connected = False
|
2020-05-14 21:06:33 +00:00
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
return ret_dict
|
2018-11-07 17:32:13 +00:00
|
|
|
|
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
class AsuswrtSensor(CoordinatorEntity):
|
|
|
|
"""The asuswrt specific sensor class."""
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2021-02-13 18:17:06 +00:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
coordinator: DataUpdateCoordinator,
|
|
|
|
data_handler: AsuswrtDataHandler,
|
|
|
|
device_name: str,
|
|
|
|
sensor_type: _SensorTypes,
|
|
|
|
):
|
2020-11-24 03:50:57 +00:00
|
|
|
"""Initialize the sensor class."""
|
|
|
|
super().__init__(coordinator)
|
2021-02-13 18:17:06 +00:00
|
|
|
self._handler = data_handler
|
|
|
|
self._device_name = device_name
|
2020-11-24 03:50:57 +00:00
|
|
|
self._type = sensor_type
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2020-11-24 03:50:57 +00:00
|
|
|
@property
|
|
|
|
def state(self):
|
|
|
|
"""Return the state of the sensor."""
|
|
|
|
return self.coordinator.data.get(self._type)
|
2018-11-07 17:32:13 +00:00
|
|
|
|
2020-05-14 21:06:33 +00:00
|
|
|
@property
|
2020-11-24 03:50:57 +00:00
|
|
|
def name(self) -> str:
|
|
|
|
"""Return the name of the sensor."""
|
|
|
|
return self._type.sensor_name
|
2020-05-14 21:06:33 +00:00
|
|
|
|
2018-11-07 17:32:13 +00:00
|
|
|
@property
|
2020-11-24 03:50:57 +00:00
|
|
|
def icon(self) -> Optional[str]:
|
|
|
|
"""Return the icon to use in the frontend."""
|
|
|
|
return self._type.icon
|
2020-12-07 11:46:53 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def unit_of_measurement(self) -> Optional[str]:
|
2021-02-13 18:17:06 +00:00
|
|
|
"""Return the unit."""
|
2020-12-07 11:46:53 +00:00
|
|
|
return self._type.unit_of_measurement
|
2021-02-13 18:17:06 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def unique_id(self) -> str:
|
|
|
|
"""Return the unique_id of the sensor."""
|
|
|
|
return f"{DOMAIN} {self._type.sensor_name}"
|
|
|
|
|
|
|
|
@property
|
|
|
|
def device_info(self) -> Dict[str, any]:
|
|
|
|
"""Return the device information."""
|
|
|
|
return {
|
|
|
|
"identifiers": {(DOMAIN, "AsusWRT")},
|
|
|
|
"name": self._device_name,
|
|
|
|
"model": "Asus Router",
|
|
|
|
"manufacturer": "Asus",
|
|
|
|
}
|
|
|
|
|
|
|
|
@property
|
|
|
|
def entity_registry_enabled_default(self) -> bool:
|
|
|
|
"""Return if the entity should be enabled when first added to the entity registry."""
|
|
|
|
return False
|
|
|
|
|
|
|
|
async def async_added_to_hass(self) -> None:
|
|
|
|
"""When entity is added to hass."""
|
|
|
|
self._handler.enable_sensor(self._type)
|
|
|
|
await super().async_added_to_hass()
|
|
|
|
|
|
|
|
async def async_will_remove_from_hass(self):
|
|
|
|
"""Call when entity is removed from hass."""
|
|
|
|
self._handler.disable_sensor(self._type)
|