coordinator update

This commit is contained in:
Tomaae 2023-08-08 00:50:09 +02:00
parent ce9ce906f5
commit a21e95c36a
No known key found for this signature in database
GPG key ID: 2F97770867DAA4E6
14 changed files with 445 additions and 496 deletions

View file

@ -1,80 +1,62 @@
"""Mikrotik Router integration.""" """Mikrotik Router integration."""
from __future__ import annotations
import voluptuous as vol import voluptuous as vol
import homeassistant.helpers.config_validation as cv from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import device_registry from homeassistant.helpers import device_registry
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.exceptions import ConfigEntryNotReady
from .const import ( from .const import (
PLATFORMS, PLATFORMS,
DOMAIN, DOMAIN,
RUN_SCRIPT_COMMAND, RUN_SCRIPT_COMMAND,
) )
from .mikrotik_controller import MikrotikControllerData from .coordinator import MikrotikCoordinator
SCRIPT_SCHEMA = vol.Schema( SCRIPT_SCHEMA = vol.Schema(
{vol.Required("router"): cv.string, vol.Required("script"): cv.string} {vol.Required("router"): cv.string, vol.Required("script"): cv.string}
) )
# ---------------------------
# async_setup
# ---------------------------
async def async_setup(hass, _config):
"""Set up configured Mikrotik Controller."""
hass.data[DOMAIN] = {}
return True
# ---------------------------
# update_listener
# ---------------------------
async def update_listener(hass, config_entry) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(config_entry.entry_id)
# --------------------------- # ---------------------------
# async_setup_entry # async_setup_entry
# --------------------------- # ---------------------------
async def async_setup_entry(hass, config_entry) -> bool: async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Set up Mikrotik Router as config entry.""" """Set up a config entry."""
controller = MikrotikControllerData(hass, config_entry) coordinator = MikrotikCoordinator(hass, config_entry)
await controller.async_hwinfo_update() await coordinator.async_config_entry_first_refresh()
if not controller.connected(): hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = coordinator
raise ConfigEntryNotReady("Cannot connect to host")
await controller.async_update()
if not controller.data:
raise ConfigEntryNotReady()
await controller.async_init()
hass.data[DOMAIN][config_entry.entry_id] = controller
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
config_entry.async_on_unload(config_entry.add_update_listener(update_listener))
config_entry.async_on_unload(config_entry.add_update_listener(async_reload_entry))
hass.services.async_register( hass.services.async_register(
DOMAIN, RUN_SCRIPT_COMMAND, controller.run_script, schema=SCRIPT_SCHEMA DOMAIN, RUN_SCRIPT_COMMAND, coordinator.run_script, schema=SCRIPT_SCHEMA
) )
return True return True
# ---------------------------
# async_reload_entry
# ---------------------------
async def async_reload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
"""Reload the config entry when it changed."""
await hass.config_entries.async_reload(config_entry.entry_id)
# --------------------------- # ---------------------------
# async_unload_entry # async_unload_entry
# --------------------------- # ---------------------------
async def async_unload_entry(hass, config_entry) -> bool: async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(
if unload_ok := await hass.config_entries.async_unload_platforms(
config_entry, PLATFORMS config_entry, PLATFORMS
) ):
if unload_ok:
controller = hass.data[DOMAIN][config_entry.entry_id]
await controller.async_reset()
hass.services.async_remove(DOMAIN, RUN_SCRIPT_COMMAND) hass.services.async_remove(DOMAIN, RUN_SCRIPT_COMMAND)
hass.data[DOMAIN].pop(config_entry.entry_id) hass.data[DOMAIN].pop(config_entry.entry_id)

View file

@ -1,9 +1,12 @@
"""API parser for JSON APIs""" """API parser for JSON APIs."""
from pytz import utc
from logging import getLogger
from datetime import datetime from datetime import datetime
from logging import getLogger
from pytz import utc
from voluptuous import Optional from voluptuous import Optional
from homeassistant.components.diagnostics import async_redact_data from homeassistant.components.diagnostics import async_redact_data
from .const import TO_REDACT from .const import TO_REDACT
_LOGGER = getLogger(__name__) _LOGGER = getLogger(__name__)
@ -13,7 +16,7 @@ _LOGGER = getLogger(__name__)
# utc_from_timestamp # utc_from_timestamp
# --------------------------- # ---------------------------
def utc_from_timestamp(timestamp: float) -> datetime: def utc_from_timestamp(timestamp: float) -> datetime:
"""Return a UTC time from a timestamp""" """Return a UTC time from a timestamp."""
return utc.localize(datetime.utcfromtimestamp(timestamp)) return utc.localize(datetime.utcfromtimestamp(timestamp))
@ -21,7 +24,7 @@ def utc_from_timestamp(timestamp: float) -> datetime:
# from_entry # from_entry
# --------------------------- # ---------------------------
def from_entry(entry, param, default="") -> str: def from_entry(entry, param, default="") -> str:
"""Validate and return str value an API dict""" """Validate and return str value an API dict."""
if "/" in param: if "/" in param:
for tmp_param in param.split("/"): for tmp_param in param.split("/"):
if isinstance(entry, dict) and tmp_param in entry: if isinstance(entry, dict) and tmp_param in entry:
@ -50,7 +53,7 @@ def from_entry(entry, param, default="") -> str:
# from_entry_bool # from_entry_bool
# --------------------------- # ---------------------------
def from_entry_bool(entry, param, default=False, reverse=False) -> bool: def from_entry_bool(entry, param, default=False, reverse=False) -> bool:
"""Validate and return a bool value from an API dict""" """Validate and return a bool value from an API dict."""
if "/" in param: if "/" in param:
for tmp_param in param.split("/"): for tmp_param in param.split("/"):
if isinstance(entry, dict) and tmp_param in entry: if isinstance(entry, dict) and tmp_param in entry:
@ -91,8 +94,11 @@ def parse_api(
only=None, only=None,
skip=None, skip=None,
) -> dict: ) -> dict:
"""Get data from API""" """Get data from API."""
debug = _LOGGER.getEffectiveLevel() == 10 debug = _LOGGER.getEffectiveLevel() == 10
if type(source) == dict:
tmp = source
source = [tmp]
if not source: if not source:
if not key and not key_search: if not key and not key_search:
@ -138,7 +144,7 @@ def parse_api(
# get_uid # get_uid
# --------------------------- # ---------------------------
def get_uid(entry, key, key_secondary, key_search, keymap) -> Optional(str): def get_uid(entry, key, key_secondary, key_search, keymap) -> Optional(str):
"""Get UID for data list""" """Get UID for data list."""
uid = None uid = None
if not key_search: if not key_search:
key_primary_found = key in entry key_primary_found = key in entry
@ -167,7 +173,7 @@ def get_uid(entry, key, key_secondary, key_search, keymap) -> Optional(str):
# generate_keymap # generate_keymap
# --------------------------- # ---------------------------
def generate_keymap(data, key_search) -> Optional(dict): def generate_keymap(data, key_search) -> Optional(dict):
"""Generate keymap""" """Generate keymap."""
return ( return (
{data[uid][key_search]: uid for uid in data if key_search in data[uid]} {data[uid][key_search]: uid for uid in data if key_search in data[uid]}
if key_search if key_search
@ -179,7 +185,7 @@ def generate_keymap(data, key_search) -> Optional(dict):
# matches_only # matches_only
# --------------------------- # ---------------------------
def matches_only(entry, only) -> bool: def matches_only(entry, only) -> bool:
"""Return True if all variables are matched""" """Return True if all variables are matched."""
ret = False ret = False
for val in only: for val in only:
if val["key"] in entry and entry[val["key"]] == val["value"]: if val["key"] in entry and entry[val["key"]] == val["value"]:
@ -195,7 +201,7 @@ def matches_only(entry, only) -> bool:
# can_skip # can_skip
# --------------------------- # ---------------------------
def can_skip(entry, skip) -> bool: def can_skip(entry, skip) -> bool:
"""Return True if at least one variable matches""" """Return True if at least one variable matches."""
ret = False ret = False
for val in skip: for val in skip:
if val["name"] in entry and entry[val["name"]] == val["value"]: if val["name"] in entry and entry[val["name"]] == val["value"]:
@ -213,7 +219,7 @@ def can_skip(entry, skip) -> bool:
# fill_defaults # fill_defaults
# --------------------------- # ---------------------------
def fill_defaults(data, vals) -> dict: def fill_defaults(data, vals) -> dict:
"""Fill defaults if source is not present""" """Fill defaults if source is not present."""
for val in vals: for val in vals:
_name = val["name"] _name = val["name"]
_type = val["type"] if "type" in val else "str" _type = val["type"] if "type" in val else "str"
@ -242,7 +248,7 @@ def fill_defaults(data, vals) -> dict:
# fill_vals # fill_vals
# --------------------------- # ---------------------------
def fill_vals(data, entry, uid, vals) -> dict: def fill_vals(data, entry, uid, vals) -> dict:
"""Fill all data""" """Fill all data."""
for val in vals: for val in vals:
_name = val["name"] _name = val["name"]
_type = val["type"] if "type" in val else "str" _type = val["type"] if "type" in val else "str"
@ -292,7 +298,7 @@ def fill_vals(data, entry, uid, vals) -> dict:
# fill_ensure_vals # fill_ensure_vals
# --------------------------- # ---------------------------
def fill_ensure_vals(data, uid, ensure_vals) -> dict: def fill_ensure_vals(data, uid, ensure_vals) -> dict:
"""Add required keys which are not available in data""" """Add required keys which are not available in data."""
for val in ensure_vals: for val in ensure_vals:
if uid: if uid:
if val["name"] not in data[uid]: if val["name"] not in data[uid]:
@ -310,7 +316,7 @@ def fill_ensure_vals(data, uid, ensure_vals) -> dict:
# fill_vals_proc # fill_vals_proc
# --------------------------- # ---------------------------
def fill_vals_proc(data, uid, vals_proc) -> dict: def fill_vals_proc(data, uid, vals_proc) -> dict:
"""Add custom keys""" """Add custom keys."""
_data = data[uid] if uid else data _data = data[uid] if uid else data
for val_sub in vals_proc: for val_sub in vals_proc:
_name = None _name = None

View file

@ -13,7 +13,7 @@ from .const import (
CONF_SENSOR_PORT_TRACKER, CONF_SENSOR_PORT_TRACKER,
DEFAULT_SENSOR_PORT_TRACKER, DEFAULT_SENSOR_PORT_TRACKER,
) )
from .model import model_async_setup_entry, MikrotikEntity from .entity import model_async_setup_entry, MikrotikEntity
from .binary_sensor_types import ( from .binary_sensor_types import (
SENSOR_TYPES, SENSOR_TYPES,
SENSOR_SERVICES, SENSOR_SERVICES,

View file

@ -2,7 +2,7 @@
import logging import logging
from homeassistant.components.button import ButtonEntity from homeassistant.components.button import ButtonEntity
from .model import model_async_setup_entry, MikrotikEntity from .entity import model_async_setup_entry, MikrotikEntity
from .button_types import ( from .button_types import (
SENSOR_TYPES, SENSOR_TYPES,
SENSOR_SERVICES, SENSOR_SERVICES,

View file

@ -12,7 +12,6 @@ from homeassistant.const import (
CONF_NAME, CONF_NAME,
CONF_HOST, CONF_HOST,
CONF_PORT, CONF_PORT,
CONF_UNIT_OF_MEASUREMENT,
CONF_USERNAME, CONF_USERNAME,
CONF_PASSWORD, CONF_PASSWORD,
CONF_SSL, CONF_SSL,
@ -55,8 +54,6 @@ from .const import (
DEFAULT_SENSOR_ENVIRONMENT, DEFAULT_SENSOR_ENVIRONMENT,
CONF_TRACK_HOSTS_TIMEOUT, CONF_TRACK_HOSTS_TIMEOUT,
DEFAULT_TRACK_HOST_TIMEOUT, DEFAULT_TRACK_HOST_TIMEOUT,
LIST_UNIT_OF_MEASUREMENT,
DEFAULT_UNIT_OF_MEASUREMENT,
DEFAULT_HOST, DEFAULT_HOST,
DEFAULT_USERNAME, DEFAULT_USERNAME,
DEFAULT_PORT, DEFAULT_PORT,
@ -193,12 +190,6 @@ class MikrotikControllerOptionsFlowHandler(OptionsFlow):
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL
), ),
): int, ): int,
vol.Optional(
CONF_UNIT_OF_MEASUREMENT,
default=self.config_entry.options.get(
CONF_UNIT_OF_MEASUREMENT, DEFAULT_UNIT_OF_MEASUREMENT
),
): vol.In(LIST_UNIT_OF_MEASUREMENT),
vol.Optional( vol.Optional(
CONF_TRACK_IFACE_CLIENTS, CONF_TRACK_IFACE_CLIENTS,
default=self.config_entry.options.get( default=self.config_entry.options.get(

View file

@ -3,11 +3,11 @@ from homeassistant.const import Platform
PLATFORMS = [ PLATFORMS = [
Platform.SENSOR, Platform.SENSOR,
Platform.BINARY_SENSOR, # Platform.BINARY_SENSOR,
Platform.DEVICE_TRACKER, # Platform.DEVICE_TRACKER,
Platform.SWITCH, # Platform.SWITCH,
Platform.BUTTON, # Platform.BUTTON,
Platform.UPDATE, # Platform.UPDATE,
] ]
DOMAIN = "mikrotik_router" DOMAIN = "mikrotik_router"
@ -27,8 +27,6 @@ DEFAULT_SSL = False
CONF_SCAN_INTERVAL = "scan_interval" CONF_SCAN_INTERVAL = "scan_interval"
DEFAULT_SCAN_INTERVAL = 30 DEFAULT_SCAN_INTERVAL = 30
LIST_UNIT_OF_MEASUREMENT = ["bps", "Kbps", "Mbps", "B/s", "KB/s", "MB/s"]
DEFAULT_UNIT_OF_MEASUREMENT = "Kbps"
CONF_TRACK_IFACE_CLIENTS = "track_iface_clients" CONF_TRACK_IFACE_CLIENTS = "track_iface_clients"
DEFAULT_TRACK_IFACE_CLIENTS = True DEFAULT_TRACK_IFACE_CLIENTS = True
CONF_TRACK_HOSTS = "track_network_hosts" CONF_TRACK_HOSTS = "track_network_hosts"

View file

@ -1,26 +1,31 @@
"""Mikrotik Controller for Mikrotik Router.""" """Mikrotik coordinator."""
from __future__ import annotations
import asyncio import asyncio
import ipaddress import ipaddress
import logging import logging
import re import re
import pytz import pytz
from typing import Any
from datetime import datetime, timedelta from datetime import datetime, timedelta
from ipaddress import ip_address, IPv4Network from ipaddress import ip_address, IPv4Network
from mac_vendor_lookup import AsyncMacLookup from mac_vendor_lookup import AsyncMacLookup
from homeassistant.core import callback from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers import entity_registry from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from homeassistant.const import ( from homeassistant.const import (
CONF_NAME, CONF_NAME,
CONF_HOST, CONF_HOST,
CONF_PORT, CONF_PORT,
CONF_UNIT_OF_MEASUREMENT,
CONF_USERNAME, CONF_USERNAME,
CONF_PASSWORD, CONF_PASSWORD,
CONF_SSL, CONF_SSL,
@ -36,7 +41,6 @@ from .const import (
DEFAULT_TRACK_HOSTS, DEFAULT_TRACK_HOSTS,
CONF_SCAN_INTERVAL, CONF_SCAN_INTERVAL,
DEFAULT_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL,
DEFAULT_UNIT_OF_MEASUREMENT,
CONF_SENSOR_PORT_TRAFFIC, CONF_SENSOR_PORT_TRAFFIC,
DEFAULT_SENSOR_PORT_TRAFFIC, DEFAULT_SENSOR_PORT_TRAFFIC,
CONF_SENSOR_CLIENT_TRAFFIC, CONF_SENSOR_CLIENT_TRAFFIC,
@ -95,13 +99,21 @@ def as_local(dattim: datetime) -> datetime:
# --------------------------- # ---------------------------
# MikrotikControllerData # MikrotikControllerData
# --------------------------- # ---------------------------
class MikrotikControllerData: class MikrotikCoordinator(DataUpdateCoordinator):
"""MikrotikController Class""" """MikrotikCoordinator Class"""
def __init__(self, hass, config_entry): def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry):
"""Initialize MikrotikController.""" """Initialize MikrotikCoordinator."""
self.hass = hass self.hass = hass
self.config_entry = config_entry self.config_entry: ConfigEntry = config_entry
super().__init__(
self.hass,
_LOGGER,
name=DOMAIN,
update_interval=timedelta(
seconds=10
), # update_interval=self.option_scan_interval
)
self.name = config_entry.data[CONF_NAME] self.name = config_entry.data[CONF_NAME]
self.host = config_entry.data[CONF_HOST] self.host = config_entry.data[CONF_HOST]
@ -143,7 +155,7 @@ class MikrotikControllerData:
self.notified_flags = [] self.notified_flags = []
self.listeners = [] # self.listeners = []
self.lock = asyncio.Lock() self.lock = asyncio.Lock()
self.lock_ping = asyncio.Lock() self.lock_ping = asyncio.Lock()
@ -185,22 +197,32 @@ class MikrotikControllerData:
self.async_mac_lookup = AsyncMacLookup() self.async_mac_lookup = AsyncMacLookup()
self.accessrights_reported = False self.accessrights_reported = False
async def async_init(self): # self.listeners.append(
self.listeners.append( # async_track_time_interval(
async_track_time_interval( # self.hass, self.force_fwupdate_check, timedelta(hours=4)
self.hass, self.force_update, self.option_scan_interval # )
) # )
) # self.listeners.append(
self.listeners.append( # async_track_time_interval(
async_track_time_interval( # self.hass, self.async_ping_tracked_hosts, timedelta(seconds=15)
self.hass, self.force_fwupdate_check, timedelta(hours=4) # )
) # )
)
self.listeners.append( # ---------------------------
async_track_time_interval( # async_init
self.hass, self.async_ping_tracked_hosts, timedelta(seconds=15) # ---------------------------
) # async def async_init(self):
) # print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
# self.listeners.append(
# async_track_time_interval(
# self.hass, self.force_fwupdate_check, timedelta(hours=4)
# )
# )
# self.listeners.append(
# async_track_time_interval(
# self.hass, self.async_ping_tracked_hosts, timedelta(seconds=15)
# )
# )
# --------------------------- # ---------------------------
# option_track_iface_clients # option_track_iface_clients
@ -333,16 +355,6 @@ class MikrotikControllerData:
) )
return timedelta(seconds=scan_interval) return timedelta(seconds=scan_interval)
# ---------------------------
# option_unit_of_measurement
# ---------------------------
@property
def option_unit_of_measurement(self):
"""Config entry option to not track ARP."""
return self.config_entry.options.get(
CONF_UNIT_OF_MEASUREMENT, DEFAULT_UNIT_OF_MEASUREMENT
)
# --------------------------- # ---------------------------
# option_zone # option_zone
# --------------------------- # ---------------------------
@ -351,25 +363,6 @@ class MikrotikControllerData:
"""Config entry option zones.""" """Config entry option zones."""
return self.config_entry.options.get(CONF_ZONE, STATE_HOME) return self.config_entry.options.get(CONF_ZONE, STATE_HOME)
# ---------------------------
# signal_update
# ---------------------------
@property
def signal_update(self):
"""Event to signal new data."""
return f"{DOMAIN}-update-{self.name}"
# ---------------------------
# async_reset
# ---------------------------
async def async_reset(self):
"""Reset dispatchers"""
for unsub_dispatcher in self.listeners:
unsub_dispatcher()
self.listeners = []
return True
# --------------------------- # ---------------------------
# connected # connected
# --------------------------- # ---------------------------
@ -490,8 +483,8 @@ class MikrotikControllerData:
"""Update Mikrotik hardware info""" """Update Mikrotik hardware info"""
try: try:
await asyncio.wait_for(self.lock.acquire(), timeout=30) await asyncio.wait_for(self.lock.acquire(), timeout=30)
except Exception: except Exception as error:
return raise UpdateFailed(error) from error
await self.hass.async_add_executor_job(self.get_access) await self.hass.async_add_executor_job(self.get_access)
@ -532,7 +525,6 @@ class MikrotikControllerData:
async def async_fwupdate_check(self): async def async_fwupdate_check(self):
"""Update Mikrotik data""" """Update Mikrotik data"""
await self.hass.async_add_executor_job(self.get_firmware_update) await self.hass.async_add_executor_job(self.get_firmware_update)
async_dispatcher_send(self.hass, self.signal_update)
# --------------------------- # ---------------------------
# async_ping_tracked_hosts # async_ping_tracked_hosts
@ -596,25 +588,20 @@ class MikrotikControllerData:
self.lock_ping.release() self.lock_ping.release()
# --------------------------- # ---------------------------
# force_update # _async_update_data
# --------------------------- # ---------------------------
@callback async def _async_update_data(self):
async def force_update(self, _now=None):
"""Trigger update by timer"""
await self.async_update()
# ---------------------------
# async_update
# ---------------------------
async def async_update(self):
"""Update Mikrotik data""" """Update Mikrotik data"""
print(
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
)
if self.api.has_reconnected(): if self.api.has_reconnected():
await self.async_hwinfo_update() await self.async_hwinfo_update()
try: try:
await asyncio.wait_for(self.lock.acquire(), timeout=10) await asyncio.wait_for(self.lock.acquire(), timeout=10)
except Exception: except Exception as error:
return raise UpdateFailed(error) from error
await self.hass.async_add_executor_job(self.get_system_resource) await self.hass.async_add_executor_job(self.get_system_resource)
@ -693,13 +680,14 @@ class MikrotikControllerData:
if self.api.connected() and self.support_gps: if self.api.connected() and self.support_gps:
await self.hass.async_add_executor_job(self.get_gps) await self.hass.async_add_executor_job(self.get_gps)
async_dispatcher_send(self.hass, self.signal_update)
self.lock.release() self.lock.release()
# async_dispatcher_send(self.hass, "update_sensors", self)
return self.data
# --------------------------- # ---------------------------
# get_access # get_access
# --------------------------- # ---------------------------
def get_access(self): def get_access(self) -> None:
"""Get access rights from Mikrotik""" """Get access rights from Mikrotik"""
tmp_user = parse_api( tmp_user = parse_api(
data={}, data={},
@ -742,7 +730,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_interface # get_interface
# --------------------------- # ---------------------------
def get_interface(self): def get_interface(self) -> None:
"""Get all interfaces data from Mikrotik""" """Get all interfaces data from Mikrotik"""
self.data["interface"] = parse_api( self.data["interface"] = parse_api(
data=self.data["interface"], data=self.data["interface"],
@ -794,30 +782,22 @@ class MikrotikControllerData:
) )
if self.option_sensor_port_traffic: if self.option_sensor_port_traffic:
uom_type, uom_div = self._get_unit_of_measurement()
for uid, vals in self.data["interface"].items(): for uid, vals in self.data["interface"].items():
self.data["interface"][uid]["rx-attr"] = uom_type
self.data["interface"][uid]["tx-attr"] = uom_type
current_tx = vals["tx-current"] current_tx = vals["tx-current"]
previous_tx = vals["tx-previous"] previous_tx = vals["tx-previous"] or current_tx
if not previous_tx:
previous_tx = current_tx
delta_tx = max(0, current_tx - previous_tx) * 8 delta_tx = max(0, current_tx - previous_tx)
self.data["interface"][uid]["tx"] = round( self.data["interface"][uid]["tx"] = round(
delta_tx / self.option_scan_interval.seconds * uom_div, 2 delta_tx / self.option_scan_interval.seconds
) )
self.data["interface"][uid]["tx-previous"] = current_tx self.data["interface"][uid]["tx-previous"] = current_tx
current_rx = vals["rx-current"] current_rx = vals["rx-current"]
previous_rx = vals["rx-previous"] previous_rx = vals["rx-previous"] or current_rx
if not previous_rx:
previous_rx = current_rx
delta_rx = max(0, current_rx - previous_rx) * 8 delta_rx = max(0, current_rx - previous_rx)
self.data["interface"][uid]["rx"] = round( self.data["interface"][uid]["rx"] = round(
delta_rx / self.option_scan_interval.seconds * uom_div, 2 delta_rx / self.option_scan_interval.seconds
) )
self.data["interface"][uid]["rx-previous"] = current_rx self.data["interface"][uid]["rx-previous"] = current_rx
@ -914,7 +894,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_bridge # get_bridge
# --------------------------- # ---------------------------
def get_bridge(self): def get_bridge(self) -> None:
"""Get system resources data from Mikrotik""" """Get system resources data from Mikrotik"""
self.data["bridge_host"] = parse_api( self.data["bridge_host"] = parse_api(
data=self.data["bridge_host"], data=self.data["bridge_host"],
@ -940,7 +920,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# process_interface_client # process_interface_client
# --------------------------- # ---------------------------
def process_interface_client(self): def process_interface_client(self) -> None:
# Remove data if disabled # Remove data if disabled
if not self.option_track_iface_clients: if not self.option_track_iface_clients:
for uid in self.data["interface"]: for uid in self.data["interface"]:
@ -978,7 +958,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_nat # get_nat
# --------------------------- # ---------------------------
def get_nat(self): def get_nat(self) -> None:
"""Get NAT data from Mikrotik""" """Get NAT data from Mikrotik"""
self.data["nat"] = parse_api( self.data["nat"] = parse_api(
data=self.data["nat"], data=self.data["nat"],
@ -1060,7 +1040,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_mangle # get_mangle
# --------------------------- # ---------------------------
def get_mangle(self): def get_mangle(self) -> None:
"""Get Mangle data from Mikrotik""" """Get Mangle data from Mikrotik"""
self.data["mangle"] = parse_api( self.data["mangle"] = parse_api(
data=self.data["mangle"], data=self.data["mangle"],
@ -1154,7 +1134,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_filter # get_filter
# --------------------------- # ---------------------------
def get_filter(self): def get_filter(self) -> None:
"""Get Filter data from Mikrotik""" """Get Filter data from Mikrotik"""
self.data["filter"] = parse_api( self.data["filter"] = parse_api(
data=self.data["filter"], data=self.data["filter"],
@ -1266,7 +1246,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_kidcontrol # get_kidcontrol
# --------------------------- # ---------------------------
def get_kidcontrol(self): def get_kidcontrol(self) -> None:
"""Get Kid-control data from Mikrotik""" """Get Kid-control data from Mikrotik"""
self.data["kid-control"] = parse_api( self.data["kid-control"] = parse_api(
data=self.data["kid-control"], data=self.data["kid-control"],
@ -1302,7 +1282,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_ppp # get_ppp
# --------------------------- # ---------------------------
def get_ppp(self): def get_ppp(self) -> None:
"""Get PPP data from Mikrotik""" """Get PPP data from Mikrotik"""
self.data["ppp_secret"] = parse_api( self.data["ppp_secret"] = parse_api(
data=self.data["ppp_secret"], data=self.data["ppp_secret"],
@ -1366,7 +1346,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_system_routerboard # get_system_routerboard
# --------------------------- # ---------------------------
def get_system_routerboard(self): def get_system_routerboard(self) -> None:
"""Get routerboard data from Mikrotik""" """Get routerboard data from Mikrotik"""
if self.data["resource"]["board-name"] in ("x86", "CHR"): if self.data["resource"]["board-name"] in ("x86", "CHR"):
self.data["routerboard"]["routerboard"] = False self.data["routerboard"]["routerboard"] = False
@ -1396,7 +1376,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_system_health # get_system_health
# --------------------------- # ---------------------------
def get_system_health(self): def get_system_health(self) -> None:
"""Get routerboard data from Mikrotik""" """Get routerboard data from Mikrotik"""
if ( if (
"write" not in self.data["access"] "write" not in self.data["access"]
@ -1434,7 +1414,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_system_resource # get_system_resource
# --------------------------- # ---------------------------
def get_system_resource(self): def get_system_resource(self) -> None:
"""Get system resources data from Mikrotik""" """Get system resources data from Mikrotik"""
tmp_rebootcheck = 0 tmp_rebootcheck = 0
if "uptime_epoch" in self.data["resource"]: if "uptime_epoch" in self.data["resource"]:
@ -1487,16 +1467,12 @@ class MikrotikControllerData:
if not self.data["resource"]["uptime"]: if not self.data["resource"]["uptime"]:
update_uptime = True update_uptime = True
else: else:
uptime_old = datetime.timestamp( uptime_old = datetime.timestamp(self.data["resource"]["uptime"])
datetime.fromisoformat(self.data["resource"]["uptime"])
)
if uptime_tm > uptime_old + 10: if uptime_tm > uptime_old + 10:
update_uptime = True update_uptime = True
if update_uptime: if update_uptime:
self.data["resource"]["uptime"] = str( self.data["resource"]["uptime"] = utc_from_timestamp(uptime_tm)
as_local(utc_from_timestamp(uptime_tm)).isoformat()
)
if self.data["resource"]["total-memory"] > 0: if self.data["resource"]["total-memory"] > 0:
self.data["resource"]["memory-usage"] = round( self.data["resource"]["memory-usage"] = round(
@ -1535,7 +1511,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_firmware_update # get_firmware_update
# --------------------------- # ---------------------------
def get_firmware_update(self): def get_firmware_update(self) -> None:
"""Check for firmware update on Mikrotik""" """Check for firmware update on Mikrotik"""
if ( if (
"write" not in self.data["access"] "write" not in self.data["access"]
@ -1579,7 +1555,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_ups # get_ups
# --------------------------- # ---------------------------
def get_ups(self): def get_ups(self) -> None:
"""Get UPS info from Mikrotik""" """Get UPS info from Mikrotik"""
self.data["ups"] = parse_api( self.data["ups"] = parse_api(
data=self.data["ups"], data=self.data["ups"],
@ -1632,7 +1608,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_gps # get_gps
# --------------------------- # ---------------------------
def get_gps(self): def get_gps(self) -> None:
"""Get GPS data from Mikrotik""" """Get GPS data from Mikrotik"""
self.data["gps"] = parse_api( self.data["gps"] = parse_api(
data=self.data["gps"], data=self.data["gps"],
@ -1659,7 +1635,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_script # get_script
# --------------------------- # ---------------------------
def get_script(self): def get_script(self) -> None:
"""Get list of all scripts from Mikrotik""" """Get list of all scripts from Mikrotik"""
self.data["script"] = parse_api( self.data["script"] = parse_api(
data=self.data["script"], data=self.data["script"],
@ -1675,7 +1651,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_environment # get_environment
# --------------------------- # ---------------------------
def get_environment(self): def get_environment(self) -> None:
"""Get list of all environment variables from Mikrotik""" """Get list of all environment variables from Mikrotik"""
self.data["environment"] = parse_api( self.data["environment"] = parse_api(
data=self.data["environment"], data=self.data["environment"],
@ -1690,7 +1666,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_captive # get_captive
# --------------------------- # ---------------------------
def get_captive(self): def get_captive(self) -> None:
"""Get list of all environment variables from Mikrotik""" """Get list of all environment variables from Mikrotik"""
self.data["hostspot_host"] = parse_api( self.data["hostspot_host"] = parse_api(
data={}, data={},
@ -1713,7 +1689,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_queue # get_queue
# --------------------------- # ---------------------------
def get_queue(self): def get_queue(self) -> None:
"""Get Queue data from Mikrotik""" """Get Queue data from Mikrotik"""
self.data["queue"] = parse_api( self.data["queue"] = parse_api(
data=self.data["queue"], data=self.data["queue"],
@ -1741,59 +1717,50 @@ class MikrotikControllerData:
], ],
) )
uom_type, uom_div = self._get_unit_of_measurement()
for uid, vals in self.data["queue"].items(): for uid, vals in self.data["queue"].items():
self.data["queue"][uid]["comment"] = str(self.data["queue"][uid]["comment"]) self.data["queue"][uid]["comment"] = str(self.data["queue"][uid]["comment"])
upload_max_limit_bps, download_max_limit_bps = [ upload_max_limit_bps, download_max_limit_bps = [
int(x) for x in vals["max-limit"].split("/") int(x) for x in vals["max-limit"].split("/")
] ]
self.data["queue"][uid][ self.data["queue"][uid]["upload-max-limit"] = f"{upload_max_limit_bps} bps"
"upload-max-limit"
] = f"{round(upload_max_limit_bps * uom_div)} {uom_type}"
self.data["queue"][uid][ self.data["queue"][uid][
"download-max-limit" "download-max-limit"
] = f"{round(download_max_limit_bps * uom_div)} {uom_type}" ] = f"{download_max_limit_bps} bps"
upload_rate_bps, download_rate_bps = [ upload_rate_bps, download_rate_bps = [
int(x) for x in vals["rate"].split("/") int(x) for x in vals["rate"].split("/")
] ]
self.data["queue"][uid][ self.data["queue"][uid]["upload-rate"] = f"{upload_rate_bps} bps"
"upload-rate" self.data["queue"][uid]["download-rate"] = f"{download_rate_bps} bps"
] = f"{round(upload_rate_bps * uom_div)} {uom_type}"
self.data["queue"][uid][
"download-rate"
] = f"{round(download_rate_bps * uom_div)} {uom_type}"
upload_limit_at_bps, download_limit_at_bps = [ upload_limit_at_bps, download_limit_at_bps = [
int(x) for x in vals["limit-at"].split("/") int(x) for x in vals["limit-at"].split("/")
] ]
self.data["queue"][uid][ self.data["queue"][uid]["upload-limit-at"] = f"{upload_limit_at_bps} bps"
"upload-limit-at"
] = f"{round(upload_limit_at_bps * uom_div)} {uom_type}"
self.data["queue"][uid][ self.data["queue"][uid][
"download-limit-at" "download-limit-at"
] = f"{round(download_limit_at_bps * uom_div)} {uom_type}" ] = f"{download_limit_at_bps} bps"
upload_burst_limit_bps, download_burst_limit_bps = [ upload_burst_limit_bps, download_burst_limit_bps = [
int(x) for x in vals["burst-limit"].split("/") int(x) for x in vals["burst-limit"].split("/")
] ]
self.data["queue"][uid][ self.data["queue"][uid][
"upload-burst-limit" "upload-burst-limit"
] = f"{round(upload_burst_limit_bps * uom_div)} {uom_type}" ] = f"{upload_burst_limit_bps} bps"
self.data["queue"][uid][ self.data["queue"][uid][
"download-burst-limit" "download-burst-limit"
] = f"{round(download_burst_limit_bps * uom_div)} {uom_type}" ] = f"{download_burst_limit_bps} bps"
upload_burst_threshold_bps, download_burst_threshold_bps = [ upload_burst_threshold_bps, download_burst_threshold_bps = [
int(x) for x in vals["burst-threshold"].split("/") int(x) for x in vals["burst-threshold"].split("/")
] ]
self.data["queue"][uid][ self.data["queue"][uid][
"upload-burst-threshold" "upload-burst-threshold"
] = f"{round(upload_burst_threshold_bps * uom_div)} {uom_type}" ] = f"{upload_burst_threshold_bps} bps"
self.data["queue"][uid][ self.data["queue"][uid][
"download-burst-threshold" "download-burst-threshold"
] = f"{round(download_burst_threshold_bps * uom_div)} {uom_type}" ] = f"{download_burst_threshold_bps} bps"
upload_burst_time, download_burst_time = vals["burst-time"].split("/") upload_burst_time, download_burst_time = vals["burst-time"].split("/")
self.data["queue"][uid]["upload-burst-time"] = upload_burst_time self.data["queue"][uid]["upload-burst-time"] = upload_burst_time
@ -1802,7 +1769,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_arp # get_arp
# --------------------------- # ---------------------------
def get_arp(self): def get_arp(self) -> None:
"""Get ARP data from Mikrotik""" """Get ARP data from Mikrotik"""
self.data["arp"] = parse_api( self.data["arp"] = parse_api(
data=self.data["arp"], data=self.data["arp"],
@ -1835,7 +1802,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_dns # get_dns
# --------------------------- # ---------------------------
def get_dns(self): def get_dns(self) -> None:
"""Get static DNS data from Mikrotik""" """Get static DNS data from Mikrotik"""
self.data["dns"] = parse_api( self.data["dns"] = parse_api(
data=self.data["dns"], data=self.data["dns"],
@ -1850,7 +1817,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_dhcp # get_dhcp
# --------------------------- # ---------------------------
def get_dhcp(self): def get_dhcp(self) -> None:
"""Get DHCP data from Mikrotik""" """Get DHCP data from Mikrotik"""
self.data["dhcp"] = parse_api( self.data["dhcp"] = parse_api(
data=self.data["dhcp"], data=self.data["dhcp"],
@ -1926,7 +1893,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_dhcp_server # get_dhcp_server
# --------------------------- # ---------------------------
def get_dhcp_server(self): def get_dhcp_server(self) -> None:
"""Get DHCP server data from Mikrotik""" """Get DHCP server data from Mikrotik"""
self.data["dhcp-server"] = parse_api( self.data["dhcp-server"] = parse_api(
data=self.data["dhcp-server"], data=self.data["dhcp-server"],
@ -1941,7 +1908,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_dhcp_client # get_dhcp_client
# --------------------------- # ---------------------------
def get_dhcp_client(self): def get_dhcp_client(self) -> None:
"""Get DHCP client data from Mikrotik""" """Get DHCP client data from Mikrotik"""
self.data["dhcp-client"] = parse_api( self.data["dhcp-client"] = parse_api(
data=self.data["dhcp-client"], data=self.data["dhcp-client"],
@ -1956,7 +1923,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_dhcp_network # get_dhcp_network
# --------------------------- # ---------------------------
def get_dhcp_network(self): def get_dhcp_network(self) -> None:
"""Get DHCP network data from Mikrotik""" """Get DHCP network data from Mikrotik"""
self.data["dhcp-network"] = parse_api( self.data["dhcp-network"] = parse_api(
data=self.data["dhcp-network"], data=self.data["dhcp-network"],
@ -1981,7 +1948,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_capsman_hosts # get_capsman_hosts
# --------------------------- # ---------------------------
def get_capsman_hosts(self): def get_capsman_hosts(self) -> None:
"""Get CAPS-MAN hosts data from Mikrotik""" """Get CAPS-MAN hosts data from Mikrotik"""
self.data["capsman_hosts"] = parse_api( self.data["capsman_hosts"] = parse_api(
data={}, data={},
@ -1997,7 +1964,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_wireless # get_wireless
# --------------------------- # ---------------------------
def get_wireless(self): def get_wireless(self) -> None:
"""Get wireless data from Mikrotik""" """Get wireless data from Mikrotik"""
wifimodule = "wifiwave2" if self.support_wifiwave2 else "wireless" wifimodule = "wifiwave2" if self.support_wifiwave2 else "wireless"
self.data["wireless"] = parse_api( self.data["wireless"] = parse_api(
@ -2047,7 +2014,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# get_wireless_hosts # get_wireless_hosts
# --------------------------- # ---------------------------
def get_wireless_hosts(self): def get_wireless_hosts(self) -> None:
"""Get wireless hosts data from Mikrotik""" """Get wireless hosts data from Mikrotik"""
wifimodule = "wifiwave2" if self.support_wifiwave2 else "wireless" wifimodule = "wifiwave2" if self.support_wifiwave2 else "wireless"
self.data["wireless_hosts"] = parse_api( self.data["wireless_hosts"] = parse_api(
@ -2065,7 +2032,7 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# async_process_host # async_process_host
# --------------------------- # ---------------------------
async def async_process_host(self): async def async_process_host(self) -> None:
"""Get host tracking data""" """Get host tracking data"""
# Add hosts from CAPS-MAN # Add hosts from CAPS-MAN
capsman_detected = {} capsman_detected = {}
@ -2281,14 +2248,13 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# process_accounting # process_accounting
# --------------------------- # ---------------------------
def process_accounting(self): def process_accounting(self) -> None:
"""Get Accounting data from Mikrotik""" """Get Accounting data from Mikrotik"""
# Check if accounting and account-local-traffic is enabled # Check if accounting and account-local-traffic is enabled
( (
accounting_enabled, accounting_enabled,
local_traffic_enabled, local_traffic_enabled,
) = self.api.is_accounting_and_local_traffic_enabled() ) = self.api.is_accounting_and_local_traffic_enabled()
uom_type, uom_div = self._get_unit_of_measurement()
# Build missing hosts from main hosts dict # Build missing hosts from main hosts dict
for uid, vals in self.data["host"].items(): for uid, vals in self.data["host"].items():
@ -2297,7 +2263,6 @@ class MikrotikControllerData:
"address": vals["address"], "address": vals["address"],
"mac-address": vals["mac-address"], "mac-address": vals["mac-address"],
"host-name": vals["host-name"], "host-name": vals["host-name"],
"tx-rx-attr": uom_type,
"available": False, "available": False,
"local_accounting": False, "local_accounting": False,
} }
@ -2351,7 +2316,7 @@ class MikrotikControllerData:
for item in accounting_data.values(): for item in accounting_data.values():
source_ip = str(item.get("src-address")).strip() source_ip = str(item.get("src-address")).strip()
destination_ip = str(item.get("dst-address")).strip() destination_ip = str(item.get("dst-address")).strip()
bits_count = int(str(item.get("bytes")).strip()) * 8 bits_count = int(str(item.get("bytes")).strip())
if self._address_part_of_local_network( if self._address_part_of_local_network(
source_ip source_ip
@ -2385,7 +2350,6 @@ class MikrotikControllerData:
) )
continue continue
self.data["client_traffic"][uid]["tx-rx-attr"] = uom_type
self.data["client_traffic"][uid]["available"] = accounting_enabled self.data["client_traffic"][uid]["available"] = accounting_enabled
self.data["client_traffic"][uid]["local_accounting"] = local_traffic_enabled self.data["client_traffic"][uid]["local_accounting"] = local_traffic_enabled
@ -2394,14 +2358,10 @@ class MikrotikControllerData:
continue continue
self.data["client_traffic"][uid]["wan-tx"] = ( self.data["client_traffic"][uid]["wan-tx"] = (
round(vals["wan-tx"] / time_diff * uom_div, 2) round(vals["wan-tx"] / time_diff) if vals["wan-tx"] else 0.0
if vals["wan-tx"]
else 0.0
) )
self.data["client_traffic"][uid]["wan-rx"] = ( self.data["client_traffic"][uid]["wan-rx"] = (
round(vals["wan-rx"] / time_diff * uom_div, 2) round(vals["wan-rx"] / time_diff) if vals["wan-rx"] else 0.0
if vals["wan-rx"]
else 0.0
) )
if not local_traffic_enabled: if not local_traffic_enabled:
@ -2409,40 +2369,16 @@ class MikrotikControllerData:
continue continue
self.data["client_traffic"][uid]["lan-tx"] = ( self.data["client_traffic"][uid]["lan-tx"] = (
round(vals["lan-tx"] / time_diff * uom_div, 2) round(vals["lan-tx"] / time_diff) if vals["lan-tx"] else 0.0
if vals["lan-tx"]
else 0.0
) )
self.data["client_traffic"][uid]["lan-rx"] = ( self.data["client_traffic"][uid]["lan-rx"] = (
round(vals["lan-rx"] / time_diff * uom_div, 2) round(vals["lan-rx"] / time_diff) if vals["lan-rx"] else 0.0
if vals["lan-rx"]
else 0.0
) )
# ---------------------------
# _get_unit_of_measurement
# ---------------------------
def _get_unit_of_measurement(self):
uom_type = self.option_unit_of_measurement
if uom_type == "Kbps":
uom_div = 0.001
elif uom_type == "Mbps":
uom_div = 0.000001
elif uom_type == "B/s":
uom_div = 0.125
elif uom_type == "KB/s":
uom_div = 0.000125
elif uom_type == "MB/s":
uom_div = 0.000000125
else:
uom_type = "bps"
uom_div = 1
return uom_type, uom_div
# --------------------------- # ---------------------------
# _address_part_of_local_network # _address_part_of_local_network
# --------------------------- # ---------------------------
def _address_part_of_local_network(self, address): def _address_part_of_local_network(self, address) -> bool:
address = ip_address(address) address = ip_address(address)
for vals in self.data["dhcp-network"].values(): for vals in self.data["dhcp-network"].values():
if address in vals["IPv4Network"]: if address in vals["IPv4Network"]:
@ -2474,11 +2410,9 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
# process_kid_control # process_kid_control
# --------------------------- # ---------------------------
def process_kid_control_devices(self): def process_kid_control_devices(self) -> None:
"""Get Kid Control Device data from Mikrotik""" """Get Kid Control Device data from Mikrotik"""
uom_type, uom_div = self._get_unit_of_measurement()
# Build missing hosts from main hosts dict # Build missing hosts from main hosts dict
for uid, vals in self.data["host"].items(): for uid, vals in self.data["host"].items():
if uid not in self.data["client_traffic"]: if uid not in self.data["client_traffic"]:
@ -2490,7 +2424,6 @@ class MikrotikControllerData:
"previous-bytes-down": 0.0, "previous-bytes-down": 0.0,
"tx": 0.0, "tx": 0.0,
"rx": 0.0, "rx": 0.0,
"tx-rx-attr": uom_type,
"available": False, "available": False,
"local_accounting": False, "local_accounting": False,
} }
@ -2538,17 +2471,13 @@ class MikrotikControllerData:
current_tx = vals["bytes-up"] current_tx = vals["bytes-up"]
previous_tx = self.data["client_traffic"][uid]["previous-bytes-up"] previous_tx = self.data["client_traffic"][uid]["previous-bytes-up"]
if time_diff: if time_diff:
delta_tx = max(0, current_tx - previous_tx) * 8 delta_tx = max(0, current_tx - previous_tx)
self.data["client_traffic"][uid]["tx"] = round( self.data["client_traffic"][uid]["tx"] = round(delta_tx / time_diff)
delta_tx / time_diff * uom_div, 2
)
self.data["client_traffic"][uid]["previous-bytes-up"] = current_tx self.data["client_traffic"][uid]["previous-bytes-up"] = current_tx
current_rx = vals["bytes-down"] current_rx = vals["bytes-down"]
previous_rx = self.data["client_traffic"][uid]["previous-bytes-down"] previous_rx = self.data["client_traffic"][uid]["previous-bytes-down"]
if time_diff: if time_diff:
delta_rx = max(0, current_rx - previous_rx) * 8 delta_rx = max(0, current_rx - previous_rx)
self.data["client_traffic"][uid]["rx"] = round( self.data["client_traffic"][uid]["rx"] = round(delta_rx / time_diff)
delta_rx / time_diff * uom_div, 2
)
self.data["client_traffic"][uid]["previous-bytes-down"] = current_rx self.data["client_traffic"][uid]["previous-bytes-down"] = current_rx

View file

@ -14,7 +14,7 @@ from .const import (
CONF_TRACK_HOSTS_TIMEOUT, CONF_TRACK_HOSTS_TIMEOUT,
DEFAULT_TRACK_HOST_TIMEOUT, DEFAULT_TRACK_HOST_TIMEOUT,
) )
from .model import model_async_setup_entry, MikrotikEntity from .entity import model_async_setup_entry, MikrotikEntity
from .device_tracker_types import SENSOR_TYPES, SENSOR_SERVICES from .device_tracker_types import SENSOR_TYPES, SENSOR_SERVICES
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View file

@ -1,13 +1,22 @@
"""Mikrotik HA shared entity model""" """Mikrotik HA shared entity model"""
from logging import getLogger from __future__ import annotations
from typing import Any
from collections.abc import Mapping from collections.abc import Mapping
from homeassistant.helpers import entity_platform from logging import getLogger
from homeassistant.helpers.entity import DeviceInfo from typing import Any, Callable
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.core import callback from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_ATTRIBUTION, CONF_NAME, CONF_HOST from homeassistant.const import ATTR_ATTRIBUTION, CONF_NAME, CONF_HOST
from .helper import format_attribute from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import (
entity_platform as ep,
entity_registry as er,
)
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import slugify
from .const import ( from .const import (
DOMAIN, DOMAIN,
ATTRIBUTION, ATTRIBUTION,
@ -18,14 +27,16 @@ from .const import (
CONF_SENSOR_PORT_TRACKER, CONF_SENSOR_PORT_TRACKER,
DEFAULT_SENSOR_PORT_TRACKER, DEFAULT_SENSOR_PORT_TRACKER,
) )
from .coordinator import MikrotikCoordinator
from .helper import format_attribute
_LOGGER = getLogger(__name__) _LOGGER = getLogger(__name__)
def _skip_sensor(config_entry, uid_sensor, uid_data, uid) -> bool: def _skip_sensor(config_entry, entity_description, data, uid) -> bool:
# Sensors # Sensors
if ( if (
uid_sensor.func == "MikrotikInterfaceTrafficSensor" entity_description.func == "MikrotikInterfaceTrafficSensor"
and not config_entry.options.get( and not config_entry.options.get(
CONF_SENSOR_PORT_TRAFFIC, DEFAULT_SENSOR_PORT_TRAFFIC CONF_SENSOR_PORT_TRAFFIC, DEFAULT_SENSOR_PORT_TRAFFIC
) )
@ -33,33 +44,36 @@ def _skip_sensor(config_entry, uid_sensor, uid_data, uid) -> bool:
return True return True
if ( if (
uid_sensor.func == "MikrotikInterfaceTrafficSensor" entity_description.func == "MikrotikInterfaceTrafficSensor"
and uid_data[uid]["type"] == "bridge" and data[uid]["type"] == "bridge"
): ):
return True return True
if ( if (
uid_sensor.func == "MikrotikClientTrafficSensor" entity_description.func == "MikrotikClientTrafficSensor"
and uid_sensor.data_attribute not in uid_data[uid].keys() and entity_description.data_attribute not in data[uid].keys()
): ):
return True return True
# Binary sensors # Binary sensors
if ( if (
uid_sensor.func == "MikrotikPortBinarySensor" entity_description.func == "MikrotikPortBinarySensor"
and uid_data[uid]["type"] == "wlan" and data[uid]["type"] == "wlan"
): ):
return True return True
if uid_sensor.func == "MikrotikPortBinarySensor" and not config_entry.options.get( if (
CONF_SENSOR_PORT_TRACKER, DEFAULT_SENSOR_PORT_TRACKER entity_description.func == "MikrotikPortBinarySensor"
and not config_entry.options.get(
CONF_SENSOR_PORT_TRACKER, DEFAULT_SENSOR_PORT_TRACKER
)
): ):
return True return True
# Device Tracker # Device Tracker
if ( if (
# Skip if host tracking is disabled # Skip if host tracking is disabled
uid_sensor.func == "MikrotikHostDeviceTracker" entity_description.func == "MikrotikHostDeviceTracker"
and not config_entry.options.get(CONF_TRACK_HOSTS, DEFAULT_TRACK_HOSTS) and not config_entry.options.get(CONF_TRACK_HOSTS, DEFAULT_TRACK_HOSTS)
): ):
return True return True
@ -68,125 +82,99 @@ def _skip_sensor(config_entry, uid_sensor, uid_data, uid) -> bool:
# --------------------------- # ---------------------------
# model_async_setup_entry # async_add_entities
# --------------------------- # ---------------------------
async def model_async_setup_entry( async def async_add_entities(
hass, config_entry, async_add_entities, sensor_services, sensor_types, dispatcher hass: HomeAssistant, config_entry: ConfigEntry, dispatcher: dict[str, Callable]
): ):
inst = config_entry.data[CONF_NAME] """Add entities."""
mikrotik_controller = hass.data[DOMAIN][config_entry.entry_id] coordinator = hass.data[DOMAIN][config_entry.entry_id]
sensors = {} platform = ep.async_get_current_platform()
services = platform.platform.SENSOR_SERVICES
descriptions = platform.platform.SENSOR_TYPES
platform = entity_platform.async_get_current_platform() for service in services:
for service in sensor_services:
platform.async_register_entity_service(service[0], service[1], service[2]) platform.async_register_entity_service(service[0], service[1], service[2])
@callback @callback
def update_controller(): async def async_update_controller(coordinator):
"""Update the values of the controller""" """Update the values of the controller."""
model_update_items(
inst,
config_entry,
mikrotik_controller,
async_add_entities,
sensors,
dispatcher,
sensor_types,
)
mikrotik_controller.listeners.append( async def async_check_exist(obj, coordinator, uid: None) -> None:
async_dispatcher_connect( """Check entity exists."""
hass, mikrotik_controller.signal_update, update_controller entity_registry = er.async_get(hass)
) if uid:
) unique_id = f"{obj._inst.lower()}-{obj.entity_description.key}-{slugify(str(obj._data[obj.entity_description.data_reference]).lower())}"
update_controller() else:
unique_id = f"{obj._inst.lower()}-{obj.entity_description.key}"
entity_id = entity_registry.async_get_entity_id(
# --------------------------- platform.domain, DOMAIN, unique_id
# model_update_items )
# --------------------------- entity = entity_registry.async_get(entity_id)
def model_update_items( if entity is None or (
inst, (entity_id not in platform.entities) and (entity.disabled is False)
config_entry,
mikrotik_controller,
async_add_entities,
sensors,
dispatcher,
sensor_types,
):
def _register_entity(_sensors, _item_id, _uid, _uid_sensor):
_LOGGER.debug("Updating entity %s (%s)", inst, _item_id)
if _item_id in _sensors:
return None
return dispatcher[_uid_sensor.func](
inst=inst,
uid=_uid,
mikrotik_controller=mikrotik_controller,
entity_description=_uid_sensor,
)
new_sensors = []
for sensor in sensor_types:
uid_sensor = sensor_types[sensor]
if not uid_sensor.data_reference:
if (
uid_sensor.data_attribute
not in mikrotik_controller.data[uid_sensor.data_path]
or mikrotik_controller.data[uid_sensor.data_path][
uid_sensor.data_attribute
]
== "unknown"
): ):
continue _LOGGER.debug("Add entity %s", entity_id)
await platform.async_add_entities([obj])
item_id = f"{inst}-{sensor}" for entity_description in descriptions:
if tmp := _register_entity(sensors, item_id, "", uid_sensor): data = coordinator.data[entity_description.data_path]
sensors[item_id] = tmp if not entity_description.data_reference:
new_sensors.append(sensors[item_id]) if data.get(entity_description.data_attribute) is None:
else:
for uid in mikrotik_controller.data[uid_sensor.data_path]:
uid_data = mikrotik_controller.data[uid_sensor.data_path]
if _skip_sensor(config_entry, uid_sensor, uid_data, uid):
continue continue
obj = dispatcher[entity_description.func](
coordinator, entity_description
)
await async_check_exist(obj, coordinator, None)
else:
for uid in data:
if _skip_sensor(config_entry, entity_description, data, uid):
continue
obj = dispatcher[entity_description.func](
coordinator, entity_description, uid
)
await async_check_exist(obj, coordinator, uid)
item_id = f"{inst}-{sensor}-{str(uid_data[uid][uid_sensor.data_reference]).lower()}" await async_update_controller(coordinator)
if tmp := _register_entity(sensors, item_id, uid, uid_sensor): unsub = async_dispatcher_connect(hass, "update_sensors", async_update_controller)
sensors[item_id] = tmp config_entry.async_on_unload(unsub)
new_sensors.append(sensors[item_id])
if new_sensors:
async_add_entities(new_sensors, True)
# --------------------------- # ---------------------------
# MikrotikEntity # MikrotikEntity
# --------------------------- # ---------------------------
class MikrotikEntity: class MikrotikEntity(CoordinatorEntity[MikrotikCoordinator], Entity):
"""Define entity""" """Define entity"""
_attr_has_entity_name = True _attr_has_entity_name = True
def __init__( def __init__(
self, self,
inst, coordinator: MikrotikCoordinator,
uid: "",
mikrotik_controller,
entity_description, entity_description,
uid: str | None = None,
): ):
"""Initialize entity""" """Initialize entity"""
super().__init__(coordinator)
self.coordinator = coordinator
self.entity_description = entity_description self.entity_description = entity_description
self._inst = inst self._inst = coordinator.config_entry.data[CONF_NAME]
self._ctrl = mikrotik_controller self._config_entry = self.coordinator.config_entry
self._config_entry = self._ctrl.config_entry
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION} self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
self._uid = uid self._uid = uid
self._data = coordinator.data[self.entity_description.data_path]
if self._uid: if self._uid:
self._data = mikrotik_controller.data[self.entity_description.data_path][ self._data = coordinator.data[self.entity_description.data_path][self._uid]
@callback
def _handle_coordinator_update(self) -> None:
self._data = self.coordinator.data[self.entity_description.data_path]
if self._uid:
self._data = self.coordinator.data[self.entity_description.data_path][
self._uid self._uid
] ]
else: super()._handle_coordinator_update()
self._data = mikrotik_controller.data[self.entity_description.data_path]
@property @property
def name(self) -> str: def name(self) -> str:
@ -215,14 +203,14 @@ class MikrotikEntity:
def unique_id(self) -> str: def unique_id(self) -> str:
"""Return a unique id for this entity""" """Return a unique id for this entity"""
if self._uid: if self._uid:
return f"{self._inst.lower()}-{self.entity_description.key}-{str(self._data[self.entity_description.data_reference]).lower()}" return f"{self._inst.lower()}-{self.entity_description.key}-{slugify(str(self._data[self.entity_description.data_reference]).lower())}"
else: else:
return f"{self._inst.lower()}-{self.entity_description.key}" return f"{self._inst.lower()}-{self.entity_description.key}"
@property @property
def available(self) -> bool: def available(self) -> bool:
"""Return if controller is available""" """Return if controller is available"""
return self._ctrl.connected() return self.coordinator.connected()
@property @property
def device_info(self) -> DeviceInfo: def device_info(self) -> DeviceInfo:
@ -231,8 +219,8 @@ class MikrotikEntity:
dev_connection_value = self.entity_description.data_reference dev_connection_value = self.entity_description.data_reference
dev_group = self.entity_description.ha_group dev_group = self.entity_description.ha_group
if self.entity_description.ha_group == "System": if self.entity_description.ha_group == "System":
dev_group = self._ctrl.data["resource"]["board-name"] dev_group = self.coordinator.data["resource"]["board-name"]
dev_connection_value = self._ctrl.data["routerboard"]["serial-number"] dev_connection_value = self.coordinator.data["routerboard"]["serial-number"]
if self.entity_description.ha_group.startswith("data__"): if self.entity_description.ha_group.startswith("data__"):
dev_group = self.entity_description.ha_group[6:] dev_group = self.entity_description.ha_group[6:]
@ -253,19 +241,24 @@ class MikrotikEntity:
connections={(dev_connection, f"{dev_connection_value}")}, connections={(dev_connection, f"{dev_connection_value}")},
identifiers={(dev_connection, f"{dev_connection_value}")}, identifiers={(dev_connection, f"{dev_connection_value}")},
default_name=f"{self._inst} {dev_group}", default_name=f"{self._inst} {dev_group}",
default_model=f"{self._ctrl.data['resource']['board-name']}", default_model=f"{self.coordinator.data['resource']['board-name']}",
default_manufacturer=f"{self._ctrl.data['resource']['platform']}", default_manufacturer=f"{self.coordinator.data['resource']['platform']}",
sw_version=f"{self._ctrl.data['resource']['version']}", sw_version=f"{self.coordinator.data['resource']['version']}",
configuration_url=f"http://{self._ctrl.config_entry.data[CONF_HOST]}", configuration_url=f"http://{self.coordinator.config_entry.data[CONF_HOST]}",
via_device=(DOMAIN, f"{self._ctrl.data['routerboard']['serial-number']}"), via_device=(
DOMAIN,
f"{self.coordinator.data['routerboard']['serial-number']}",
),
) )
if "mac-address" in self.entity_description.data_reference: if "mac-address" in self.entity_description.data_reference:
dev_group = self._data[self.entity_description.data_name] dev_group = self._data[self.entity_description.data_name]
dev_manufacturer = "" dev_manufacturer = ""
if dev_connection_value in self._ctrl.data["host"]: if dev_connection_value in self.coordinator.data["host"]:
dev_group = self._ctrl.data["host"][dev_connection_value]["host-name"] dev_group = self.coordinator.data["host"][dev_connection_value][
dev_manufacturer = self._ctrl.data["host"][dev_connection_value][ "host-name"
]
dev_manufacturer = self.coordinator.data["host"][dev_connection_value][
"manufacturer" "manufacturer"
] ]
@ -275,7 +268,7 @@ class MikrotikEntity:
default_manufacturer=f"{dev_manufacturer}", default_manufacturer=f"{dev_manufacturer}",
via_device=( via_device=(
DOMAIN, DOMAIN,
f"{self._ctrl.data['routerboard']['serial-number']}", f"{self.coordinator.data['routerboard']['serial-number']}",
), ),
) )
@ -301,16 +294,16 @@ class MikrotikEntity:
async def start(self): async def start(self):
"""Dummy run function""" """Dummy run function"""
_LOGGER.error("Start functionality does not exist for %s", self.unique_id) raise NotImplementedError()
async def stop(self): async def stop(self):
"""Dummy stop function""" """Dummy stop function"""
_LOGGER.error("Stop functionality does not exist for %s", self.unique_id) raise NotImplementedError()
async def restart(self): async def restart(self):
"""Dummy restart function""" """Dummy restart function"""
_LOGGER.error("Restart functionality does not exist for %s", self.unique_id) raise NotImplementedError()
async def reload(self): async def reload(self):
"""Dummy reload function""" """Dummy reload function"""
_LOGGER.error("Reload functionality does not exist for %s", self.unique_id) raise NotImplementedError()

View file

@ -44,7 +44,7 @@ class MikrotikAPI:
self._connection = None self._connection = None
self._connected = False self._connected = False
self._reconnected = False self._reconnected = True
self._connection_epoch = 0 self._connection_epoch = 0
self._connection_retry_sec = 58 self._connection_retry_sec = 58
self.error = None self.error = None

View file

@ -1,11 +1,21 @@
"""Implementation of Mikrotik Router sensor entities.""" """Mikrotik sensor platform."""
from __future__ import annotations
import logging from logging import getLogger
from typing import Any, Optional
from collections.abc import Mapping from collections.abc import Mapping
from datetime import date, datetime
from decimal import Decimal
from typing import Any
from homeassistant.components.sensor import SensorEntity from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .entity import MikrotikEntity, async_add_entities
from .coordinator import MikrotikCoordinator
from .helper import format_attribute from .helper import format_attribute
from .model import model_async_setup_entry, MikrotikEntity
from .sensor_types import ( from .sensor_types import (
SENSOR_TYPES, SENSOR_TYPES,
SENSOR_SERVICES, SENSOR_SERVICES,
@ -14,52 +24,56 @@ from .sensor_types import (
DEVICE_ATTRIBUTES_IFACE_WIRELESS, DEVICE_ATTRIBUTES_IFACE_WIRELESS,
) )
_LOGGER = logging.getLogger(__name__) _LOGGER = getLogger(__name__)
# --------------------------- # ---------------------------
# async_setup_entry # async_setup_entry
# --------------------------- # ---------------------------
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
_async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up entry for component""" """Set up entry for component"""
dispatcher = { dispatcher = {
"MikrotikSensor": MikrotikSensor, "MikrotikSensor": MikrotikSensor,
"MikrotikInterfaceTrafficSensor": MikrotikInterfaceTrafficSensor, "MikrotikInterfaceTrafficSensor": MikrotikInterfaceTrafficSensor,
"MikrotikClientTrafficSensor": MikrotikClientTrafficSensor, "MikrotikClientTrafficSensor": MikrotikClientTrafficSensor,
} }
await model_async_setup_entry( await async_add_entities(hass, config_entry, dispatcher)
hass,
config_entry,
async_add_entities,
SENSOR_SERVICES,
SENSOR_TYPES,
dispatcher,
)
# --------------------------- # ---------------------------
# MikrotikSensor # MikrotikSensor
# --------------------------- # ---------------------------
class MikrotikSensor(MikrotikEntity, SensorEntity): class MikrotikSensor(MikrotikEntity, SensorEntity):
"""Define an Mikrotik Controller sensor.""" """Define an Mikrotik sensor."""
def __init__(
self,
coordinator: MikrotikCoordinator,
entity_description,
uid: str | None = None,
):
super().__init__(coordinator, entity_description, uid)
self._attr_suggested_unit_of_measurement = (
self.entity_description.suggested_unit_of_measurement
)
@property @property
def state(self) -> Optional[str]: def native_value(self) -> StateType | date | datetime | Decimal:
"""Return the state.""" """Return the value reported by the sensor."""
if self.entity_description.data_attribute: return self._data[self.entity_description.data_attribute]
return self._data[self.entity_description.data_attribute]
else:
return "unknown"
@property @property
def native_unit_of_measurement(self): def native_unit_of_measurement(self) -> str | None:
"""Return the unit the value is expressed in.""" """Return the unit the value is expressed in."""
if self.entity_description.native_unit_of_measurement: if self.entity_description.native_unit_of_measurement:
if self.entity_description.native_unit_of_measurement.startswith("data__"): if self.entity_description.native_unit_of_measurement.startswith("data__"):
uom = self.entity_description.native_unit_of_measurement[6:] uom = self.entity_description.native_unit_of_measurement[6:]
if uom in self._data: if uom in self._data:
uom = self._data[uom] return self._data[uom]
return uom
return self.entity_description.native_unit_of_measurement return self.entity_description.native_unit_of_measurement
@ -113,9 +127,9 @@ class MikrotikClientTrafficSensor(MikrotikSensor):
""" """
if self.entity_description.data_attribute in ["lan-tx", "lan-rx"]: if self.entity_description.data_attribute in ["lan-tx", "lan-rx"]:
return ( return (
self._ctrl.connected() self.coordinator.connected()
and self._data["available"] and self._data["available"]
and self._data["local_accounting"] and self._data["local_accounting"]
) )
else: else:
return self._ctrl.connected() and self._data["available"] return self.coordinator.connected() and self._data["available"]

