2022-02-01 10:12:38 +01:00
|
|
|
"""Implementation of Mikrotik Router sensor entities."""
|
2019-12-03 03:09:30 +01:00
|
|
|
|
|
|
|
import logging
|
2022-02-01 10:12:38 +01:00
|
|
|
|
2022-02-01 14:07:21 +01:00
|
|
|
from typing import Any, Optional
|
2022-02-01 14:06:33 +01:00
|
|
|
from collections.abc import Mapping
|
2020-03-21 19:02:28 +03:00
|
|
|
|
2020-05-27 20:04:09 +02:00
|
|
|
from homeassistant.const import (
|
|
|
|
CONF_NAME,
|
2021-12-13 13:26:46 +01:00
|
|
|
CONF_HOST,
|
2020-05-27 20:04:09 +02:00
|
|
|
ATTR_ATTRIBUTION,
|
|
|
|
)
|
2020-12-12 12:19:40 +01:00
|
|
|
|
2022-02-01 10:12:38 +01:00
|
|
|
from homeassistant.helpers.entity import DeviceInfo
|
|
|
|
from homeassistant.components.sensor import SensorEntity
|
2021-12-13 11:42:55 +01:00
|
|
|
|
2020-12-12 12:19:40 +01:00
|
|
|
from .const import (
|
|
|
|
CONF_SENSOR_PORT_TRAFFIC,
|
|
|
|
DEFAULT_SENSOR_PORT_TRAFFIC,
|
|
|
|
)
|
|
|
|
|
2019-12-03 03:09:30 +01:00
|
|
|
from homeassistant.core import callback
|
|
|
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
2020-03-21 19:02:28 +03:00
|
|
|
|
2020-04-11 05:45:36 +02:00
|
|
|
from .const import DOMAIN, DATA_CLIENT, ATTRIBUTION
|
2022-02-01 10:12:38 +01:00
|
|
|
from .sensor_types import (
|
|
|
|
MikrotikSensorEntityDescription,
|
|
|
|
SENSOR_TYPES,
|
|
|
|
)
|
2019-12-03 03:09:30 +01:00
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
2020-04-04 19:42:05 +02:00
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# format_attribute
|
|
|
|
# ---------------------------
|
|
|
|
def format_attribute(attr):
|
|
|
|
res = attr.replace("-", " ")
|
|
|
|
res = res.capitalize()
|
|
|
|
res = res.replace(" ip ", " IP ")
|
|
|
|
res = res.replace(" mac ", " MAC ")
|
|
|
|
res = res.replace(" mtu", " MTU")
|
2021-04-12 14:29:15 +02:00
|
|
|
res = res.replace("Sfp", "SFP")
|
|
|
|
res = res.replace("Poe", "POE")
|
|
|
|
res = res.replace(" tx", " TX")
|
|
|
|
res = res.replace(" rx", " RX")
|
2020-04-04 19:42:05 +02:00
|
|
|
return res
|
|
|
|
|
|
|
|
|
2019-12-03 03:09:30 +01:00
|
|
|
# ---------------------------
|
|
|
|
# async_setup_entry
|
|
|
|
# ---------------------------
|
|
|
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
|
|
|
"""Set up device tracker for Mikrotik Router component."""
|
2019-12-06 01:22:34 +01:00
|
|
|
inst = config_entry.data[CONF_NAME]
|
2019-12-03 03:09:30 +01:00
|
|
|
mikrotik_controller = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id]
|
|
|
|
sensors = {}
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2019-12-03 03:09:30 +01:00
|
|
|
@callback
|
|
|
|
def update_controller():
|
|
|
|
"""Update the values of the controller."""
|
2020-12-12 12:19:40 +01:00
|
|
|
update_items(
|
|
|
|
inst, config_entry, mikrotik_controller, async_add_entities, sensors
|
|
|
|
)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2019-12-03 03:09:30 +01:00
|
|
|
mikrotik_controller.listeners.append(
|
2020-03-16 04:51:41 +01:00
|
|
|
async_dispatcher_connect(
|
|
|
|
hass, mikrotik_controller.signal_update, update_controller
|
|
|
|
)
|
2019-12-03 03:09:30 +01:00
|
|
|
)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2019-12-03 03:09:30 +01:00
|
|
|
update_controller()
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# update_items
|
|
|
|
# ---------------------------
|
|
|
|
@callback
|
2020-12-12 12:19:40 +01:00
|
|
|
def update_items(inst, config_entry, mikrotik_controller, async_add_entities, sensors):
|
2019-12-03 03:09:30 +01:00
|
|
|
"""Update sensor state from the controller."""
|
|
|
|
new_sensors = []
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2022-02-01 10:12:38 +01:00
|
|
|
for sensor, sid_func in zip(
|
2020-12-26 00:12:53 +01:00
|
|
|
# Data point name
|
|
|
|
["environment"],
|
|
|
|
# Switch function
|
|
|
|
[
|
2022-02-01 10:12:38 +01:00
|
|
|
MikrotikControllerSensor,
|
2020-12-26 00:12:53 +01:00
|
|
|
],
|
|
|
|
):
|
2022-02-01 10:12:38 +01:00
|
|
|
for uid in mikrotik_controller.data[sensor]:
|
|
|
|
item_id = f"{inst}-{sensor}-{mikrotik_controller.data[sensor][uid][SENSOR_TYPES[sensor].data_uid]}"
|
2020-12-26 00:12:53 +01:00
|
|
|
_LOGGER.debug("Updating sensor %s", item_id)
|
|
|
|
if item_id in sensors:
|
|
|
|
if sensors[item_id].enabled:
|
|
|
|
sensors[item_id].async_schedule_update_ha_state()
|
|
|
|
continue
|
|
|
|
|
2022-02-01 10:12:38 +01:00
|
|
|
sensors[item_id] = sid_func(
|
|
|
|
inst=inst,
|
|
|
|
uid=uid,
|
|
|
|
mikrotik_controller=mikrotik_controller,
|
|
|
|
entity_description=SENSOR_TYPES[sensor],
|
|
|
|
)
|
2020-12-26 00:12:53 +01:00
|
|
|
new_sensors.append(sensors[item_id])
|
|
|
|
|
2019-12-03 03:09:30 +01:00
|
|
|
for sensor in SENSOR_TYPES:
|
2022-01-02 08:17:58 +00:00
|
|
|
if sensor.startswith("system_"):
|
2020-12-14 15:25:00 +01:00
|
|
|
if (
|
2022-02-01 10:12:38 +01:00
|
|
|
SENSOR_TYPES[sensor].data_attribute
|
|
|
|
not in mikrotik_controller.data[SENSOR_TYPES[sensor].data_path]
|
|
|
|
or mikrotik_controller.data[SENSOR_TYPES[sensor].data_path][
|
|
|
|
SENSOR_TYPES[sensor].data_attribute
|
2020-12-14 15:25:00 +01:00
|
|
|
]
|
|
|
|
== "unknown"
|
|
|
|
):
|
|
|
|
continue
|
2020-03-16 19:02:54 +01:00
|
|
|
item_id = f"{inst}-{sensor}"
|
2020-04-04 19:46:40 +02:00
|
|
|
_LOGGER.debug("Updating sensor %s", item_id)
|
2019-12-09 10:07:36 +01:00
|
|
|
if item_id in sensors:
|
|
|
|
if sensors[item_id].enabled:
|
|
|
|
sensors[item_id].async_schedule_update_ha_state()
|
|
|
|
continue
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2020-03-16 04:51:41 +01:00
|
|
|
sensors[item_id] = MikrotikControllerSensor(
|
2022-02-01 10:12:38 +01:00
|
|
|
inst=inst,
|
|
|
|
uid="",
|
|
|
|
mikrotik_controller=mikrotik_controller,
|
|
|
|
entity_description=SENSOR_TYPES[sensor],
|
2020-03-16 04:51:41 +01:00
|
|
|
)
|
2019-12-09 10:07:36 +01:00
|
|
|
new_sensors.append(sensors[item_id])
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2022-02-01 14:06:33 +01:00
|
|
|
if sensor.startswith("traffic_"):
|
|
|
|
if not config_entry.options.get(
|
|
|
|
CONF_SENSOR_PORT_TRAFFIC, DEFAULT_SENSOR_PORT_TRAFFIC
|
|
|
|
):
|
|
|
|
continue
|
|
|
|
|
|
|
|
for uid in mikrotik_controller.data["interface"]:
|
|
|
|
if mikrotik_controller.data["interface"][uid]["type"] != "bridge":
|
|
|
|
item_id = f"{inst}-{sensor}-{mikrotik_controller.data['interface'][uid]['default-name']}"
|
|
|
|
_LOGGER.debug("Updating sensor %s", item_id)
|
|
|
|
if item_id in sensors:
|
|
|
|
if sensors[item_id].enabled:
|
|
|
|
sensors[item_id].async_schedule_update_ha_state()
|
|
|
|
continue
|
|
|
|
|
|
|
|
sensors[item_id] = MikrotikControllerSensor(
|
|
|
|
inst=inst,
|
|
|
|
mikrotik_controller=mikrotik_controller,
|
|
|
|
uid=uid,
|
|
|
|
entity_description=SENSOR_TYPES[sensor],
|
|
|
|
)
|
|
|
|
new_sensors.append(sensors[item_id])
|
|
|
|
|
|
|
|
if sensor.startswith("client_traffic_"):
|
|
|
|
for uid in mikrotik_controller.data["client_traffic"]:
|
|
|
|
item_id = f"{inst}-{sensor}-{mikrotik_controller.data['client_traffic'][uid]['mac-address']}"
|
|
|
|
if item_id in sensors:
|
|
|
|
if sensors[item_id].enabled:
|
|
|
|
sensors[item_id].async_schedule_update_ha_state()
|
|
|
|
continue
|
|
|
|
|
|
|
|
if (
|
|
|
|
SENSOR_TYPES[sensor].data_attribute
|
|
|
|
in mikrotik_controller.data["client_traffic"][uid].keys()
|
|
|
|
):
|
|
|
|
sensors[item_id] = MikrotikClientTrafficSensor(
|
|
|
|
inst=inst,
|
|
|
|
mikrotik_controller=mikrotik_controller,
|
|
|
|
uid=uid,
|
|
|
|
entity_description=SENSOR_TYPES[sensor],
|
|
|
|
)
|
|
|
|
new_sensors.append(sensors[item_id])
|
2020-04-05 23:23:07 +02:00
|
|
|
|
2019-12-03 03:09:30 +01:00
|
|
|
if new_sensors:
|
|
|
|
async_add_entities(new_sensors, True)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2019-12-03 03:09:30 +01:00
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# MikrotikControllerSensor
|
|
|
|
# ---------------------------
|
2021-04-21 22:04:22 +02:00
|
|
|
class MikrotikControllerSensor(SensorEntity):
|
2019-12-03 03:09:30 +01:00
|
|
|
"""Define an Mikrotik Controller sensor."""
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2022-02-01 10:12:38 +01:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
mikrotik_controller,
|
|
|
|
inst,
|
|
|
|
uid: "",
|
|
|
|
entity_description: MikrotikSensorEntityDescription,
|
|
|
|
):
|
2019-12-03 03:09:30 +01:00
|
|
|
"""Initialize."""
|
2022-02-01 10:12:38 +01:00
|
|
|
self.entity_description = entity_description
|
2022-02-01 14:06:33 +01:00
|
|
|
self._inst = inst
|
2019-12-06 01:22:34 +01:00
|
|
|
self._ctrl = mikrotik_controller
|
2022-02-01 10:12:38 +01:00
|
|
|
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
|
|
|
self._uid = uid
|
|
|
|
if self._uid:
|
|
|
|
self._data = mikrotik_controller.data[self.entity_description.data_path][
|
|
|
|
self._uid
|
|
|
|
]
|
2020-12-26 00:12:53 +01:00
|
|
|
else:
|
2022-02-01 10:12:38 +01:00
|
|
|
self._data = mikrotik_controller.data[self.entity_description.data_path]
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2019-12-03 03:09:30 +01:00
|
|
|
@property
|
2020-12-25 20:28:36 +01:00
|
|
|
def name(self) -> str:
|
2019-12-03 03:09:30 +01:00
|
|
|
"""Return the name."""
|
2022-02-01 10:12:38 +01:00
|
|
|
if self._uid:
|
2022-02-01 14:06:33 +01:00
|
|
|
if self.entity_description.name:
|
|
|
|
return f"{self._inst} {self._data[self.entity_description.data_name]} {self.entity_description.name}"
|
|
|
|
|
|
|
|
return f"{self._inst} {self._data[self.entity_description.data_name]}"
|
2022-02-01 10:12:38 +01:00
|
|
|
else:
|
|
|
|
return f"{self._inst} {self.entity_description.name}"
|
2019-12-03 03:09:30 +01:00
|
|
|
|
|
|
|
@property
|
2020-12-25 20:28:36 +01:00
|
|
|
def unique_id(self) -> str:
|
|
|
|
"""Return a unique id for this entity."""
|
2022-02-01 10:12:38 +01:00
|
|
|
if self._uid:
|
|
|
|
return f"{self._inst.lower()}-{self.entity_description.key}-{self._data[self.entity_description.data_reference].lower()}"
|
|
|
|
else:
|
|
|
|
return f"{self._inst.lower()}-{self.entity_description.key}"
|
2019-12-03 03:09:30 +01:00
|
|
|
|
|
|
|
@property
|
2022-02-01 10:12:38 +01:00
|
|
|
def state(self) -> Optional[str]:
|
|
|
|
"""Return the state."""
|
|
|
|
if self.entity_description.data_attribute:
|
|
|
|
return self._data[self.entity_description.data_attribute]
|
|
|
|
else:
|
|
|
|
return "unknown"
|
2019-12-03 03:09:30 +01:00
|
|
|
|
2022-02-01 14:06:33 +01:00
|
|
|
@property
|
|
|
|
def native_unit_of_measurement(self):
|
|
|
|
"""Return the unit the value is expressed in."""
|
|
|
|
if self.entity_description.native_unit_of_measurement:
|
|
|
|
if self.entity_description.native_unit_of_measurement.startswith("data__"):
|
|
|
|
uom = self.entity_description.native_unit_of_measurement[6:]
|
|
|
|
if uom in self._data:
|
|
|
|
uom = self._data[uom]
|
|
|
|
return uom
|
|
|
|
|
|
|
|
return self.entity_description.native_unit_of_measurement
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
2019-12-03 03:09:30 +01:00
|
|
|
@property
|
2019-12-08 22:42:54 +01:00
|
|
|
def available(self) -> bool:
|
|
|
|
"""Return if controller is available."""
|
|
|
|
return self._ctrl.connected()
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2019-12-03 03:09:30 +01:00
|
|
|
@property
|
2022-02-01 10:12:38 +01:00
|
|
|
def device_info(self) -> DeviceInfo:
|
2020-12-25 20:28:36 +01:00
|
|
|
"""Return a description for device registry."""
|
2022-02-01 14:06:33 +01:00
|
|
|
dev_connection = DOMAIN
|
|
|
|
dev_connection_value = self.entity_description.data_reference
|
|
|
|
dev_name = self.entity_description.ha_group
|
2022-02-01 10:12:38 +01:00
|
|
|
if self.entity_description.ha_group == "System":
|
2022-02-01 14:06:33 +01:00
|
|
|
dev_name = self._ctrl.data["resource"]["board-name"]
|
|
|
|
dev_connection_value = self._ctrl.data["routerboard"]["serial-number"]
|
|
|
|
|
|
|
|
if self.entity_description.ha_group.startswith("data__"):
|
|
|
|
dev_name = self.entity_description.ha_group[6:]
|
|
|
|
if dev_name in self._data:
|
|
|
|
dev_name = self._data[dev_name]
|
|
|
|
dev_connection_value = dev_name
|
|
|
|
|
|
|
|
if self.entity_description.ha_connection:
|
|
|
|
dev_connection = self.entity_description.ha_connection
|
|
|
|
|
|
|
|
if self.entity_description.ha_connection_value:
|
|
|
|
dev_connection_value = self.entity_description.ha_connection_value
|
|
|
|
if dev_connection_value.startswith("data__"):
|
|
|
|
dev_connection_value = dev_connection_value[6:]
|
|
|
|
dev_connection_value = self._data[dev_connection_value]
|
2022-02-01 10:12:38 +01:00
|
|
|
|
|
|
|
info = DeviceInfo(
|
2022-02-01 14:06:33 +01:00
|
|
|
connections={(dev_connection, f"{dev_connection_value}")},
|
|
|
|
default_name=f"{self._inst} {dev_name}",
|
2022-02-01 10:12:38 +01:00
|
|
|
model=f"{self._ctrl.data['resource']['board-name']}",
|
|
|
|
manufacturer=f"{self._ctrl.data['resource']['platform']}",
|
|
|
|
sw_version=f"{self._ctrl.data['resource']['version']}",
|
|
|
|
configuration_url=f"http://{self._ctrl.config_entry.data[CONF_HOST]}",
|
|
|
|
via_device=(DOMAIN, f"{self._ctrl.data['routerboard']['serial-number']}"),
|
|
|
|
)
|
2020-04-10 09:22:28 +02:00
|
|
|
|
2022-02-01 14:06:33 +01:00
|
|
|
if "mac-address" in self.entity_description.data_reference:
|
|
|
|
info = DeviceInfo(
|
|
|
|
connections={(dev_connection, f"{dev_connection_value}")},
|
|
|
|
default_name=f"{self._data[self.entity_description.data_name]}",
|
|
|
|
via_device=(
|
|
|
|
DOMAIN,
|
|
|
|
f"{self._ctrl.data['routerboard']['serial-number']}",
|
|
|
|
),
|
|
|
|
)
|
2019-12-08 11:20:02 +01:00
|
|
|
|
2022-02-01 14:06:33 +01:00
|
|
|
if "manufacturer" in self._data and self._data["manufacturer"] != "":
|
|
|
|
info["manufacturer"] = self._data["manufacturer"]
|
2019-12-08 11:20:02 +01:00
|
|
|
|
2022-02-01 14:06:33 +01:00
|
|
|
return info
|
2021-12-13 10:59:36 +01:00
|
|
|
|
2021-11-07 17:07:43 +00:00
|
|
|
@property
|
2022-02-01 14:06:33 +01:00
|
|
|
def extra_state_attributes(self) -> Mapping[str, Any]:
|
|
|
|
"""Return the state attributes."""
|
|
|
|
attributes = super().extra_state_attributes
|
|
|
|
for variable in self.entity_description.data_attributes_list:
|
|
|
|
if variable in self._data:
|
|
|
|
attributes[format_attribute(variable)] = self._data[variable]
|
2019-12-08 11:20:02 +01:00
|
|
|
|
2022-02-01 14:06:33 +01:00
|
|
|
return attributes
|
2020-04-10 09:22:28 +02:00
|
|
|
|
2022-02-01 14:06:33 +01:00
|
|
|
async def async_added_to_hass(self):
|
|
|
|
"""Run when entity about to be added to hass."""
|
|
|
|
_LOGGER.debug("New sensor %s (%s)", self._inst, self.unique_id)
|
2019-12-10 22:29:35 +01:00
|
|
|
|
2020-04-04 19:42:05 +02:00
|
|
|
|
|
|
|
# ---------------------------
|
2022-01-01 17:26:40 +00:00
|
|
|
# MikrotikClientTrafficSensor
|
2020-04-04 19:42:05 +02:00
|
|
|
# ---------------------------
|
2022-01-01 17:26:40 +00:00
|
|
|
class MikrotikClientTrafficSensor(MikrotikControllerSensor):
|
|
|
|
"""Define an Mikrotik MikrotikClientTrafficSensor sensor."""
|
2020-04-04 19:42:05 +02:00
|
|
|
|
|
|
|
@property
|
2020-12-25 20:28:36 +01:00
|
|
|
def name(self) -> str:
|
2020-04-04 19:42:05 +02:00
|
|
|
"""Return the name."""
|
2022-02-01 14:06:33 +01:00
|
|
|
return f"{self._data[self.entity_description.data_name]} {self.entity_description.name}"
|
2020-04-04 19:42:05 +02:00
|
|
|
|
2020-04-08 13:41:03 +02:00
|
|
|
@property
|
|
|
|
def available(self) -> bool:
|
|
|
|
"""Return if controller and accounting feature in Mikrotik is available.
|
2020-12-02 15:38:17 +01:00
|
|
|
Additional check for lan-tx/rx sensors
|
2020-04-08 13:41:03 +02:00
|
|
|
"""
|
2022-02-01 14:06:33 +01:00
|
|
|
if self.entity_description.data_attribute in ["lan-tx", "lan-rx"]:
|
2020-04-11 05:45:36 +02:00
|
|
|
return (
|
|
|
|
self._ctrl.connected()
|
|
|
|
and self._data["available"]
|
|
|
|
and self._data["local_accounting"]
|
|
|
|
)
|
2020-04-08 13:41:03 +02:00
|
|
|
else:
|
2020-04-11 05:45:36 +02:00
|
|
|
return self._ctrl.connected() and self._data["available"]
|