View file

@ -1,4 +1,6 @@
"""Definitions for Mikrotik Router sensor entities.""" """Definitions for sensor entities."""
from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import List from typing import List
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
@ -9,11 +11,13 @@ from homeassistant.components.sensor import (
SensorEntityDescription, SensorEntityDescription,
) )
from homeassistant.const import ( from homeassistant.const import (
TEMP_CELSIUS,
ELECTRIC_POTENTIAL_VOLT,
POWER_WATT,
PERCENTAGE, PERCENTAGE,
DATA_BYTES, REVOLUTIONS_PER_MINUTE,
UnitOfTemperature,
UnitOfDataRate,
UnitOfInformation,
UnitOfElectricPotential,
UnitOfPower,
) )
from .const import DOMAIN from .const import DOMAIN
@ -113,28 +117,30 @@ DEVICE_ATTRIBUTES_GPS = [
class MikrotikSensorEntityDescription(SensorEntityDescription): class MikrotikSensorEntityDescription(SensorEntityDescription):
"""Class describing mikrotik entities.""" """Class describing mikrotik entities."""
ha_group: str = "" ha_group: str | None = None
ha_connection: str = "" ha_connection: str | None = None
ha_connection_value: str = "" ha_connection_value: str | None = None
data_path: str = "" data_path: str | None = None
data_attribute: str = "" data_attribute: str | None = None
data_name: str = "" data_name: str | None = None
data_name_comment: bool = False data_name_comment: bool = False
data_uid: str = "" data_uid: str | None = None
data_reference: str = "" data_reference: str | None = None
data_attributes_list: List = field(default_factory=lambda: []) data_attributes_list: List = field(default_factory=lambda: [])
func: str = "MikrotikSensor" func: str = "MikrotikSensor"
SENSOR_TYPES = { SENSOR_TYPES: tuple[MikrotikSensorEntityDescription, ...] = (
"system_temperature": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_temperature", key="system_temperature",
name="Temperature", name="Temperature",
icon="mdi:thermometer", icon="mdi:thermometer",
native_unit_of_measurement=TEMP_CELSIUS, native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=0,
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=EntityCategory.DIAGNOSTIC,
ha_group="System", ha_group="System",
data_path="health", data_path="health",
data_attribute="temperature", data_attribute="temperature",
@ -142,11 +148,13 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_voltage": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_voltage", key="system_voltage",
name="Voltage", name="Voltage",
icon="mdi:lightning-bolt", icon="mdi:lightning-bolt",
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, native_unit_of_measurement=UnitOfElectricPotential.VOLT,
suggested_unit_of_measurement=UnitOfElectricPotential.VOLT,
suggested_display_precision=1,
device_class=SensorDeviceClass.VOLTAGE, device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
@ -157,14 +165,16 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_cpu-temperature": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_cpu-temperature", key="system_cpu-temperature",
name="CPU temperature", name="CPU temperature",
icon="mdi:thermometer", icon="mdi:thermometer",
native_unit_of_measurement=TEMP_CELSIUS, native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=0,
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=EntityCategory.DIAGNOSTIC,
ha_group="System", ha_group="System",
data_path="health", data_path="health",
data_attribute="cpu-temperature", data_attribute="cpu-temperature",
@ -172,14 +182,16 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_switch-temperature": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_switch-temperature", key="system_switch-temperature",
name="Switch temperature", name="Switch temperature",
icon="mdi:thermometer", icon="mdi:thermometer",
native_unit_of_measurement=TEMP_CELSIUS, native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=0,
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=EntityCategory.DIAGNOSTIC,
ha_group="System", ha_group="System",
data_path="health", data_path="health",
data_attribute="switch-temperature", data_attribute="switch-temperature",
@ -187,14 +199,16 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_board-temperature1": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_board-temperature1", key="system_board-temperature1",
name="Board temperature", name="Board temperature",
icon="mdi:thermometer", icon="mdi:thermometer",
native_unit_of_measurement=TEMP_CELSIUS, native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=0,
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=EntityCategory.DIAGNOSTIC,
ha_group="System", ha_group="System",
data_path="health", data_path="health",
data_attribute="board-temperature1", data_attribute="board-temperature1",
@ -202,14 +216,16 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_power-consumption": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_power-consumption", key="system_power-consumption",
name="Power consumption", name="Power consumption",
icon="mdi:transmission-tower", icon="mdi:transmission-tower",
native_unit_of_measurement=POWER_WATT, native_unit_of_measurement=UnitOfPower.WATT,
suggested_unit_of_measurement=UnitOfPower.WATT,
suggested_display_precision=0,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=EntityCategory.DIAGNOSTIC,
ha_group="System", ha_group="System",
data_path="health", data_path="health",
data_attribute="power-consumption", data_attribute="power-consumption",
@ -217,14 +233,14 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_fan1-speed": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_fan1-speed", key="system_fan1-speed",
name="Fan1 speed", name="Fan1 speed",
icon="mdi:fan", icon="mdi:fan",
native_unit_of_measurement="RPM", native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
device_class=None, device_class=None,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=EntityCategory.DIAGNOSTIC,
ha_group="System", ha_group="System",
data_path="health", data_path="health",
data_attribute="fan1-speed", data_attribute="fan1-speed",
@ -232,14 +248,14 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_fan2-speed": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_fan2-speed", key="system_fan2-speed",
name="Fan2 speed", name="Fan2 speed",
icon="mdi:fan", icon="mdi:fan",
native_unit_of_measurement="RPM", native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
device_class=None, device_class=None,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=EntityCategory.DIAGNOSTIC,
ha_group="System", ha_group="System",
data_path="health", data_path="health",
data_attribute="fan2-speed", data_attribute="fan2-speed",
@ -247,7 +263,7 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_uptime": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_uptime", key="system_uptime",
name="Uptime", name="Uptime",
icon=None, icon=None,
@ -262,7 +278,7 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_cpu-load": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_cpu-load", key="system_cpu-load",
name="CPU load", name="CPU load",
icon="mdi:speedometer", icon="mdi:speedometer",
@ -277,7 +293,7 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_memory-usage": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_memory-usage", key="system_memory-usage",
name="Memory usage", name="Memory usage",
icon="mdi:memory", icon="mdi:memory",
@ -292,7 +308,7 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_hdd-usage": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_hdd-usage", key="system_hdd-usage",
name="HDD usage", name="HDD usage",
icon="mdi:harddisk", icon="mdi:harddisk",
@ -307,7 +323,7 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_clients-wired": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_clients-wired", key="system_clients-wired",
name="Wired clients", name="Wired clients",
icon="mdi:lan", icon="mdi:lan",
@ -322,7 +338,7 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_clients-wireless": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_clients-wireless", key="system_clients-wireless",
name="Wireless clients", name="Wireless clients",
icon="mdi:wifi", icon="mdi:wifi",
@ -337,7 +353,7 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_captive-authorized": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_captive-authorized", key="system_captive-authorized",
name="Captive portal clients", name="Captive portal clients",
icon="mdi:key-wireless", icon="mdi:key-wireless",
@ -352,7 +368,7 @@ SENSOR_TYPES = {
data_uid="", data_uid="",
data_reference="", data_reference="",
), ),
"system_gps-latitude": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_gps-latitude", key="system_gps-latitude",
name="Latitude", name="Latitude",
icon="mdi:latitude", icon="mdi:latitude",
@ -368,7 +384,7 @@ SENSOR_TYPES = {
data_reference="", data_reference="",
data_attributes_list=DEVICE_ATTRIBUTES_GPS, data_attributes_list=DEVICE_ATTRIBUTES_GPS,
), ),
"system_gps-longitude": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="system_gps-longitude", key="system_gps-longitude",
name="Longitude", name="Longitude",
icon="mdi:longitude", icon="mdi:longitude",
@ -384,12 +400,14 @@ SENSOR_TYPES = {
data_reference="", data_reference="",
data_attributes_list=DEVICE_ATTRIBUTES_GPS, data_attributes_list=DEVICE_ATTRIBUTES_GPS,
), ),
"traffic_tx": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="traffic_tx", key="traffic_tx",
name="TX", name="TX",
icon="mdi:upload-network-outline", icon="mdi:upload-network-outline",
native_unit_of_measurement="data__tx-attr", native_unit_of_measurement=UnitOfDataRate.BITS_PER_SECOND,
device_class=None, suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
suggested_display_precision=1,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=None,
ha_group="data__default-name", ha_group="data__default-name",
@ -403,12 +421,14 @@ SENSOR_TYPES = {
data_attributes_list=DEVICE_ATTRIBUTES_IFACE, data_attributes_list=DEVICE_ATTRIBUTES_IFACE,
func="MikrotikInterfaceTrafficSensor", func="MikrotikInterfaceTrafficSensor",
), ),
"traffic_rx": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="traffic_rx", key="traffic_rx",
name="RX", name="RX",
icon="mdi:download-network-outline", icon="mdi:download-network-outline",
native_unit_of_measurement="data__rx-attr", native_unit_of_measurement=UnitOfDataRate.BITS_PER_SECOND,
device_class=None, suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
suggested_display_precision=1,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=None,
ha_group="data__default-name", ha_group="data__default-name",
@ -422,12 +442,14 @@ SENSOR_TYPES = {
data_attributes_list=DEVICE_ATTRIBUTES_IFACE, data_attributes_list=DEVICE_ATTRIBUTES_IFACE,
func="MikrotikInterfaceTrafficSensor", func="MikrotikInterfaceTrafficSensor",
), ),
"total_tx": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="tx-total", key="tx-total",
name="TX total", name="TX total",
icon="mdi:upload-network", icon="mdi:upload-network",
native_unit_of_measurement=DATA_BYTES, native_unit_of_measurement=UnitOfInformation.BITS,
device_class=None, suggested_unit_of_measurement=UnitOfInformation.GIGABYTES,
suggested_display_precision=1,
device_class=SensorDeviceClass.DATA_SIZE,
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
entity_category=None, entity_category=None,
ha_group="data__default-name", ha_group="data__default-name",
@ -441,12 +463,14 @@ SENSOR_TYPES = {
data_attributes_list=DEVICE_ATTRIBUTES_IFACE, data_attributes_list=DEVICE_ATTRIBUTES_IFACE,
func="MikrotikInterfaceTrafficSensor", func="MikrotikInterfaceTrafficSensor",
), ),
"total_rx": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="rx-total", key="rx-total",
name="RX total", name="RX total",
icon="mdi:download-network", icon="mdi:download-network",
native_unit_of_measurement=DATA_BYTES, native_unit_of_measurement=UnitOfInformation.BITS,
device_class=None, suggested_unit_of_measurement=UnitOfInformation.GIGABYTES,
suggested_display_precision=1,
device_class=SensorDeviceClass.DATA_SIZE,
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
entity_category=None, entity_category=None,
ha_group="data__default-name", ha_group="data__default-name",
@ -460,12 +484,14 @@ SENSOR_TYPES = {
data_attributes_list=DEVICE_ATTRIBUTES_IFACE, data_attributes_list=DEVICE_ATTRIBUTES_IFACE,
func="MikrotikInterfaceTrafficSensor", func="MikrotikInterfaceTrafficSensor",
), ),
"client_traffic_lan_tx": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="client_traffic_lan_tx", key="client_traffic_lan_tx",
name="LAN TX", name="LAN TX",
icon="mdi:upload-network", icon="mdi:upload-network",
native_unit_of_measurement="data__tx-rx-attr", native_unit_of_measurement=UnitOfDataRate.BITS_PER_SECOND,
device_class=None, suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
suggested_display_precision=1,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=None,
ha_group="", ha_group="",
@ -479,12 +505,14 @@ SENSOR_TYPES = {
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC, data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
func="MikrotikClientTrafficSensor", func="MikrotikClientTrafficSensor",
), ),
"client_traffic_lan_rx": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="client_traffic_lan_rx", key="client_traffic_lan_rx",
name="LAN RX", name="LAN RX",
icon="mdi:download-network", icon="mdi:download-network",
native_unit_of_measurement="data__tx-rx-attr", native_unit_of_measurement=UnitOfDataRate.BITS_PER_SECOND,
device_class=None, suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
suggested_display_precision=1,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=None,
ha_group="", ha_group="",
@ -498,12 +526,14 @@ SENSOR_TYPES = {
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC, data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
func="MikrotikClientTrafficSensor", func="MikrotikClientTrafficSensor",
), ),
"client_traffic_wan_tx": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="client_traffic_wan_tx", key="client_traffic_wan_tx",
name="WAN TX", name="WAN TX",
icon="mdi:upload-network", icon="mdi:upload-network",
native_unit_of_measurement="data__tx-rx-attr", native_unit_of_measurement=UnitOfDataRate.BITS_PER_SECOND,
device_class=None, suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
suggested_display_precision=1,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=None,
ha_group="", ha_group="",
@ -517,12 +547,14 @@ SENSOR_TYPES = {
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC, data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
func="MikrotikClientTrafficSensor", func="MikrotikClientTrafficSensor",
), ),
"client_traffic_wan_rx": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="client_traffic_wan_rx", key="client_traffic_wan_rx",
name="WAN RX", name="WAN RX",
icon="mdi:download-network", icon="mdi:download-network",
native_unit_of_measurement="data__tx-rx-attr", native_unit_of_measurement=UnitOfDataRate.BITS_PER_SECOND,
device_class=None, suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
suggested_display_precision=1,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=None,
ha_group="", ha_group="",
@ -536,12 +568,14 @@ SENSOR_TYPES = {
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC, data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
func="MikrotikClientTrafficSensor", func="MikrotikClientTrafficSensor",
), ),
"client_traffic_tx": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="client_traffic_tx", key="client_traffic_tx",
name="TX", name="TX",
icon="mdi:upload-network", icon="mdi:upload-network",
native_unit_of_measurement="data__tx-rx-attr", native_unit_of_measurement=UnitOfDataRate.BITS_PER_SECOND,
device_class=None, suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
suggested_display_precision=1,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=None,
ha_group="", ha_group="",
@ -555,12 +589,14 @@ SENSOR_TYPES = {
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC, data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
func="MikrotikClientTrafficSensor", func="MikrotikClientTrafficSensor",
), ),
"client_traffic_rx": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="client_traffic_rx", key="client_traffic_rx",
name="RX", name="RX",
icon="mdi:download-network", icon="mdi:download-network",
native_unit_of_measurement="data__tx-rx-attr", native_unit_of_measurement=UnitOfDataRate.BITS_PER_SECOND,
device_class=None, suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
suggested_display_precision=1,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=None, entity_category=None,
ha_group="", ha_group="",
@ -574,7 +610,7 @@ SENSOR_TYPES = {
data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC, data_attributes_list=DEVICE_ATTRIBUTES_CLIENT_TRAFFIC,
func="MikrotikClientTrafficSensor", func="MikrotikClientTrafficSensor",
), ),
"environment": MikrotikSensorEntityDescription( MikrotikSensorEntityDescription(
key="environment", key="environment",
name="", name="",
icon="mdi:clipboard-list", icon="mdi:clipboard-list",
@ -591,6 +627,6 @@ SENSOR_TYPES = {
data_uid="name", data_uid="name",
data_reference="name", data_reference="name",
), ),
} )
SENSOR_SERVICES = {} SENSOR_SERVICES = []

View file

@ -6,7 +6,7 @@ from collections.abc import Mapping
from homeassistant.components.switch import SwitchEntity from homeassistant.components.switch import SwitchEntity
from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.restore_state import RestoreEntity
from .helper import format_attribute from .helper import format_attribute
from .model import model_async_setup_entry, MikrotikEntity from .entity import model_async_setup_entry, MikrotikEntity
from .switch_types import ( from .switch_types import (
SENSOR_TYPES, SENSOR_TYPES,
SENSOR_SERVICES, SENSOR_SERVICES,

View file

@ -8,7 +8,7 @@ from homeassistant.components.update import (
UpdateDeviceClass, UpdateDeviceClass,
UpdateEntityFeature, UpdateEntityFeature,
) )
from .model import model_async_setup_entry, MikrotikEntity from .entity import model_async_setup_entry, MikrotikEntity
from .update_types import ( from .update_types import (
SENSOR_TYPES, SENSOR_TYPES,
SENSOR_SERVICES, SENSOR_SERVICES,