2023-08-08 00:50:09 +02:00
|
|
|
"""Mikrotik coordinator."""
|
|
|
|
|
|
|
|
from __future__ import annotations
|
2019-12-02 04:19:40 +01:00
|
|
|
|
2021-04-12 11:00:08 +02:00
|
|
|
import ipaddress
|
2021-05-14 10:16:06 +02:00
|
|
|
import logging
|
|
|
|
import re
|
|
|
|
import pytz
|
2021-04-12 11:00:08 +02:00
|
|
|
|
2021-05-14 10:16:06 +02:00
|
|
|
from datetime import datetime, timedelta
|
2023-08-09 23:00:00 +02:00
|
|
|
from dataclasses import dataclass
|
2020-04-08 10:19:29 +02:00
|
|
|
from ipaddress import ip_address, IPv4Network
|
2020-12-03 16:41:06 +01:00
|
|
|
from mac_vendor_lookup import AsyncMacLookup
|
2020-03-21 18:46:54 +03:00
|
|
|
|
2023-08-08 00:50:09 +02:00
|
|
|
from homeassistant.config_entries import ConfigEntry
|
2023-08-10 02:03:11 +02:00
|
|
|
from homeassistant.core import HomeAssistant
|
2023-08-08 00:50:09 +02:00
|
|
|
from homeassistant.helpers import entity_registry
|
|
|
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
2020-04-08 09:17:25 +02:00
|
|
|
from homeassistant.util.dt import utcnow
|
2019-12-02 04:19:40 +01:00
|
|
|
|
2023-08-08 00:50:09 +02:00
|
|
|
|
2020-04-08 20:31:47 +02:00
|
|
|
from homeassistant.const import (
|
|
|
|
CONF_NAME,
|
|
|
|
CONF_HOST,
|
|
|
|
CONF_PORT,
|
|
|
|
CONF_USERNAME,
|
|
|
|
CONF_PASSWORD,
|
|
|
|
CONF_SSL,
|
2021-12-16 22:45:29 +01:00
|
|
|
CONF_ZONE,
|
|
|
|
STATE_HOME,
|
2020-04-08 20:31:47 +02:00
|
|
|
)
|
|
|
|
|
2019-12-02 04:19:40 +01:00
|
|
|
from .const import (
|
2019-12-02 18:13:55 +01:00
|
|
|
DOMAIN,
|
2020-04-08 20:31:47 +02:00
|
|
|
CONF_TRACK_IFACE_CLIENTS,
|
|
|
|
DEFAULT_TRACK_IFACE_CLIENTS,
|
2020-04-09 23:17:39 +02:00
|
|
|
CONF_TRACK_HOSTS,
|
|
|
|
DEFAULT_TRACK_HOSTS,
|
2019-12-02 18:13:55 +01:00
|
|
|
CONF_SCAN_INTERVAL,
|
|
|
|
DEFAULT_SCAN_INTERVAL,
|
2020-12-12 12:19:40 +01:00
|
|
|
CONF_SENSOR_PORT_TRAFFIC,
|
|
|
|
DEFAULT_SENSOR_PORT_TRAFFIC,
|
|
|
|
CONF_SENSOR_CLIENT_TRAFFIC,
|
|
|
|
DEFAULT_SENSOR_CLIENT_TRAFFIC,
|
2022-05-26 09:41:00 +02:00
|
|
|
CONF_SENSOR_CLIENT_CAPTIVE,
|
|
|
|
DEFAULT_SENSOR_CLIENT_CAPTIVE,
|
2020-12-12 12:19:40 +01:00
|
|
|
CONF_SENSOR_SIMPLE_QUEUES,
|
|
|
|
DEFAULT_SENSOR_SIMPLE_QUEUES,
|
2020-12-12 12:30:36 +01:00
|
|
|
CONF_SENSOR_NAT,
|
|
|
|
DEFAULT_SENSOR_NAT,
|
2020-12-18 19:58:54 +01:00
|
|
|
CONF_SENSOR_MANGLE,
|
|
|
|
DEFAULT_SENSOR_MANGLE,
|
2021-04-12 11:38:16 +02:00
|
|
|
CONF_SENSOR_FILTER,
|
|
|
|
DEFAULT_SENSOR_FILTER,
|
2020-12-25 10:15:24 +01:00
|
|
|
CONF_SENSOR_KIDCONTROL,
|
|
|
|
DEFAULT_SENSOR_KIDCONTROL,
|
|
|
|
CONF_SENSOR_PPP,
|
|
|
|
DEFAULT_SENSOR_PPP,
|
2020-12-12 12:30:05 +01:00
|
|
|
CONF_SENSOR_SCRIPTS,
|
|
|
|
DEFAULT_SENSOR_SCRIPTS,
|
2020-12-12 12:32:47 +01:00
|
|
|
CONF_SENSOR_ENVIRONMENT,
|
|
|
|
DEFAULT_SENSOR_ENVIRONMENT,
|
2019-12-02 04:19:40 +01:00
|
|
|
)
|
2019-12-12 23:01:57 +01:00
|
|
|
from .exceptions import ApiEntryNotFound
|
2022-02-02 22:01:12 +01:00
|
|
|
from .apiparser import parse_api
|
2020-03-21 19:02:28 +03:00
|
|
|
from .mikrotikapi import MikrotikAPI
|
2019-12-02 04:19:40 +01:00
|
|
|
|
2019-12-02 17:59:49 +01:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
2019-12-02 04:19:40 +01:00
|
|
|
|
2021-05-14 10:16:06 +02:00
|
|
|
DEFAULT_TIME_ZONE = None
|
|
|
|
|
2019-12-02 16:22:01 +01:00
|
|
|
|
2021-04-12 11:00:08 +02:00
|
|
|
def is_valid_ip(address):
|
|
|
|
try:
|
|
|
|
ipaddress.ip_address(address)
|
|
|
|
return True
|
|
|
|
except ValueError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2021-05-14 10:16:06 +02:00
|
|
|
def utc_from_timestamp(timestamp: float) -> datetime:
|
|
|
|
"""Return a UTC time from a timestamp."""
|
|
|
|
return pytz.utc.localize(datetime.utcfromtimestamp(timestamp))
|
|
|
|
|
|
|
|
|
|
|
|
def as_local(dattim: datetime) -> datetime:
|
|
|
|
"""Convert a UTC datetime object to local time zone."""
|
|
|
|
if dattim.tzinfo == DEFAULT_TIME_ZONE:
|
|
|
|
return dattim
|
|
|
|
if dattim.tzinfo is None:
|
|
|
|
dattim = pytz.utc.localize(dattim)
|
|
|
|
|
|
|
|
return dattim.astimezone(DEFAULT_TIME_ZONE)
|
|
|
|
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
@dataclass
|
|
|
|
class MikrotikData:
|
|
|
|
"""Data for the mikrotik integration."""
|
|
|
|
|
|
|
|
data_coordinator: MikrotikCoordinator
|
|
|
|
tracker_coordinator: MikrotikTrackerCoordinator
|
|
|
|
|
|
|
|
|
|
|
|
class MikrotikTrackerCoordinator(DataUpdateCoordinator[None]):
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
hass: HomeAssistant,
|
|
|
|
config_entry: ConfigEntry,
|
|
|
|
coordinator: MikrotikCoordinator,
|
|
|
|
):
|
|
|
|
"""Initialize MikrotikTrackerCoordinator."""
|
|
|
|
self.hass = hass
|
|
|
|
self.config_entry: ConfigEntry = config_entry
|
|
|
|
self.coordinator = coordinator
|
|
|
|
|
|
|
|
super().__init__(
|
|
|
|
self.hass,
|
|
|
|
_LOGGER,
|
|
|
|
name=DOMAIN,
|
|
|
|
update_interval=timedelta(seconds=10),
|
|
|
|
)
|
|
|
|
self.name = config_entry.data[CONF_NAME]
|
|
|
|
self.host = config_entry.data[CONF_HOST]
|
|
|
|
|
|
|
|
self.api = MikrotikAPI(
|
|
|
|
config_entry.data[CONF_HOST],
|
|
|
|
config_entry.data[CONF_USERNAME],
|
|
|
|
config_entry.data[CONF_PASSWORD],
|
|
|
|
config_entry.data[CONF_PORT],
|
|
|
|
config_entry.data[CONF_SSL],
|
|
|
|
)
|
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# option_zone
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_zone(self):
|
|
|
|
"""Config entry option zones."""
|
|
|
|
return self.config_entry.options.get(CONF_ZONE, STATE_HOME)
|
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# _async_update_data
|
|
|
|
# ---------------------------
|
|
|
|
async def _async_update_data(self):
|
|
|
|
"""Trigger update by timer"""
|
|
|
|
if not self.coordinator.option_track_network_hosts:
|
|
|
|
return
|
|
|
|
|
|
|
|
if "test" not in self.coordinator.ds["access"]:
|
|
|
|
return
|
|
|
|
|
|
|
|
for uid in list(self.coordinator.ds["host"]):
|
|
|
|
if not self.coordinator.host_tracking_initialized:
|
|
|
|
# Add missing default values
|
|
|
|
for key, default in zip(
|
|
|
|
[
|
|
|
|
"address",
|
|
|
|
"mac-address",
|
|
|
|
"interface",
|
|
|
|
"host-name",
|
|
|
|
"last-seen",
|
|
|
|
"available",
|
|
|
|
],
|
|
|
|
["unknown", "unknown", "unknown", "unknown", False, False],
|
|
|
|
):
|
|
|
|
if key not in self.coordinator.ds["host"][uid]:
|
|
|
|
self.coordinator.ds["host"][uid][key] = default
|
|
|
|
|
|
|
|
# Check host availability
|
|
|
|
if (
|
|
|
|
self.coordinator.ds["host"][uid]["source"]
|
|
|
|
not in ["capsman", "wireless"]
|
|
|
|
and self.coordinator.ds["host"][uid]["address"] not in ["unknown", ""]
|
|
|
|
and self.coordinator.ds["host"][uid]["interface"] not in ["unknown", ""]
|
|
|
|
):
|
|
|
|
tmp_interface = self.coordinator.ds["host"][uid]["interface"]
|
|
|
|
if (
|
|
|
|
uid in self.coordinator.ds["arp"]
|
|
|
|
and self.coordinator.ds["arp"][uid]["bridge"] != ""
|
|
|
|
):
|
|
|
|
tmp_interface = self.coordinator.ds["arp"][uid]["bridge"]
|
|
|
|
|
|
|
|
_LOGGER.debug(
|
|
|
|
"Ping host: %s", self.coordinator.ds["host"][uid]["address"]
|
|
|
|
)
|
|
|
|
|
|
|
|
self.coordinator.ds["host"][uid][
|
|
|
|
"available"
|
|
|
|
] = await self.hass.async_add_executor_job(
|
|
|
|
self.api.arp_ping,
|
|
|
|
self.coordinator.ds["host"][uid]["address"],
|
|
|
|
tmp_interface,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Update last seen
|
|
|
|
if self.coordinator.ds["host"][uid]["available"]:
|
|
|
|
self.coordinator.ds["host"][uid]["last-seen"] = utcnow()
|
|
|
|
|
|
|
|
self.coordinator.host_tracking_initialized = True
|
|
|
|
|
|
|
|
await self.coordinator.async_process_host()
|
|
|
|
return {
|
|
|
|
"host": self.coordinator.ds["host"],
|
|
|
|
"routerboard": self.coordinator.ds["routerboard"],
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-02 17:59:49 +01:00
|
|
|
# ---------------------------
|
2019-12-02 04:19:40 +01:00
|
|
|
# MikrotikControllerData
|
2019-12-02 17:59:49 +01:00
|
|
|
# ---------------------------
|
2023-08-09 23:00:00 +02:00
|
|
|
class MikrotikCoordinator(DataUpdateCoordinator[None]):
|
2023-08-08 00:50:09 +02:00
|
|
|
"""MikrotikCoordinator Class"""
|
2020-03-16 04:51:41 +01:00
|
|
|
|
2023-08-08 00:50:09 +02:00
|
|
|
def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry):
|
|
|
|
"""Initialize MikrotikCoordinator."""
|
2019-12-02 18:13:55 +01:00
|
|
|
self.hass = hass
|
2023-08-08 00:50:09 +02:00
|
|
|
self.config_entry: ConfigEntry = config_entry
|
|
|
|
super().__init__(
|
|
|
|
self.hass,
|
|
|
|
_LOGGER,
|
|
|
|
name=DOMAIN,
|
2023-08-09 13:55:53 +02:00
|
|
|
update_interval=self.option_scan_interval,
|
2023-08-08 00:50:09 +02:00
|
|
|
)
|
2020-04-08 20:31:47 +02:00
|
|
|
self.name = config_entry.data[CONF_NAME]
|
|
|
|
self.host = config_entry.data[CONF_HOST]
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds = {
|
2022-08-20 01:42:03 +02:00
|
|
|
"access": {},
|
2020-03-16 04:51:41 +01:00
|
|
|
"routerboard": {},
|
|
|
|
"resource": {},
|
2020-05-27 20:04:09 +02:00
|
|
|
"health": {},
|
2022-01-08 22:07:31 +01:00
|
|
|
"health7": {},
|
2020-03-16 04:51:41 +01:00
|
|
|
"interface": {},
|
2020-04-11 02:32:33 +02:00
|
|
|
"bridge": {},
|
2020-04-11 01:26:44 +02:00
|
|
|
"bridge_host": {},
|
2020-03-16 04:51:41 +01:00
|
|
|
"arp": {},
|
|
|
|
"nat": {},
|
2020-12-25 18:42:57 +01:00
|
|
|
"kid-control": {},
|
2020-12-18 19:58:54 +01:00
|
|
|
"mangle": {},
|
2021-04-12 11:38:16 +02:00
|
|
|
"filter": {},
|
2020-12-25 11:48:07 +01:00
|
|
|
"ppp_secret": {},
|
|
|
|
"ppp_active": {},
|
2020-03-16 04:51:41 +01:00
|
|
|
"fw-update": {},
|
|
|
|
"script": {},
|
2020-03-25 13:18:49 +01:00
|
|
|
"queue": {},
|
2020-04-08 09:15:26 +02:00
|
|
|
"dns": {},
|
2020-04-07 03:55:38 +02:00
|
|
|
"dhcp-server": {},
|
2022-08-11 13:23:51 +02:00
|
|
|
"dhcp-client": {},
|
2020-04-08 10:09:31 +02:00
|
|
|
"dhcp-network": {},
|
2020-04-07 03:55:38 +02:00
|
|
|
"dhcp": {},
|
2020-04-09 22:24:48 +02:00
|
|
|
"capsman_hosts": {},
|
2022-08-21 22:27:49 +02:00
|
|
|
"wireless": {},
|
2020-04-09 22:24:48 +02:00
|
|
|
"wireless_hosts": {},
|
2020-04-08 07:15:14 +02:00
|
|
|
"host": {},
|
2020-04-09 02:53:43 +02:00
|
|
|
"host_hass": {},
|
2022-05-26 09:41:00 +02:00
|
|
|
"hostspot_host": {},
|
2022-01-01 17:26:40 +00:00
|
|
|
"client_traffic": {},
|
2022-01-20 12:11:33 +01:00
|
|
|
"environment": {},
|
2022-06-28 21:26:49 +02:00
|
|
|
"ups": {},
|
2022-06-28 21:57:24 +02:00
|
|
|
"gps": {},
|
2020-03-16 04:51:41 +01:00
|
|
|
}
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2022-01-03 08:06:25 +00:00
|
|
|
self.notified_flags = []
|
|
|
|
|
2020-04-08 20:31:47 +02:00
|
|
|
self.api = MikrotikAPI(
|
|
|
|
config_entry.data[CONF_HOST],
|
|
|
|
config_entry.data[CONF_USERNAME],
|
|
|
|
config_entry.data[CONF_PASSWORD],
|
|
|
|
config_entry.data[CONF_PORT],
|
2020-04-11 05:45:36 +02:00
|
|
|
config_entry.data[CONF_SSL],
|
2020-04-08 20:31:47 +02:00
|
|
|
)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2022-02-18 07:33:38 +01:00
|
|
|
self.debug = False
|
|
|
|
if _LOGGER.getEffectiveLevel() == 10:
|
|
|
|
self.debug = True
|
|
|
|
|
2020-04-06 22:50:01 +02:00
|
|
|
self.nat_removed = {}
|
2020-12-25 09:23:25 +01:00
|
|
|
self.mangle_removed = {}
|
2021-04-12 12:40:45 +02:00
|
|
|
self.filter_removed = {}
|
2020-04-09 02:53:43 +02:00
|
|
|
self.host_hass_recovered = False
|
2020-04-11 05:14:39 +02:00
|
|
|
self.host_tracking_initialized = False
|
2020-04-06 22:50:01 +02:00
|
|
|
|
2020-04-09 22:08:26 +02:00
|
|
|
self.support_capsman = False
|
|
|
|
self.support_wireless = False
|
2022-02-18 09:10:07 +01:00
|
|
|
self.support_wifiwave2 = False
|
2020-12-25 20:27:37 +01:00
|
|
|
self.support_ppp = False
|
2022-06-28 21:26:49 +02:00
|
|
|
self.support_ups = False
|
2022-06-28 21:57:24 +02:00
|
|
|
self.support_gps = False
|
2020-04-09 22:08:26 +02:00
|
|
|
|
2020-12-02 10:10:47 +01:00
|
|
|
self.major_fw_version = 0
|
|
|
|
|
2020-12-03 16:41:06 +01:00
|
|
|
self.async_mac_lookup = AsyncMacLookup()
|
2022-08-20 01:42:03 +02:00
|
|
|
self.accessrights_reported = False
|
2020-12-03 16:41:06 +01:00
|
|
|
|
2023-08-09 10:49:49 +02:00
|
|
|
self.last_hwinfo_update = datetime(1970, 1, 1)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
2020-04-08 20:31:47 +02:00
|
|
|
# option_track_iface_clients
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
|
|
|
@property
|
2020-04-08 20:31:47 +02:00
|
|
|
def option_track_iface_clients(self):
|
2019-12-02 18:13:55 +01:00
|
|
|
"""Config entry option to not track ARP."""
|
2020-04-11 05:45:36 +02:00
|
|
|
return self.config_entry.options.get(
|
|
|
|
CONF_TRACK_IFACE_CLIENTS, DEFAULT_TRACK_IFACE_CLIENTS
|
|
|
|
)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2020-04-09 23:17:39 +02:00
|
|
|
# ---------------------------
|
|
|
|
# option_track_network_hosts
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_track_network_hosts(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(CONF_TRACK_HOSTS, DEFAULT_TRACK_HOSTS)
|
|
|
|
|
2020-12-12 12:19:40 +01:00
|
|
|
# ---------------------------
|
|
|
|
# option_sensor_port_traffic
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_sensor_port_traffic(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(
|
|
|
|
CONF_SENSOR_PORT_TRAFFIC, DEFAULT_SENSOR_PORT_TRAFFIC
|
|
|
|
)
|
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# option_sensor_client_traffic
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_sensor_client_traffic(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(
|
|
|
|
CONF_SENSOR_CLIENT_TRAFFIC, DEFAULT_SENSOR_CLIENT_TRAFFIC
|
|
|
|
)
|
|
|
|
|
2022-05-26 09:41:00 +02:00
|
|
|
# ---------------------------
|
|
|
|
# option_sensor_client_captive
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_sensor_client_captive(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(
|
|
|
|
CONF_SENSOR_CLIENT_CAPTIVE, DEFAULT_SENSOR_CLIENT_CAPTIVE
|
|
|
|
)
|
|
|
|
|
2020-12-12 12:19:40 +01:00
|
|
|
# ---------------------------
|
|
|
|
# option_sensor_simple_queues
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_sensor_simple_queues(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(
|
|
|
|
CONF_SENSOR_SIMPLE_QUEUES, DEFAULT_SENSOR_SIMPLE_QUEUES
|
|
|
|
)
|
|
|
|
|
2020-12-12 12:30:36 +01:00
|
|
|
# ---------------------------
|
|
|
|
# option_sensor_nat
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_sensor_nat(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(CONF_SENSOR_NAT, DEFAULT_SENSOR_NAT)
|
|
|
|
|
2020-12-18 19:58:54 +01:00
|
|
|
# ---------------------------
|
2020-12-25 10:15:24 +01:00
|
|
|
# option_sensor_mangle
|
2020-12-18 19:58:54 +01:00
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_sensor_mangle(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(CONF_SENSOR_MANGLE, DEFAULT_SENSOR_MANGLE)
|
|
|
|
|
2021-04-12 12:40:45 +02:00
|
|
|
# ---------------------------
|
|
|
|
# option_sensor_filter
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_sensor_filter(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(CONF_SENSOR_FILTER, DEFAULT_SENSOR_FILTER)
|
|
|
|
|
2020-12-25 10:15:24 +01:00
|
|
|
# ---------------------------
|
|
|
|
# option_sensor_kidcontrol
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_sensor_kidcontrol(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(
|
|
|
|
CONF_SENSOR_KIDCONTROL, DEFAULT_SENSOR_KIDCONTROL
|
|
|
|
)
|
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# option_sensor_ppp
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_sensor_ppp(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(CONF_SENSOR_PPP, DEFAULT_SENSOR_PPP)
|
|
|
|
|
2020-12-12 12:30:05 +01:00
|
|
|
# ---------------------------
|
|
|
|
# option_sensor_scripts
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_sensor_scripts(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(
|
|
|
|
CONF_SENSOR_SCRIPTS, DEFAULT_SENSOR_SCRIPTS
|
|
|
|
)
|
|
|
|
|
2020-12-12 12:32:47 +01:00
|
|
|
# ---------------------------
|
|
|
|
# option_sensor_environment
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_sensor_environment(self):
|
|
|
|
"""Config entry option to not track ARP."""
|
|
|
|
return self.config_entry.options.get(
|
|
|
|
CONF_SENSOR_ENVIRONMENT, DEFAULT_SENSOR_ENVIRONMENT
|
|
|
|
)
|
|
|
|
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
|
|
|
# option_scan_interval
|
|
|
|
# ---------------------------
|
|
|
|
@property
|
|
|
|
def option_scan_interval(self):
|
|
|
|
"""Config entry option scan interval."""
|
2020-03-16 04:51:41 +01:00
|
|
|
scan_interval = self.config_entry.options.get(
|
|
|
|
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL
|
|
|
|
)
|
2019-12-02 18:13:55 +01:00
|
|
|
return timedelta(seconds=scan_interval)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
|
|
|
# connected
|
|
|
|
# ---------------------------
|
|
|
|
def connected(self):
|
2019-12-05 20:42:14 +01:00
|
|
|
"""Return connected state"""
|
2019-12-02 18:13:55 +01:00
|
|
|
return self.api.connected()
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
2020-04-11 01:10:56 +02:00
|
|
|
# set_value
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
2020-04-11 01:10:56 +02:00
|
|
|
def set_value(self, path, param, value, mod_param, mod_value):
|
|
|
|
"""Change value using Mikrotik API"""
|
2022-06-28 20:28:00 +02:00
|
|
|
return self.api.set_value(path, param, value, mod_param, mod_value)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2020-12-28 01:26:46 +01:00
|
|
|
# ---------------------------
|
|
|
|
# execute
|
|
|
|
# ---------------------------
|
|
|
|
def execute(self, path, command, param, value):
|
|
|
|
"""Change value using Mikrotik API"""
|
|
|
|
return self.api.execute(path, command, param, value)
|
|
|
|
|
2019-12-04 16:09:30 +01:00
|
|
|
# ---------------------------
|
2020-04-11 01:10:56 +02:00
|
|
|
# run_script
|
2019-12-04 16:09:30 +01:00
|
|
|
# ---------------------------
|
2020-04-11 01:10:56 +02:00
|
|
|
def run_script(self, name):
|
|
|
|
"""Run script using Mikrotik API"""
|
2020-04-20 11:05:49 +02:00
|
|
|
if type(name) != str:
|
2020-04-20 11:46:23 +02:00
|
|
|
if "router" not in name.data:
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.config_entry.data["name"] != name.data.get("router"):
|
|
|
|
return
|
|
|
|
|
|
|
|
if "script" in name.data:
|
|
|
|
name = name.data.get("script")
|
|
|
|
else:
|
|
|
|
return
|
2020-04-20 11:05:49 +02:00
|
|
|
|
2020-04-11 01:10:56 +02:00
|
|
|
try:
|
|
|
|
self.api.run_script(name)
|
|
|
|
except ApiEntryNotFound as error:
|
|
|
|
_LOGGER.error("Failed to run script: %s", error)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2020-04-09 22:08:26 +02:00
|
|
|
# ---------------------------
|
2020-04-11 01:10:56 +02:00
|
|
|
# get_capabilities
|
2020-04-09 22:08:26 +02:00
|
|
|
# ---------------------------
|
2020-04-11 01:10:56 +02:00
|
|
|
def get_capabilities(self):
|
2020-04-09 22:08:26 +02:00
|
|
|
"""Update Mikrotik data"""
|
|
|
|
packages = parse_api(
|
|
|
|
data={},
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/system/package"),
|
2020-04-09 22:08:26 +02:00
|
|
|
key="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "name"},
|
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2022-02-18 09:10:07 +01:00
|
|
|
if 0 < self.major_fw_version < 7:
|
|
|
|
if "ppp" in packages:
|
|
|
|
self.support_ppp = packages["ppp"]["enabled"]
|
2020-12-25 20:27:37 +01:00
|
|
|
|
2022-02-18 09:10:07 +01:00
|
|
|
if "wireless" in packages:
|
|
|
|
self.support_capsman = packages["wireless"]["enabled"]
|
|
|
|
self.support_wireless = packages["wireless"]["enabled"]
|
|
|
|
else:
|
|
|
|
self.support_capsman = False
|
|
|
|
self.support_wireless = False
|
2020-04-09 22:08:26 +02:00
|
|
|
|
2022-02-18 09:10:07 +01:00
|
|
|
elif 0 < self.major_fw_version >= 7:
|
2020-12-25 20:27:37 +01:00
|
|
|
self.support_ppp = True
|
2022-02-18 09:10:07 +01:00
|
|
|
self.support_wireless = True
|
|
|
|
if "wifiwave2" in packages and packages["wifiwave2"]["enabled"]:
|
|
|
|
self.support_wifiwave2 = True
|
|
|
|
self.support_capsman = False
|
|
|
|
else:
|
|
|
|
self.support_wifiwave2 = False
|
|
|
|
self.support_capsman = True
|
2020-12-25 20:27:37 +01:00
|
|
|
|
2022-06-28 21:26:49 +02:00
|
|
|
if "ups" in packages and packages["ups"]["enabled"]:
|
|
|
|
self.support_ups = True
|
|
|
|
|
2022-06-28 21:57:24 +02:00
|
|
|
if "gps" in packages and packages["gps"]["enabled"]:
|
|
|
|
self.support_gps = True
|
|
|
|
|
2020-04-11 01:10:56 +02:00
|
|
|
# ---------------------------
|
|
|
|
# async_get_host_hass
|
|
|
|
# ---------------------------
|
|
|
|
async def async_get_host_hass(self):
|
|
|
|
"""Get host data from HA entity registry"""
|
2022-06-25 13:55:46 +02:00
|
|
|
registry = entity_registry.async_get(self.hass)
|
2020-04-11 01:10:56 +02:00
|
|
|
for entity in registry.entities.values():
|
2020-04-11 05:45:36 +02:00
|
|
|
if (
|
|
|
|
entity.config_entry_id == self.config_entry.entry_id
|
2022-06-25 13:55:46 +02:00
|
|
|
and entity.entity_id.startswith("device_tracker.")
|
2020-04-11 05:45:36 +02:00
|
|
|
):
|
2022-08-17 09:29:46 +02:00
|
|
|
tmp = entity.unique_id.split("-")
|
|
|
|
if tmp[0] != self.name.lower():
|
|
|
|
continue
|
|
|
|
|
|
|
|
if tmp[1] != "host":
|
|
|
|
continue
|
|
|
|
|
|
|
|
if ":" not in tmp[2]:
|
|
|
|
continue
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host_hass"][tmp[2].upper()] = entity.original_name
|
2020-04-11 05:14:39 +02:00
|
|
|
|
2020-04-11 01:10:56 +02:00
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
# _async_update_data
|
2020-04-11 01:10:56 +02:00
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
async def _async_update_data(self):
|
2019-12-05 20:42:14 +01:00
|
|
|
"""Update Mikrotik data"""
|
2023-08-09 10:49:49 +02:00
|
|
|
delta = datetime.now().replace(microsecond=0) - self.last_hwinfo_update
|
|
|
|
if self.api.has_reconnected() or delta.total_seconds() > 60 * 60 * 4:
|
|
|
|
await self.hass.async_add_executor_job(self.get_access)
|
|
|
|
|
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.get_firmware_update)
|
|
|
|
|
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.get_system_resource)
|
|
|
|
|
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.get_capabilities)
|
|
|
|
|
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.get_system_routerboard)
|
|
|
|
|
|
|
|
if self.api.connected() and self.option_sensor_scripts:
|
|
|
|
await self.hass.async_add_executor_job(self.get_script)
|
|
|
|
|
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.get_dhcp_network)
|
|
|
|
|
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.get_dns)
|
|
|
|
|
|
|
|
if not self.api.connected():
|
|
|
|
raise UpdateFailed("Mikrotik Disconnected")
|
|
|
|
|
|
|
|
if self.api.connected():
|
|
|
|
self.last_hwinfo_update = datetime.now().replace(microsecond=0)
|
2020-04-13 07:36:00 +02:00
|
|
|
|
2022-08-11 10:04:57 +02:00
|
|
|
await self.hass.async_add_executor_job(self.get_system_resource)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
# if self.api.connected() and "available" not in self.ds["fw-update"]:
|
2023-08-09 10:49:49 +02:00
|
|
|
# await self.hass.async_add_executor_job(self.get_firmware_update)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2022-02-18 10:42:10 +01:00
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.get_system_health)
|
|
|
|
|
2022-03-03 11:10:41 +01:00
|
|
|
if self.api.connected():
|
2022-08-11 13:23:51 +02:00
|
|
|
await self.hass.async_add_executor_job(self.get_dhcp_client)
|
2022-03-03 11:10:41 +01:00
|
|
|
|
2022-02-18 10:42:10 +01:00
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.get_interface)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.api.connected() and not self.ds["host_hass"]:
|
2020-04-11 00:02:50 +02:00
|
|
|
await self.async_get_host_hass()
|
2020-04-09 02:53:43 +02:00
|
|
|
|
2020-04-13 07:50:06 +02:00
|
|
|
if self.api.connected() and self.support_capsman:
|
2020-04-09 22:24:48 +02:00
|
|
|
await self.hass.async_add_executor_job(self.get_capsman_hosts)
|
|
|
|
|
2022-08-21 22:27:49 +02:00
|
|
|
if self.api.connected() and self.support_wireless:
|
|
|
|
await self.hass.async_add_executor_job(self.get_wireless)
|
|
|
|
|
2020-04-13 07:50:06 +02:00
|
|
|
if self.api.connected() and self.support_wireless:
|
2020-04-09 22:24:48 +02:00
|
|
|
await self.hass.async_add_executor_job(self.get_wireless_hosts)
|
2020-04-09 22:09:38 +02:00
|
|
|
|
2020-04-13 07:50:06 +02:00
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.get_bridge)
|
|
|
|
|
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.get_arp)
|
|
|
|
|
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.get_dhcp)
|
|
|
|
|
|
|
|
if self.api.connected():
|
|
|
|
await self.async_process_host()
|
|
|
|
|
|
|
|
if self.api.connected():
|
|
|
|
await self.hass.async_add_executor_job(self.process_interface_client)
|
|
|
|
|
2020-12-12 12:30:36 +01:00
|
|
|
if self.api.connected() and self.option_sensor_nat:
|
2020-04-13 07:50:06 +02:00
|
|
|
await self.hass.async_add_executor_job(self.get_nat)
|
|
|
|
|
2020-12-25 18:42:57 +01:00
|
|
|
if self.api.connected() and self.option_sensor_kidcontrol:
|
|
|
|
await self.hass.async_add_executor_job(self.get_kidcontrol)
|
|
|
|
|
2020-12-18 19:58:54 +01:00
|
|
|
if self.api.connected() and self.option_sensor_mangle:
|
|
|
|
await self.hass.async_add_executor_job(self.get_mangle)
|
|
|
|
|
2021-04-12 12:40:45 +02:00
|
|
|
if self.api.connected() and self.option_sensor_filter:
|
|
|
|
await self.hass.async_add_executor_job(self.get_filter)
|
|
|
|
|
2020-12-25 20:27:37 +01:00
|
|
|
if self.api.connected() and self.support_ppp and self.option_sensor_ppp:
|
2020-12-25 11:48:07 +01:00
|
|
|
await self.hass.async_add_executor_job(self.get_ppp)
|
|
|
|
|
2022-01-01 17:26:40 +00:00
|
|
|
if self.api.connected() and self.option_sensor_client_traffic:
|
|
|
|
if 0 < self.major_fw_version < 7:
|
|
|
|
await self.hass.async_add_executor_job(self.process_accounting)
|
|
|
|
elif 0 < self.major_fw_version >= 7:
|
|
|
|
await self.hass.async_add_executor_job(self.process_kid_control_devices)
|
2020-04-08 07:15:14 +02:00
|
|
|
|
2022-05-26 09:41:00 +02:00
|
|
|
if self.api.connected() and self.option_sensor_client_captive:
|
|
|
|
await self.hass.async_add_executor_job(self.get_captive)
|
|
|
|
|
2020-12-12 12:24:32 +01:00
|
|
|
if self.api.connected() and self.option_sensor_simple_queues:
|
2020-05-03 15:04:16 +02:00
|
|
|
await self.hass.async_add_executor_job(self.get_queue)
|
|
|
|
|
2020-12-14 15:27:36 +01:00
|
|
|
if self.api.connected() and self.option_sensor_environment:
|
2020-12-02 13:56:50 +01:00
|
|
|
await self.hass.async_add_executor_job(self.get_environment)
|
|
|
|
|
2022-06-28 21:26:49 +02:00
|
|
|
if self.api.connected() and self.support_ups:
|
|
|
|
await self.hass.async_add_executor_job(self.get_ups)
|
|
|
|
|
2022-06-28 21:57:24 +02:00
|
|
|
if self.api.connected() and self.support_gps:
|
|
|
|
await self.hass.async_add_executor_job(self.get_gps)
|
|
|
|
|
2023-08-09 10:49:49 +02:00
|
|
|
if not self.api.connected():
|
|
|
|
raise UpdateFailed("Mikrotik Disconnected")
|
|
|
|
|
2023-08-08 00:50:09 +02:00
|
|
|
# async_dispatcher_send(self.hass, "update_sensors", self)
|
2023-08-09 23:00:00 +02:00
|
|
|
return self.ds
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2022-08-20 01:42:03 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_access
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_access(self) -> None:
|
2022-08-20 01:42:03 +02:00
|
|
|
"""Get access rights from Mikrotik"""
|
|
|
|
tmp_user = parse_api(
|
|
|
|
data={},
|
|
|
|
source=self.api.query("/user"),
|
|
|
|
key="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "name"},
|
|
|
|
{"name": "group"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
tmp_group = parse_api(
|
|
|
|
data={},
|
|
|
|
source=self.api.query("/user/group"),
|
|
|
|
key="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "name"},
|
|
|
|
{"name": "policy"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["access"] = tmp_group[
|
2022-08-20 01:42:03 +02:00
|
|
|
tmp_user[self.config_entry.data[CONF_USERNAME]]["group"]
|
|
|
|
]["policy"].split(",")
|
|
|
|
|
|
|
|
if not self.accessrights_reported:
|
|
|
|
self.accessrights_reported = True
|
|
|
|
if (
|
2023-08-09 23:00:00 +02:00
|
|
|
"write" not in self.ds["access"]
|
|
|
|
or "policy" not in self.ds["access"]
|
|
|
|
or "reboot" not in self.ds["access"]
|
|
|
|
or "test" not in self.ds["access"]
|
2022-08-20 01:42:03 +02:00
|
|
|
):
|
|
|
|
_LOGGER.warning(
|
|
|
|
"Mikrotik %s user %s does not have sufficient access rights. Integration functionality will be limited.",
|
|
|
|
self.host,
|
|
|
|
self.config_entry.data[CONF_USERNAME],
|
|
|
|
)
|
|
|
|
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
2019-12-05 20:42:14 +01:00
|
|
|
# get_interface
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_interface(self) -> None:
|
2019-12-05 20:42:14 +01:00
|
|
|
"""Get all interfaces data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"] = parse_api(
|
|
|
|
data=self.ds["interface"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/interface"),
|
2020-03-16 04:51:41 +01:00
|
|
|
key="default-name",
|
2020-04-13 09:16:33 +02:00
|
|
|
key_secondary="name",
|
2019-12-10 22:32:59 +01:00
|
|
|
vals=[
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": "default-name"},
|
2021-04-13 13:43:06 +02:00
|
|
|
{"name": ".id"},
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": "name", "default_val": "default-name"},
|
|
|
|
{"name": "type", "default": "unknown"},
|
|
|
|
{"name": "running", "type": "bool"},
|
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
|
|
|
},
|
|
|
|
{"name": "port-mac-address", "source": "mac-address"},
|
|
|
|
{"name": "comment"},
|
|
|
|
{"name": "last-link-down-time"},
|
|
|
|
{"name": "last-link-up-time"},
|
|
|
|
{"name": "link-downs"},
|
|
|
|
{"name": "tx-queue-drop"},
|
|
|
|
{"name": "actual-mtu"},
|
2020-12-14 15:54:32 +01:00
|
|
|
{"name": "about", "source": ".about", "default": ""},
|
2022-03-03 11:24:39 +01:00
|
|
|
{"name": "rx-current", "source": "rx-byte", "default": 0.0},
|
|
|
|
{"name": "tx-current", "source": "tx-byte", "default": 0.0},
|
2019-12-10 22:32:59 +01:00
|
|
|
],
|
|
|
|
ensure_vals=[
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": "client-ip-address"},
|
|
|
|
{"name": "client-mac-address"},
|
2022-03-03 10:12:05 +01:00
|
|
|
{"name": "rx-previous", "default": 0.0},
|
|
|
|
{"name": "tx-previous", "default": 0.0},
|
|
|
|
{"name": "rx", "default": 0.0},
|
|
|
|
{"name": "tx", "default": 0.0},
|
2022-06-25 14:34:25 +02:00
|
|
|
{"name": "rx-total", "default": 0.0},
|
|
|
|
{"name": "tx-total", "default": 0.0},
|
2020-03-16 04:51:41 +01:00
|
|
|
],
|
2020-06-26 23:45:52 +02:00
|
|
|
skip=[
|
|
|
|
{"name": "type", "value": "bridge"},
|
|
|
|
{"name": "type", "value": "ppp-in"},
|
|
|
|
{"name": "type", "value": "pptp-in"},
|
|
|
|
{"name": "type", "value": "sstp-in"},
|
|
|
|
{"name": "type", "value": "l2tp-in"},
|
|
|
|
{"name": "type", "value": "pppoe-in"},
|
2020-06-27 18:19:15 +02:00
|
|
|
{"name": "type", "value": "ovpn-in"},
|
2020-12-02 09:28:56 +01:00
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2022-03-03 11:24:39 +01:00
|
|
|
if self.option_sensor_port_traffic:
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["interface"].items():
|
2022-03-03 11:24:39 +01:00
|
|
|
current_tx = vals["tx-current"]
|
2023-08-08 00:50:09 +02:00
|
|
|
previous_tx = vals["tx-previous"] or current_tx
|
2022-03-03 11:24:39 +01:00
|
|
|
|
2023-08-08 00:50:09 +02:00
|
|
|
delta_tx = max(0, current_tx - previous_tx)
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"][uid]["tx"] = round(
|
2023-08-08 00:50:09 +02:00
|
|
|
delta_tx / self.option_scan_interval.seconds
|
2022-03-03 11:24:39 +01:00
|
|
|
)
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"][uid]["tx-previous"] = current_tx
|
2022-03-03 11:24:39 +01:00
|
|
|
|
|
|
|
current_rx = vals["rx-current"]
|
2023-08-08 00:50:09 +02:00
|
|
|
previous_rx = vals["rx-previous"] or current_rx
|
2022-03-03 11:24:39 +01:00
|
|
|
|
2023-08-08 00:50:09 +02:00
|
|
|
delta_rx = max(0, current_rx - previous_rx)
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"][uid]["rx"] = round(
|
2023-08-08 00:50:09 +02:00
|
|
|
delta_rx / self.option_scan_interval.seconds
|
2022-03-03 11:24:39 +01:00
|
|
|
)
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"][uid]["rx-previous"] = current_rx
|
2022-03-03 11:24:39 +01:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"][uid]["tx-total"] = current_tx
|
|
|
|
self.ds["interface"][uid]["rx-total"] = current_rx
|
2022-06-25 14:34:25 +02:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"] = parse_api(
|
|
|
|
data=self.ds["interface"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/interface/ethernet"),
|
2020-12-02 09:28:56 +01:00
|
|
|
key="default-name",
|
|
|
|
key_secondary="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "default-name"},
|
|
|
|
{"name": "name", "default_val": "default-name"},
|
|
|
|
{"name": "poe-out", "default": "N/A"},
|
2021-04-12 09:54:13 +02:00
|
|
|
{"name": "sfp-shutdown-temperature", "default": ""},
|
2020-12-02 09:28:56 +01:00
|
|
|
],
|
|
|
|
skip=[
|
|
|
|
{"name": "type", "value": "bridge"},
|
|
|
|
{"name": "type", "value": "ppp-in"},
|
|
|
|
{"name": "type", "value": "pptp-in"},
|
|
|
|
{"name": "type", "value": "sstp-in"},
|
|
|
|
{"name": "type", "value": "l2tp-in"},
|
|
|
|
{"name": "type", "value": "pppoe-in"},
|
|
|
|
{"name": "type", "value": "ovpn-in"},
|
2020-06-26 23:45:52 +02:00
|
|
|
],
|
2019-12-10 22:32:59 +01:00
|
|
|
)
|
2019-12-08 22:49:36 +01:00
|
|
|
|
2020-04-13 09:16:33 +02:00
|
|
|
# Udpate virtual interfaces
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["interface"].items():
|
|
|
|
self.ds["interface"][uid]["comment"] = str(
|
|
|
|
self.ds["interface"][uid]["comment"]
|
2022-04-06 19:57:27 +02:00
|
|
|
)
|
|
|
|
|
2020-04-13 09:16:33 +02:00
|
|
|
if vals["default-name"] == "":
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"][uid]["default-name"] = vals["name"]
|
|
|
|
self.ds["interface"][uid][
|
2020-04-13 09:16:33 +02:00
|
|
|
"port-mac-address"
|
|
|
|
] = f"{vals['port-mac-address']}-{vals['name']}"
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["interface"][uid]["type"] == "ether":
|
2021-12-15 23:07:11 +01:00
|
|
|
if (
|
|
|
|
"sfp-shutdown-temperature" in vals
|
|
|
|
and vals["sfp-shutdown-temperature"] != ""
|
|
|
|
):
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"] = parse_api(
|
|
|
|
data=self.ds["interface"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query(
|
2022-06-28 20:13:07 +02:00
|
|
|
"/interface/ethernet",
|
|
|
|
command="monitor",
|
|
|
|
args={".id": vals[".id"], "once": True},
|
|
|
|
),
|
2021-12-15 23:07:11 +01:00
|
|
|
key_search="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "status", "default": "unknown"},
|
|
|
|
{"name": "auto-negotiation", "default": "unknown"},
|
|
|
|
{"name": "advertising", "default": "unknown"},
|
|
|
|
{"name": "link-partner-advertising", "default": "unknown"},
|
|
|
|
{"name": "sfp-temperature", "default": "unknown"},
|
|
|
|
{"name": "sfp-supply-voltage", "default": "unknown"},
|
|
|
|
{"name": "sfp-module-present", "default": "unknown"},
|
|
|
|
{"name": "sfp-tx-bias-current", "default": "unknown"},
|
|
|
|
{"name": "sfp-tx-power", "default": "unknown"},
|
|
|
|
{"name": "sfp-rx-power", "default": "unknown"},
|
|
|
|
{"name": "sfp-rx-loss", "default": "unknown"},
|
|
|
|
{"name": "sfp-tx-fault", "default": "unknown"},
|
|
|
|
{"name": "sfp-type", "default": "unknown"},
|
|
|
|
{"name": "sfp-connector-type", "default": "unknown"},
|
|
|
|
{"name": "sfp-vendor-name", "default": "unknown"},
|
|
|
|
{"name": "sfp-vendor-part-number", "default": "unknown"},
|
|
|
|
{"name": "sfp-vendor-revision", "default": "unknown"},
|
|
|
|
{"name": "sfp-vendor-serial", "default": "unknown"},
|
|
|
|
{"name": "sfp-manufacturing-date", "default": "unknown"},
|
|
|
|
{"name": "eeprom-checksum", "default": "unknown"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"] = parse_api(
|
|
|
|
data=self.ds["interface"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query(
|
2022-06-28 20:13:07 +02:00
|
|
|
"/interface/ethernet",
|
|
|
|
command="monitor",
|
|
|
|
args={".id": vals[".id"], "once": True},
|
|
|
|
),
|
2021-12-15 23:07:11 +01:00
|
|
|
key_search="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "status", "default": "unknown"},
|
|
|
|
{"name": "rate", "default": "unknown"},
|
|
|
|
{"name": "full-duplex", "default": "unknown"},
|
|
|
|
{"name": "auto-negotiation", "default": "unknown"},
|
|
|
|
],
|
|
|
|
)
|
2021-04-12 09:54:13 +02:00
|
|
|
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
2020-04-11 02:32:33 +02:00
|
|
|
# get_bridge
|
2020-04-11 01:26:44 +02:00
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_bridge(self) -> None:
|
2020-04-11 01:26:44 +02:00
|
|
|
"""Get system resources data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["bridge_host"] = parse_api(
|
|
|
|
data=self.ds["bridge_host"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/interface/bridge/host"),
|
2020-04-11 01:26:44 +02:00
|
|
|
key="mac-address",
|
|
|
|
vals=[
|
|
|
|
{"name": "mac-address"},
|
|
|
|
{"name": "interface", "default": "unknown"},
|
|
|
|
{"name": "bridge", "default": "unknown"},
|
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
|
|
|
},
|
|
|
|
],
|
2020-04-12 08:49:34 +02:00
|
|
|
only=[{"key": "local", "value": False}],
|
2020-04-11 01:26:44 +02:00
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["bridge_host"].items():
|
|
|
|
self.ds["bridge"][vals["bridge"]] = True
|
2020-04-11 02:32:33 +02:00
|
|
|
|
2020-04-11 01:26:44 +02:00
|
|
|
# ---------------------------
|
2020-04-11 02:33:06 +02:00
|
|
|
# process_interface_client
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def process_interface_client(self) -> None:
|
2019-12-05 22:29:25 +01:00
|
|
|
# Remove data if disabled
|
2020-04-08 20:31:47 +02:00
|
|
|
if not self.option_track_iface_clients:
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid in self.ds["interface"]:
|
|
|
|
self.ds["interface"][uid]["client-ip-address"] = "disabled"
|
|
|
|
self.ds["interface"][uid]["client-mac-address"] = "disabled"
|
2019-12-12 09:06:15 +01:00
|
|
|
return
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["interface"].items():
|
|
|
|
self.ds["interface"][uid]["client-ip-address"] = ""
|
|
|
|
self.ds["interface"][uid]["client-mac-address"] = ""
|
|
|
|
for arp_uid, arp_vals in self.ds["arp"].items():
|
2020-04-11 02:33:06 +02:00
|
|
|
if arp_vals["interface"] != vals["name"]:
|
|
|
|
continue
|
2020-03-11 23:39:43 +01:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["interface"][uid]["client-ip-address"] == "":
|
|
|
|
self.ds["interface"][uid]["client-ip-address"] = arp_vals["address"]
|
2020-04-11 02:33:06 +02:00
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"][uid]["client-ip-address"] = "multiple"
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["interface"][uid]["client-mac-address"] == "":
|
|
|
|
self.ds["interface"][uid]["client-mac-address"] = arp_vals[
|
2020-04-11 05:45:36 +02:00
|
|
|
"mac-address"
|
|
|
|
]
|
2020-04-11 02:33:06 +02:00
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["interface"][uid]["client-mac-address"] = "multiple"
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["interface"][uid]["client-ip-address"] == "":
|
|
|
|
self.ds["interface"][uid]["client-ip-address"] = "none"
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["interface"][uid]["client-mac-address"] == "":
|
|
|
|
self.ds["interface"][uid]["client-mac-address"] = "none"
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2019-12-03 18:29:05 +01:00
|
|
|
# ---------------------------
|
|
|
|
# get_nat
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_nat(self) -> None:
|
2019-12-05 20:42:14 +01:00
|
|
|
"""Get NAT data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["nat"] = parse_api(
|
|
|
|
data=self.ds["nat"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/firewall/nat"),
|
2020-03-16 04:51:41 +01:00
|
|
|
key=".id",
|
2019-12-12 09:06:15 +01:00
|
|
|
vals=[
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": ".id"},
|
2020-12-25 16:52:36 +01:00
|
|
|
{"name": "chain", "default": "unknown"},
|
|
|
|
{"name": "action", "default": "unknown"},
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": "protocol", "default": "any"},
|
|
|
|
{"name": "dst-port", "default": "any"},
|
|
|
|
{"name": "in-interface", "default": "any"},
|
2020-12-25 16:52:36 +01:00
|
|
|
{"name": "out-interface", "default": "any"},
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": "to-addresses"},
|
2020-12-25 16:52:36 +01:00
|
|
|
{"name": "to-ports", "default": "any"},
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": "comment"},
|
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
|
|
|
},
|
2019-12-12 09:06:15 +01:00
|
|
|
],
|
|
|
|
val_proc=[
|
2020-12-25 16:52:36 +01:00
|
|
|
[
|
|
|
|
{"name": "uniq-id"},
|
|
|
|
{"action": "combine"},
|
|
|
|
{"key": "chain"},
|
|
|
|
{"text": ","},
|
|
|
|
{"key": "action"},
|
|
|
|
{"text": ","},
|
|
|
|
{"key": "protocol"},
|
|
|
|
{"text": ","},
|
|
|
|
{"key": "in-interface"},
|
|
|
|
{"text": ":"},
|
|
|
|
{"key": "dst-port"},
|
|
|
|
{"text": "-"},
|
|
|
|
{"key": "out-interface"},
|
|
|
|
{"text": ":"},
|
|
|
|
{"key": "to-addresses"},
|
|
|
|
{"text": ":"},
|
|
|
|
{"key": "to-ports"},
|
|
|
|
],
|
2019-12-12 09:06:15 +01:00
|
|
|
[
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": "name"},
|
|
|
|
{"action": "combine"},
|
|
|
|
{"key": "protocol"},
|
|
|
|
{"text": ":"},
|
|
|
|
{"key": "dst-port"},
|
2020-12-25 16:52:36 +01:00
|
|
|
],
|
2019-12-12 13:02:11 +01:00
|
|
|
],
|
2020-04-12 08:49:34 +02:00
|
|
|
only=[{"key": "action", "value": "dst-nat"}],
|
2019-12-12 09:06:15 +01:00
|
|
|
)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2020-04-11 00:02:50 +02:00
|
|
|
# Remove duplicate NAT entries to prevent crash
|
2020-04-06 22:50:01 +02:00
|
|
|
nat_uniq = {}
|
|
|
|
nat_del = {}
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid in self.ds["nat"]:
|
|
|
|
self.ds["nat"][uid]["comment"] = str(self.ds["nat"][uid]["comment"])
|
2022-04-06 19:57:27 +02:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
tmp_name = self.ds["nat"][uid]["uniq-id"]
|
2020-04-06 22:50:01 +02:00
|
|
|
if tmp_name not in nat_uniq:
|
|
|
|
nat_uniq[tmp_name] = uid
|
|
|
|
else:
|
|
|
|
nat_del[uid] = 1
|
|
|
|
nat_del[nat_uniq[tmp_name]] = 1
|
|
|
|
|
|
|
|
for uid in nat_del:
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["nat"][uid]["uniq-id"] not in self.nat_removed:
|
|
|
|
self.nat_removed[self.ds["nat"][uid]["uniq-id"]] = 1
|
2020-04-11 05:45:36 +02:00
|
|
|
_LOGGER.error(
|
|
|
|
"Mikrotik %s duplicate NAT rule %s, entity will be unavailable.",
|
|
|
|
self.host,
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["nat"][uid]["name"],
|
2020-04-11 05:45:36 +02:00
|
|
|
)
|
2020-04-06 22:50:01 +02:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
del self.ds["nat"][uid]
|
2020-04-06 22:50:01 +02:00
|
|
|
|
2020-12-18 19:58:54 +01:00
|
|
|
# ---------------------------
|
|
|
|
# get_mangle
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_mangle(self) -> None:
|
2020-12-18 19:58:54 +01:00
|
|
|
"""Get Mangle data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["mangle"] = parse_api(
|
|
|
|
data=self.ds["mangle"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/firewall/mangle"),
|
2020-12-18 19:58:54 +01:00
|
|
|
key=".id",
|
|
|
|
vals=[
|
|
|
|
{"name": ".id"},
|
|
|
|
{"name": "chain"},
|
|
|
|
{"name": "action"},
|
|
|
|
{"name": "comment"},
|
|
|
|
{"name": "address-list"},
|
|
|
|
{"name": "passthrough", "type": "bool", "default": False},
|
|
|
|
{"name": "protocol", "default": "any"},
|
2020-12-25 09:22:54 +01:00
|
|
|
{"name": "src-address", "default": "any"},
|
2020-12-18 19:58:54 +01:00
|
|
|
{"name": "src-port", "default": "any"},
|
2020-12-25 09:22:54 +01:00
|
|
|
{"name": "dst-address", "default": "any"},
|
2020-12-18 19:58:54 +01:00
|
|
|
{"name": "dst-port", "default": "any"},
|
2021-08-24 18:24:41 +07:00
|
|
|
{"name": "src-address-list", "default": "any"},
|
|
|
|
{"name": "dst-address-list", "default": "any"},
|
2020-12-18 19:58:54 +01:00
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
val_proc=[
|
|
|
|
[
|
2020-12-25 22:57:12 +01:00
|
|
|
{"name": "uniq-id"},
|
2020-12-18 19:58:54 +01:00
|
|
|
{"action": "combine"},
|
2020-12-25 09:22:54 +01:00
|
|
|
{"key": "chain"},
|
|
|
|
{"text": ","},
|
|
|
|
{"key": "action"},
|
|
|
|
{"text": ","},
|
2020-12-18 19:58:54 +01:00
|
|
|
{"key": "protocol"},
|
2020-12-25 09:22:54 +01:00
|
|
|
{"text": ","},
|
|
|
|
{"key": "src-address"},
|
|
|
|
{"text": ":"},
|
|
|
|
{"key": "src-port"},
|
|
|
|
{"text": "-"},
|
|
|
|
{"key": "dst-address"},
|
2020-12-18 19:58:54 +01:00
|
|
|
{"text": ":"},
|
|
|
|
{"key": "dst-port"},
|
2021-08-24 18:24:41 +07:00
|
|
|
{"text": ","},
|
|
|
|
{"key": "src-address-list"},
|
2021-08-24 19:24:50 +07:00
|
|
|
{"text": "-"},
|
2021-08-24 18:24:41 +07:00
|
|
|
{"key": "dst-address-list"},
|
2020-12-25 22:57:12 +01:00
|
|
|
],
|
|
|
|
[
|
|
|
|
{"name": "name"},
|
|
|
|
{"action": "combine"},
|
|
|
|
{"key": "action"},
|
|
|
|
{"text": ","},
|
|
|
|
{"key": "protocol"},
|
|
|
|
{"text": ":"},
|
|
|
|
{"key": "dst-port"},
|
|
|
|
],
|
2020-12-18 19:58:54 +01:00
|
|
|
],
|
|
|
|
skip=[
|
|
|
|
{"name": "dynamic", "value": True},
|
|
|
|
{"name": "action", "value": "jump"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2020-12-25 09:23:25 +01:00
|
|
|
# Remove duplicate Mangle entries to prevent crash
|
|
|
|
mangle_uniq = {}
|
|
|
|
mangle_del = {}
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid in self.ds["mangle"]:
|
|
|
|
self.ds["mangle"][uid]["comment"] = str(self.ds["mangle"][uid]["comment"])
|
2022-04-06 19:57:27 +02:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
tmp_name = self.ds["mangle"][uid]["uniq-id"]
|
2020-12-25 09:23:25 +01:00
|
|
|
if tmp_name not in mangle_uniq:
|
|
|
|
mangle_uniq[tmp_name] = uid
|
|
|
|
else:
|
|
|
|
mangle_del[uid] = 1
|
|
|
|
mangle_del[mangle_uniq[tmp_name]] = 1
|
|
|
|
|
|
|
|
for uid in mangle_del:
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["mangle"][uid]["uniq-id"] not in self.mangle_removed:
|
|
|
|
self.mangle_removed[self.ds["mangle"][uid]["uniq-id"]] = 1
|
2020-12-25 09:23:25 +01:00
|
|
|
_LOGGER.error(
|
|
|
|
"Mikrotik %s duplicate Mangle rule %s, entity will be unavailable.",
|
|
|
|
self.host,
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["mangle"][uid]["name"],
|
2020-12-25 09:23:25 +01:00
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
del self.ds["mangle"][uid]
|
2020-12-25 09:23:25 +01:00
|
|
|
|
2021-04-12 12:40:45 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_filter
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_filter(self) -> None:
|
2021-04-12 12:40:45 +02:00
|
|
|
"""Get Filter data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["filter"] = parse_api(
|
|
|
|
data=self.ds["filter"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/firewall/filter"),
|
2021-04-12 12:40:45 +02:00
|
|
|
key=".id",
|
|
|
|
vals=[
|
|
|
|
{"name": ".id"},
|
|
|
|
{"name": "chain"},
|
|
|
|
{"name": "action"},
|
|
|
|
{"name": "comment"},
|
|
|
|
{"name": "address-list"},
|
|
|
|
{"name": "protocol", "default": "any"},
|
|
|
|
{"name": "in-interface", "default": "any"},
|
2022-02-18 11:11:14 +01:00
|
|
|
{"name": "in-interface-list", "default": "any"},
|
2021-04-12 12:40:45 +02:00
|
|
|
{"name": "out-interface", "default": "any"},
|
2022-02-18 11:11:14 +01:00
|
|
|
{"name": "out-interface-list", "default": "any"},
|
2021-04-12 12:40:45 +02:00
|
|
|
{"name": "src-address", "default": "any"},
|
2022-02-18 11:11:14 +01:00
|
|
|
{"name": "src-address-list", "default": "any"},
|
2021-04-12 12:40:45 +02:00
|
|
|
{"name": "src-port", "default": "any"},
|
|
|
|
{"name": "dst-address", "default": "any"},
|
2022-02-18 11:11:14 +01:00
|
|
|
{"name": "dst-address-list", "default": "any"},
|
2021-04-12 12:40:45 +02:00
|
|
|
{"name": "dst-port", "default": "any"},
|
|
|
|
{"name": "layer7-protocol", "default": "any"},
|
|
|
|
{"name": "connection-state", "default": "any"},
|
|
|
|
{"name": "tcp-flags", "default": "any"},
|
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
|
|
|
"default": True,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
val_proc=[
|
|
|
|
[
|
|
|
|
{"name": "uniq-id"},
|
|
|
|
{"action": "combine"},
|
|
|
|
{"key": "chain"},
|
|
|
|
{"text": ","},
|
|
|
|
{"key": "action"},
|
|
|
|
{"text": ","},
|
|
|
|
{"key": "protocol"},
|
|
|
|
{"text": ","},
|
|
|
|
{"key": "layer7-protocol"},
|
|
|
|
{"text": ","},
|
|
|
|
{"key": "in-interface"},
|
2022-02-18 11:11:14 +01:00
|
|
|
{"text": ","},
|
|
|
|
{"key": "in-interface-list"},
|
2021-04-12 12:40:45 +02:00
|
|
|
{"text": ":"},
|
|
|
|
{"key": "src-address"},
|
2022-02-18 11:11:14 +01:00
|
|
|
{"text": ","},
|
|
|
|
{"key": "src-address-list"},
|
2021-04-12 12:40:45 +02:00
|
|
|
{"text": ":"},
|
|
|
|
{"key": "src-port"},
|
|
|
|
{"text": "-"},
|
|
|
|
{"key": "out-interface"},
|
2022-02-18 11:11:14 +01:00
|
|
|
{"text": ","},
|
|
|
|
{"key": "out-interface-list"},
|
2021-04-12 12:40:45 +02:00
|
|
|
{"text": ":"},
|
|
|
|
{"key": "dst-address"},
|
2022-02-18 11:11:14 +01:00
|
|
|
{"text": ","},
|
|
|
|
{"key": "dst-address-list"},
|
2021-04-12 12:40:45 +02:00
|
|
|
{"text": ":"},
|
|
|
|
{"key": "dst-port"},
|
|
|
|
],
|
|
|
|
[
|
|
|
|
{"name": "name"},
|
|
|
|
{"action": "combine"},
|
|
|
|
{"key": "action"},
|
|
|
|
{"text": ","},
|
|
|
|
{"key": "protocol"},
|
|
|
|
{"text": ":"},
|
|
|
|
{"key": "dst-port"},
|
|
|
|
],
|
|
|
|
],
|
|
|
|
skip=[
|
|
|
|
{"name": "dynamic", "value": True},
|
|
|
|
{"name": "action", "value": "jump"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
# Remove duplicate filter entries to prevent crash
|
|
|
|
filter_uniq = {}
|
|
|
|
filter_del = {}
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid in self.ds["filter"]:
|
|
|
|
self.ds["filter"][uid]["comment"] = str(self.ds["filter"][uid]["comment"])
|
2022-04-06 19:57:27 +02:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
tmp_name = self.ds["filter"][uid]["uniq-id"]
|
2021-04-12 12:40:45 +02:00
|
|
|
if tmp_name not in filter_uniq:
|
|
|
|
filter_uniq[tmp_name] = uid
|
|
|
|
else:
|
|
|
|
filter_del[uid] = 1
|
|
|
|
filter_del[filter_uniq[tmp_name]] = 1
|
|
|
|
|
|
|
|
for uid in filter_del:
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["filter"][uid]["uniq-id"] not in self.filter_removed:
|
|
|
|
self.filter_removed[self.ds["filter"][uid]["uniq-id"]] = 1
|
2021-04-12 12:40:45 +02:00
|
|
|
_LOGGER.error(
|
2022-05-23 15:43:14 +02:00
|
|
|
"Mikrotik %s duplicate Filter rule %s (ID %s), entity will be unavailable.",
|
2021-04-12 12:40:45 +02:00
|
|
|
self.host,
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["filter"][uid]["name"],
|
|
|
|
self.ds["filter"][uid][".id"],
|
2021-04-12 12:40:45 +02:00
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
del self.ds["filter"][uid]
|
2021-04-12 12:40:45 +02:00
|
|
|
|
2020-12-25 18:42:57 +01:00
|
|
|
# ---------------------------
|
|
|
|
# get_kidcontrol
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_kidcontrol(self) -> None:
|
2020-12-25 18:42:57 +01:00
|
|
|
"""Get Kid-control data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["kid-control"] = parse_api(
|
|
|
|
data=self.ds["kid-control"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/kid-control"),
|
2020-12-25 18:42:57 +01:00
|
|
|
key="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "name"},
|
|
|
|
{"name": "rate-limit"},
|
|
|
|
{"name": "mon", "default": "None"},
|
|
|
|
{"name": "tue", "default": "None"},
|
|
|
|
{"name": "wed", "default": "None"},
|
|
|
|
{"name": "thu", "default": "None"},
|
|
|
|
{"name": "fri", "default": "None"},
|
|
|
|
{"name": "sat", "default": "None"},
|
|
|
|
{"name": "sun", "default": "None"},
|
|
|
|
{"name": "comment"},
|
2022-09-30 00:36:55 +02:00
|
|
|
{"name": "blocked", "type": "bool", "default": False},
|
2020-12-28 01:26:46 +01:00
|
|
|
{"name": "paused", "type": "bool", "reverse": True},
|
2020-12-25 18:42:57 +01:00
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid in self.ds["kid-control"]:
|
|
|
|
self.ds["kid-control"][uid]["comment"] = str(
|
|
|
|
self.ds["kid-control"][uid]["comment"]
|
2022-04-06 19:57:27 +02:00
|
|
|
)
|
|
|
|
|
2020-12-25 11:48:07 +01:00
|
|
|
# ---------------------------
|
|
|
|
# get_ppp
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_ppp(self) -> None:
|
2020-12-25 11:48:07 +01:00
|
|
|
"""Get PPP data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["ppp_secret"] = parse_api(
|
|
|
|
data=self.ds["ppp_secret"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ppp/secret"),
|
2020-12-25 11:48:07 +01:00
|
|
|
key="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "name"},
|
|
|
|
{"name": "service"},
|
|
|
|
{"name": "profile"},
|
|
|
|
{"name": "comment"},
|
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
ensure_vals=[
|
2020-12-25 20:27:37 +01:00
|
|
|
{"name": "caller-id", "default": ""},
|
2022-03-15 10:31:48 +01:00
|
|
|
{"name": "address", "default": ""},
|
2020-12-25 20:27:37 +01:00
|
|
|
{"name": "encoding", "default": ""},
|
2020-12-25 11:48:07 +01:00
|
|
|
{"name": "connected", "default": False},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["ppp_active"] = parse_api(
|
2020-12-25 11:48:07 +01:00
|
|
|
data={},
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ppp/active"),
|
2020-12-25 11:48:07 +01:00
|
|
|
key="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "name"},
|
|
|
|
{"name": "service"},
|
|
|
|
{"name": "caller-id"},
|
2022-03-15 10:31:48 +01:00
|
|
|
{"name": "address"},
|
2020-12-25 11:48:07 +01:00
|
|
|
{"name": "encoding"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid in self.ds["ppp_secret"]:
|
|
|
|
self.ds["ppp_secret"][uid]["comment"] = str(
|
|
|
|
self.ds["ppp_secret"][uid]["comment"]
|
2022-04-06 19:57:27 +02:00
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["ppp_secret"][uid]["name"] in self.ds["ppp_active"]:
|
|
|
|
self.ds["ppp_secret"][uid]["connected"] = True
|
|
|
|
self.ds["ppp_secret"][uid]["caller-id"] = self.ds["ppp_active"][uid][
|
|
|
|
"caller-id"
|
|
|
|
]
|
|
|
|
self.ds["ppp_secret"][uid]["address"] = self.ds["ppp_active"][uid][
|
2022-03-15 10:31:48 +01:00
|
|
|
"address"
|
|
|
|
]
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["ppp_secret"][uid]["encoding"] = self.ds["ppp_active"][uid][
|
2020-12-25 11:48:07 +01:00
|
|
|
"encoding"
|
|
|
|
]
|
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["ppp_secret"][uid]["connected"] = False
|
|
|
|
self.ds["ppp_secret"][uid]["caller-id"] = "not connected"
|
|
|
|
self.ds["ppp_secret"][uid]["address"] = "not connected"
|
|
|
|
self.ds["ppp_secret"][uid]["encoding"] = "not connected"
|
2020-12-25 11:48:07 +01:00
|
|
|
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
|
|
|
# get_system_routerboard
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_system_routerboard(self) -> None:
|
2019-12-05 20:42:14 +01:00
|
|
|
"""Get routerboard data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["resource"]["board-name"] in ("x86", "CHR"):
|
|
|
|
self.ds["routerboard"]["routerboard"] = False
|
|
|
|
self.ds["routerboard"]["model"] = self.ds["resource"]["board-name"]
|
|
|
|
self.ds["routerboard"]["serial-number"] = "N/A"
|
2022-02-18 12:29:41 +01:00
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["routerboard"] = parse_api(
|
|
|
|
data=self.ds["routerboard"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/system/routerboard"),
|
2022-02-18 12:29:41 +01:00
|
|
|
vals=[
|
|
|
|
{"name": "routerboard", "type": "bool"},
|
|
|
|
{"name": "model", "default": "unknown"},
|
|
|
|
{"name": "serial-number", "default": "unknown"},
|
2022-08-11 14:58:30 +02:00
|
|
|
{"name": "current-firmware", "default": "unknown"},
|
|
|
|
{"name": "upgrade-firmware", "default": "unknown"},
|
2022-02-18 12:29:41 +01:00
|
|
|
],
|
|
|
|
)
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2022-08-20 01:42:03 +02:00
|
|
|
if (
|
2023-08-09 23:00:00 +02:00
|
|
|
"write" not in self.ds["access"]
|
|
|
|
or "policy" not in self.ds["access"]
|
|
|
|
or "reboot" not in self.ds["access"]
|
2022-08-20 01:42:03 +02:00
|
|
|
):
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["routerboard"].pop("current-firmware")
|
|
|
|
self.ds["routerboard"].pop("upgrade-firmware")
|
2022-08-20 01:42:03 +02:00
|
|
|
|
2020-05-27 20:04:09 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_system_health
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_system_health(self) -> None:
|
2020-05-27 20:04:09 +02:00
|
|
|
"""Get routerboard data from Mikrotik"""
|
2022-08-20 01:42:03 +02:00
|
|
|
if (
|
2023-08-09 23:00:00 +02:00
|
|
|
"write" not in self.ds["access"]
|
|
|
|
or "policy" not in self.ds["access"]
|
|
|
|
or "reboot" not in self.ds["access"]
|
2022-08-20 01:42:03 +02:00
|
|
|
):
|
|
|
|
return
|
|
|
|
|
2022-01-31 09:57:16 +01:00
|
|
|
if 0 < self.major_fw_version < 7:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["health"] = parse_api(
|
|
|
|
data=self.ds["health"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/system/health"),
|
2022-01-08 22:07:31 +01:00
|
|
|
vals=[
|
|
|
|
{"name": "temperature", "default": "unknown"},
|
|
|
|
{"name": "voltage", "default": "unknown"},
|
|
|
|
{"name": "cpu-temperature", "default": "unknown"},
|
|
|
|
{"name": "power-consumption", "default": "unknown"},
|
|
|
|
{"name": "board-temperature1", "default": "unknown"},
|
|
|
|
{"name": "fan1-speed", "default": "unknown"},
|
|
|
|
{"name": "fan2-speed", "default": "unknown"},
|
|
|
|
],
|
|
|
|
)
|
2022-01-31 09:57:16 +01:00
|
|
|
elif 0 < self.major_fw_version >= 7:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["health7"] = parse_api(
|
|
|
|
data=self.ds["health7"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/system/health"),
|
2022-01-31 09:57:16 +01:00
|
|
|
key="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "value", "default": "unknown"},
|
|
|
|
],
|
|
|
|
)
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["health7"].items():
|
|
|
|
self.ds["health"][uid] = vals["value"]
|
2020-05-27 20:04:09 +02:00
|
|
|
|
2019-12-02 18:13:55 +01:00
|
|
|
# ---------------------------
|
|
|
|
# get_system_resource
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_system_resource(self) -> None:
|
2019-12-05 20:42:14 +01:00
|
|
|
"""Get system resources data from Mikrotik"""
|
2022-02-18 10:42:10 +01:00
|
|
|
tmp_rebootcheck = 0
|
2023-08-09 23:00:00 +02:00
|
|
|
if "uptime_epoch" in self.ds["resource"]:
|
|
|
|
tmp_rebootcheck = self.ds["resource"]["uptime_epoch"]
|
2022-02-18 10:42:10 +01:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["resource"] = parse_api(
|
|
|
|
data=self.ds["resource"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/system/resource"),
|
2019-12-11 15:34:35 +01:00
|
|
|
vals=[
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": "platform", "default": "unknown"},
|
|
|
|
{"name": "board-name", "default": "unknown"},
|
|
|
|
{"name": "version", "default": "unknown"},
|
2020-12-02 10:52:34 +01:00
|
|
|
{"name": "uptime_str", "source": "uptime", "default": "unknown"},
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": "cpu-load", "default": "unknown"},
|
|
|
|
{"name": "free-memory", "default": 0},
|
|
|
|
{"name": "total-memory", "default": 0},
|
|
|
|
{"name": "free-hdd-space", "default": 0},
|
|
|
|
{"name": "total-hdd-space", "default": 0},
|
|
|
|
],
|
2020-12-02 10:52:34 +01:00
|
|
|
ensure_vals=[
|
|
|
|
{"name": "uptime", "default": 0},
|
2022-02-18 10:42:10 +01:00
|
|
|
{"name": "uptime_epoch", "default": 0},
|
2022-02-18 12:21:25 +01:00
|
|
|
{"name": "clients_wired", "default": 0},
|
|
|
|
{"name": "clients_wireless", "default": 0},
|
2022-06-25 15:18:00 +02:00
|
|
|
{"name": "captive_authorized", "default": 0},
|
2020-12-02 10:52:34 +01:00
|
|
|
],
|
2019-12-11 15:34:35 +01:00
|
|
|
)
|
2019-12-08 22:49:36 +01:00
|
|
|
|
2020-12-13 02:47:03 +01:00
|
|
|
tmp_uptime = 0
|
2023-08-09 23:00:00 +02:00
|
|
|
tmp = re.split(r"(\d+)[s]", self.ds["resource"]["uptime_str"])
|
2020-12-13 02:47:03 +01:00
|
|
|
if len(tmp) > 1:
|
|
|
|
tmp_uptime += int(tmp[1])
|
2023-08-09 23:00:00 +02:00
|
|
|
tmp = re.split(r"(\d+)[m]", self.ds["resource"]["uptime_str"])
|
2021-05-14 10:16:06 +02:00
|
|
|
if len(tmp) > 1:
|
|
|
|
tmp_uptime += int(tmp[1]) * 60
|
2023-08-09 23:00:00 +02:00
|
|
|
tmp = re.split(r"(\d+)[h]", self.ds["resource"]["uptime_str"])
|
2021-05-14 10:16:06 +02:00
|
|
|
if len(tmp) > 1:
|
|
|
|
tmp_uptime += int(tmp[1]) * 3600
|
2023-08-09 23:00:00 +02:00
|
|
|
tmp = re.split(r"(\d+)[d]", self.ds["resource"]["uptime_str"])
|
2020-12-13 02:47:03 +01:00
|
|
|
if len(tmp) > 1:
|
2021-05-14 10:16:06 +02:00
|
|
|
tmp_uptime += int(tmp[1]) * 86400
|
2023-08-09 23:00:00 +02:00
|
|
|
tmp = re.split(r"(\d+)[w]", self.ds["resource"]["uptime_str"])
|
2020-12-13 02:47:03 +01:00
|
|
|
if len(tmp) > 1:
|
2021-05-14 10:16:06 +02:00
|
|
|
tmp_uptime += int(tmp[1]) * 604800
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["resource"]["uptime_epoch"] = tmp_uptime
|
2021-05-14 10:16:06 +02:00
|
|
|
now = datetime.now().replace(microsecond=0)
|
|
|
|
uptime_tm = datetime.timestamp(now - timedelta(seconds=tmp_uptime))
|
2021-05-14 13:11:16 +02:00
|
|
|
update_uptime = False
|
2023-08-09 23:00:00 +02:00
|
|
|
if not self.ds["resource"]["uptime"]:
|
2021-05-14 13:11:16 +02:00
|
|
|
update_uptime = True
|
2021-05-14 10:16:06 +02:00
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
uptime_old = datetime.timestamp(self.ds["resource"]["uptime"])
|
2021-05-14 13:11:16 +02:00
|
|
|
if uptime_tm > uptime_old + 10:
|
|
|
|
update_uptime = True
|
2020-12-13 02:47:03 +01:00
|
|
|
|
2021-05-14 10:16:06 +02:00
|
|
|
if update_uptime:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["resource"]["uptime"] = utc_from_timestamp(uptime_tm)
|
2020-12-02 10:52:34 +01:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["resource"]["total-memory"] > 0:
|
|
|
|
self.ds["resource"]["memory-usage"] = round(
|
2020-03-16 04:51:41 +01:00
|
|
|
(
|
|
|
|
(
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["resource"]["total-memory"]
|
|
|
|
- self.ds["resource"]["free-memory"]
|
2020-03-16 04:51:41 +01:00
|
|
|
)
|
2023-08-09 23:00:00 +02:00
|
|
|
/ self.ds["resource"]["total-memory"]
|
2020-03-16 04:51:41 +01:00
|
|
|
)
|
|
|
|
* 100
|
|
|
|
)
|
2019-12-12 08:37:28 +01:00
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["resource"]["memory-usage"] = "unknown"
|
2020-03-16 04:51:41 +01:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["resource"]["total-hdd-space"] > 0:
|
|
|
|
self.ds["resource"]["hdd-usage"] = round(
|
2020-03-16 04:51:41 +01:00
|
|
|
(
|
|
|
|
(
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["resource"]["total-hdd-space"]
|
|
|
|
- self.ds["resource"]["free-hdd-space"]
|
2020-03-16 04:51:41 +01:00
|
|
|
)
|
2023-08-09 23:00:00 +02:00
|
|
|
/ self.ds["resource"]["total-hdd-space"]
|
2020-03-16 04:51:41 +01:00
|
|
|
)
|
|
|
|
* 100
|
|
|
|
)
|
2019-12-12 08:37:28 +01:00
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["resource"]["hdd-usage"] = "unknown"
|
2019-12-05 20:45:04 +01:00
|
|
|
|
2022-03-26 01:38:46 +01:00
|
|
|
if (
|
2023-08-09 23:00:00 +02:00
|
|
|
"uptime_epoch" in self.ds["resource"]
|
|
|
|
and 0 < tmp_rebootcheck < self.ds["resource"]["uptime_epoch"]
|
2022-03-26 01:38:46 +01:00
|
|
|
):
|
|
|
|
self.get_firmware_update()
|
2022-02-18 10:42:10 +01:00
|
|
|
|
2019-12-04 16:09:30 +01:00
|
|
|
# ---------------------------
|
2020-04-07 13:33:48 +02:00
|
|
|
# get_firmware_update
|
2019-12-04 16:09:30 +01:00
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_firmware_update(self) -> None:
|
2019-12-05 20:42:14 +01:00
|
|
|
"""Check for firmware update on Mikrotik"""
|
2022-08-20 01:43:54 +02:00
|
|
|
if (
|
2023-08-09 23:00:00 +02:00
|
|
|
"write" not in self.ds["access"]
|
|
|
|
or "policy" not in self.ds["access"]
|
|
|
|
or "reboot" not in self.ds["access"]
|
2022-08-20 01:43:54 +02:00
|
|
|
):
|
2022-08-20 01:42:03 +02:00
|
|
|
return
|
|
|
|
|
2022-05-27 10:58:51 +02:00
|
|
|
self.execute("/system/package/update", "check-for-updates", None, None)
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["fw-update"] = parse_api(
|
|
|
|
data=self.ds["fw-update"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/system/package/update"),
|
2019-12-12 13:02:11 +01:00
|
|
|
vals=[
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": "status"},
|
|
|
|
{"name": "channel", "default": "unknown"},
|
|
|
|
{"name": "installed-version", "default": "unknown"},
|
|
|
|
{"name": "latest-version", "default": "unknown"},
|
|
|
|
],
|
2019-12-12 13:02:11 +01:00
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if "status" in self.ds["fw-update"]:
|
|
|
|
self.ds["fw-update"]["available"] = (
|
|
|
|
self.ds["fw-update"]["status"] == "New version is available"
|
2020-03-16 04:51:41 +01:00
|
|
|
)
|
2022-03-26 01:38:46 +01:00
|
|
|
|
2019-12-12 13:02:11 +01:00
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["fw-update"]["available"] = False
|
2019-12-12 13:02:11 +01:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["fw-update"]["installed-version"] != "unknown":
|
2020-12-12 12:54:51 +01:00
|
|
|
try:
|
|
|
|
self.major_fw_version = int(
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["fw-update"].get("installed-version").split(".")[0]
|
2020-12-12 12:54:51 +01:00
|
|
|
)
|
2022-04-06 16:19:56 +02:00
|
|
|
except Exception:
|
2020-12-12 12:54:51 +01:00
|
|
|
_LOGGER.error(
|
|
|
|
"Mikrotik %s unable to determine major FW version (%s).",
|
|
|
|
self.host,
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["fw-update"].get("installed-version"),
|
2020-12-12 12:54:51 +01:00
|
|
|
)
|
|
|
|
|
2022-06-28 21:26:49 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_ups
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_ups(self) -> None:
|
2022-06-28 21:26:49 +02:00
|
|
|
"""Get UPS info from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["ups"] = parse_api(
|
|
|
|
data=self.ds["ups"],
|
2022-06-28 21:26:49 +02:00
|
|
|
source=self.api.query("/system/ups"),
|
|
|
|
vals=[
|
|
|
|
{"name": "name", "default": "unknown"},
|
|
|
|
{"name": "offline-time", "default": "unknown"},
|
|
|
|
{"name": "min-runtime", "default": "unknown"},
|
|
|
|
{"name": "alarm-setting", "default": "unknown"},
|
|
|
|
{"name": "model", "default": "unknown"},
|
|
|
|
{"name": "serial", "default": "unknown"},
|
|
|
|
{"name": "manufacture-date", "default": "unknown"},
|
|
|
|
{"name": "nominal-battery-voltage", "default": "unknown"},
|
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
ensure_vals=[
|
|
|
|
{"name": "on-line", "type": "bool"},
|
|
|
|
{"name": "runtime-left", "default": "unknown"},
|
|
|
|
{"name": "battery-charge", "default": 0},
|
|
|
|
{"name": "battery-voltage", "default": 0.0},
|
|
|
|
{"name": "line-voltage", "default": 0},
|
|
|
|
{"name": "load", "default": 0},
|
|
|
|
{"name": "hid-self-test", "default": "unknown"},
|
|
|
|
],
|
|
|
|
)
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["ups"]["enabled"]:
|
|
|
|
self.ds["ups"] = parse_api(
|
|
|
|
data=self.ds["ups"],
|
2022-06-28 21:26:49 +02:00
|
|
|
source=self.api.query(
|
|
|
|
"/system/ups",
|
|
|
|
command="monitor",
|
|
|
|
args={".id": 0, "once": True},
|
|
|
|
),
|
|
|
|
vals=[
|
|
|
|
{"name": "on-line", "type": "bool"},
|
2022-06-28 21:57:24 +02:00
|
|
|
{"name": "runtime-left", "default": 0},
|
2022-06-28 21:26:49 +02:00
|
|
|
{"name": "battery-charge", "default": 0},
|
|
|
|
{"name": "battery-voltage", "default": 0.0},
|
|
|
|
{"name": "line-voltage", "default": 0},
|
|
|
|
{"name": "load", "default": 0},
|
|
|
|
{"name": "hid-self-test", "default": "unknown"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2022-06-28 21:57:24 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_gps
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_gps(self) -> None:
|
2022-06-28 21:57:24 +02:00
|
|
|
"""Get GPS data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["gps"] = parse_api(
|
|
|
|
data=self.ds["gps"],
|
2022-06-28 21:57:24 +02:00
|
|
|
source=self.api.query(
|
|
|
|
"/system/gps",
|
|
|
|
command="monitor",
|
|
|
|
args={"once": True},
|
|
|
|
),
|
|
|
|
vals=[
|
|
|
|
{"name": "valid", "type": "bool"},
|
|
|
|
{"name": "latitude", "default": "unknown"},
|
|
|
|
{"name": "longitude", "default": "unknown"},
|
|
|
|
{"name": "altitude", "default": "unknown"},
|
|
|
|
{"name": "speed", "default": "unknown"},
|
|
|
|
{"name": "destination-bearing", "default": "unknown"},
|
|
|
|
{"name": "true-bearing", "default": "unknown"},
|
|
|
|
{"name": "magnetic-bearing", "default": "unknown"},
|
|
|
|
{"name": "satellites", "default": 0},
|
|
|
|
{"name": "fix-quality", "default": 0},
|
|
|
|
{"name": "horizontal-dilution", "default": "unknown"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2019-12-04 20:13:11 +01:00
|
|
|
# ---------------------------
|
|
|
|
# get_script
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_script(self) -> None:
|
2019-12-05 20:42:14 +01:00
|
|
|
"""Get list of all scripts from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["script"] = parse_api(
|
|
|
|
data=self.ds["script"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/system/script"),
|
2020-03-16 04:51:41 +01:00
|
|
|
key="name",
|
2019-12-11 15:34:35 +01:00
|
|
|
vals=[
|
2020-03-16 04:51:41 +01:00
|
|
|
{"name": "name"},
|
|
|
|
{"name": "last-started", "default": "unknown"},
|
|
|
|
{"name": "run-count", "default": "unknown"},
|
|
|
|
],
|
2019-12-11 15:34:35 +01:00
|
|
|
)
|
2020-03-25 13:18:49 +01:00
|
|
|
|
2020-12-02 13:56:50 +01:00
|
|
|
# ---------------------------
|
|
|
|
# get_environment
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_environment(self) -> None:
|
2020-12-02 13:56:50 +01:00
|
|
|
"""Get list of all environment variables from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["environment"] = parse_api(
|
|
|
|
data=self.ds["environment"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/system/script/environment"),
|
2020-12-02 13:56:50 +01:00
|
|
|
key="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "name"},
|
|
|
|
{"name": "value"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2022-05-26 09:41:00 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_captive
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_captive(self) -> None:
|
2022-05-26 09:41:00 +02:00
|
|
|
"""Get list of all environment variables from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["hostspot_host"] = parse_api(
|
2022-05-26 09:41:00 +02:00
|
|
|
data={},
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/hotspot/host"),
|
2022-05-26 09:41:00 +02:00
|
|
|
key="mac-address",
|
|
|
|
vals=[
|
|
|
|
{"name": "mac-address"},
|
2022-06-29 14:07:57 +02:00
|
|
|
{"name": "authorized", "type": "bool"},
|
|
|
|
{"name": "bypassed", "type": "bool"},
|
2022-05-26 09:41:00 +02:00
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2022-06-25 15:18:00 +02:00
|
|
|
auth_hosts = sum(
|
|
|
|
1
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid in self.ds["hostspot_host"]
|
|
|
|
if self.ds["hostspot_host"][uid]["authorized"]
|
2022-06-25 15:18:00 +02:00
|
|
|
)
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["resource"]["captive_authorized"] = auth_hosts
|
2022-06-25 15:18:00 +02:00
|
|
|
|
2020-03-25 13:18:49 +01:00
|
|
|
# ---------------------------
|
|
|
|
# get_queue
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_queue(self) -> None:
|
2020-03-25 13:18:49 +01:00
|
|
|
"""Get Queue data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["queue"] = parse_api(
|
|
|
|
data=self.ds["queue"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/queue/simple"),
|
2020-03-25 13:18:49 +01:00
|
|
|
key="name",
|
|
|
|
vals=[
|
2020-03-25 13:28:52 +01:00
|
|
|
{"name": ".id"},
|
2020-03-25 13:18:49 +01:00
|
|
|
{"name": "name", "default": "unknown"},
|
|
|
|
{"name": "target", "default": "unknown"},
|
2020-05-03 15:33:51 +02:00
|
|
|
{"name": "rate", "default": "0/0"},
|
2020-03-25 13:18:49 +01:00
|
|
|
{"name": "max-limit", "default": "0/0"},
|
|
|
|
{"name": "limit-at", "default": "0/0"},
|
|
|
|
{"name": "burst-limit", "default": "0/0"},
|
|
|
|
{"name": "burst-threshold", "default": "0/0"},
|
|
|
|
{"name": "burst-time", "default": "0s/0s"},
|
|
|
|
{"name": "packet-marks", "default": "none"},
|
|
|
|
{"name": "parent", "default": "none"},
|
|
|
|
{"name": "comment"},
|
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
|
|
|
},
|
2020-04-11 05:45:36 +02:00
|
|
|
],
|
2020-03-25 13:18:49 +01:00
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["queue"].items():
|
|
|
|
self.ds["queue"][uid]["comment"] = str(self.ds["queue"][uid]["comment"])
|
2022-04-06 19:57:27 +02:00
|
|
|
|
2020-04-11 05:45:36 +02:00
|
|
|
upload_max_limit_bps, download_max_limit_bps = [
|
|
|
|
int(x) for x in vals["max-limit"].split("/")
|
|
|
|
]
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["queue"][uid]["upload-max-limit"] = f"{upload_max_limit_bps} bps"
|
|
|
|
self.ds["queue"][uid][
|
2020-04-11 05:45:36 +02:00
|
|
|
"download-max-limit"
|
2023-08-08 00:50:09 +02:00
|
|
|
] = f"{download_max_limit_bps} bps"
|
2020-04-11 05:45:36 +02:00
|
|
|
|
2020-05-03 15:33:51 +02:00
|
|
|
upload_rate_bps, download_rate_bps = [
|
|
|
|
int(x) for x in vals["rate"].split("/")
|
|
|
|
]
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["queue"][uid]["upload-rate"] = f"{upload_rate_bps} bps"
|
|
|
|
self.ds["queue"][uid]["download-rate"] = f"{download_rate_bps} bps"
|
2020-05-03 15:33:51 +02:00
|
|
|
|
2020-04-11 05:45:36 +02:00
|
|
|
upload_limit_at_bps, download_limit_at_bps = [
|
|
|
|
int(x) for x in vals["limit-at"].split("/")
|
|
|
|
]
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["queue"][uid]["upload-limit-at"] = f"{upload_limit_at_bps} bps"
|
|
|
|
self.ds["queue"][uid]["download-limit-at"] = f"{download_limit_at_bps} bps"
|
2020-04-11 05:45:36 +02:00
|
|
|
|
|
|
|
upload_burst_limit_bps, download_burst_limit_bps = [
|
|
|
|
int(x) for x in vals["burst-limit"].split("/")
|
|
|
|
]
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["queue"][uid][
|
2020-04-11 05:45:36 +02:00
|
|
|
"upload-burst-limit"
|
2023-08-08 00:50:09 +02:00
|
|
|
] = f"{upload_burst_limit_bps} bps"
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["queue"][uid][
|
2020-04-11 05:45:36 +02:00
|
|
|
"download-burst-limit"
|
2023-08-08 00:50:09 +02:00
|
|
|
] = f"{download_burst_limit_bps} bps"
|
2020-04-11 05:45:36 +02:00
|
|
|
|
|
|
|
upload_burst_threshold_bps, download_burst_threshold_bps = [
|
|
|
|
int(x) for x in vals["burst-threshold"].split("/")
|
|
|
|
]
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["queue"][uid][
|
2020-04-11 05:45:36 +02:00
|
|
|
"upload-burst-threshold"
|
2023-08-08 00:50:09 +02:00
|
|
|
] = f"{upload_burst_threshold_bps} bps"
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["queue"][uid][
|
2020-04-11 05:45:36 +02:00
|
|
|
"download-burst-threshold"
|
2023-08-08 00:50:09 +02:00
|
|
|
] = f"{download_burst_threshold_bps} bps"
|
2020-04-11 05:45:36 +02:00
|
|
|
|
|
|
|
upload_burst_time, download_burst_time = vals["burst-time"].split("/")
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["queue"][uid]["upload-burst-time"] = upload_burst_time
|
|
|
|
self.ds["queue"][uid]["download-burst-time"] = download_burst_time
|
2020-04-07 03:55:38 +02:00
|
|
|
|
2020-04-08 10:01:09 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_arp
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_arp(self) -> None:
|
2020-04-08 10:01:09 +02:00
|
|
|
"""Get ARP data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["arp"] = parse_api(
|
|
|
|
data=self.ds["arp"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/arp"),
|
2020-04-08 10:01:09 +02:00
|
|
|
key="mac-address",
|
2020-04-13 12:25:08 +02:00
|
|
|
vals=[{"name": "mac-address"}, {"name": "address"}, {"name": "interface"}],
|
|
|
|
ensure_vals=[{"name": "bridge", "default": ""}],
|
2020-04-08 10:01:09 +02:00
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["arp"].items():
|
|
|
|
if vals["interface"] in self.ds["bridge"] and uid in self.ds["bridge_host"]:
|
|
|
|
self.ds["arp"][uid]["bridge"] = vals["interface"]
|
|
|
|
self.ds["arp"][uid]["interface"] = self.ds["bridge_host"][uid][
|
2020-04-11 05:45:36 +02:00
|
|
|
"interface"
|
|
|
|
]
|
2020-04-11 02:32:33 +02:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["dhcp-client"]:
|
2022-03-26 01:38:46 +01:00
|
|
|
to_remove = [
|
|
|
|
uid
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["arp"].items()
|
|
|
|
if vals["interface"] in self.ds["dhcp-client"]
|
2022-03-26 01:38:46 +01:00
|
|
|
]
|
2022-03-03 11:10:41 +01:00
|
|
|
|
|
|
|
for uid in to_remove:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["arp"].pop(uid)
|
2022-03-03 11:10:41 +01:00
|
|
|
|
2020-04-08 09:15:26 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_dns
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_dns(self) -> None:
|
2020-04-08 09:15:26 +02:00
|
|
|
"""Get static DNS data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["dns"] = parse_api(
|
|
|
|
data=self.ds["dns"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/dns/static"),
|
2020-04-08 09:15:26 +02:00
|
|
|
key="name",
|
2021-04-12 13:23:05 +02:00
|
|
|
vals=[{"name": "name"}, {"name": "address"}, {"name": "comment"}],
|
2020-04-08 09:15:26 +02:00
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["dns"].items():
|
|
|
|
self.ds["dns"][uid]["comment"] = str(self.ds["dns"][uid]["comment"])
|
2022-04-06 19:57:27 +02:00
|
|
|
|
2020-04-07 03:55:38 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_dhcp
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_dhcp(self) -> None:
|
2020-04-07 03:55:38 +02:00
|
|
|
"""Get DHCP data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["dhcp"] = parse_api(
|
|
|
|
data=self.ds["dhcp"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/dhcp-server/lease"),
|
2020-04-07 03:55:38 +02:00
|
|
|
key="mac-address",
|
|
|
|
vals=[
|
|
|
|
{"name": "mac-address"},
|
2021-04-12 11:14:41 +02:00
|
|
|
{"name": "active-mac-address", "default": "unknown"},
|
2020-04-07 03:55:38 +02:00
|
|
|
{"name": "address", "default": "unknown"},
|
2021-04-12 11:00:08 +02:00
|
|
|
{"name": "active-address", "default": "unknown"},
|
2020-04-07 03:55:38 +02:00
|
|
|
{"name": "host-name", "default": "unknown"},
|
|
|
|
{"name": "status", "default": "unknown"},
|
|
|
|
{"name": "last-seen", "default": "unknown"},
|
|
|
|
{"name": "server", "default": "unknown"},
|
2020-04-08 16:31:26 +02:00
|
|
|
{"name": "comment", "default": ""},
|
2023-01-17 23:45:16 +01:00
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
|
|
|
},
|
2020-04-07 04:01:33 +02:00
|
|
|
],
|
2020-04-13 12:25:08 +02:00
|
|
|
ensure_vals=[{"name": "interface", "default": "unknown"}],
|
2020-04-07 03:55:38 +02:00
|
|
|
)
|
|
|
|
|
2020-04-13 06:43:02 +02:00
|
|
|
dhcpserver_query = False
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid in self.ds["dhcp"]:
|
|
|
|
self.ds["dhcp"][uid]["comment"] = str(self.ds["dhcp"][uid]["comment"])
|
2022-04-06 19:57:27 +02:00
|
|
|
|
2021-04-12 11:00:08 +02:00
|
|
|
# is_valid_ip
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["dhcp"][uid]["address"] != "unknown":
|
|
|
|
if not is_valid_ip(self.ds["dhcp"][uid]["address"]):
|
|
|
|
self.ds["dhcp"][uid]["address"] = "unknown"
|
2021-04-12 11:00:08 +02:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["dhcp"][uid]["active-address"] not in [
|
|
|
|
self.ds["dhcp"][uid]["address"],
|
2022-03-26 01:38:46 +01:00
|
|
|
"unknown",
|
|
|
|
]:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["dhcp"][uid]["address"] = self.ds["dhcp"][uid][
|
2021-04-12 11:00:08 +02:00
|
|
|
"active-address"
|
|
|
|
]
|
|
|
|
|
|
|
|
if (
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["dhcp"][uid]["mac-address"]
|
|
|
|
!= self.ds["dhcp"][uid]["active-mac-address"]
|
2022-03-26 01:38:46 +01:00
|
|
|
!= "unknown"
|
2021-04-12 11:00:08 +02:00
|
|
|
):
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["dhcp"][uid]["mac-address"] = self.ds["dhcp"][uid][
|
2021-04-12 11:00:08 +02:00
|
|
|
"active-mac-address"
|
|
|
|
]
|
|
|
|
|
2020-04-13 07:52:26 +02:00
|
|
|
if (
|
|
|
|
not dhcpserver_query
|
2023-08-09 23:00:00 +02:00
|
|
|
and self.ds["dhcp"][uid]["server"] not in self.ds["dhcp-server"]
|
2020-04-13 07:52:26 +02:00
|
|
|
):
|
2020-04-13 07:00:11 +02:00
|
|
|
self.get_dhcp_server()
|
2020-04-13 06:43:02 +02:00
|
|
|
dhcpserver_query = True
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["dhcp"][uid]["server"] in self.ds["dhcp-server"]:
|
|
|
|
self.ds["dhcp"][uid]["interface"] = self.ds["dhcp-server"][
|
|
|
|
self.ds["dhcp"][uid]["server"]
|
2020-04-13 06:09:50 +02:00
|
|
|
]["interface"]
|
2023-08-09 23:00:00 +02:00
|
|
|
elif uid in self.ds["arp"]:
|
|
|
|
if self.ds["arp"][uid]["bridge"] != "unknown":
|
|
|
|
self.ds["dhcp"][uid]["interface"] = self.ds["arp"][uid]["bridge"]
|
2020-04-13 06:09:50 +02:00
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["dhcp"][uid]["interface"] = self.ds["arp"][uid]["interface"]
|
2020-04-07 03:55:38 +02:00
|
|
|
|
2020-04-13 07:00:11 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_dhcp_server
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_dhcp_server(self) -> None:
|
2020-04-13 07:00:11 +02:00
|
|
|
"""Get DHCP server data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["dhcp-server"] = parse_api(
|
|
|
|
data=self.ds["dhcp-server"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/dhcp-server"),
|
2020-04-13 07:00:11 +02:00
|
|
|
key="name",
|
2022-08-11 13:23:51 +02:00
|
|
|
vals=[
|
|
|
|
{"name": "name"},
|
|
|
|
{"name": "interface", "default": "unknown"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# get_dhcp_client
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_dhcp_client(self) -> None:
|
2022-08-11 13:23:51 +02:00
|
|
|
"""Get DHCP client data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["dhcp-client"] = parse_api(
|
|
|
|
data=self.ds["dhcp-client"],
|
2022-08-11 13:23:51 +02:00
|
|
|
source=self.api.query("/ip/dhcp-client"),
|
|
|
|
key="interface",
|
|
|
|
vals=[
|
|
|
|
{"name": "interface", "default": "unknown"},
|
|
|
|
{"name": "status", "default": "unknown"},
|
|
|
|
],
|
2020-04-13 07:00:11 +02:00
|
|
|
)
|
|
|
|
|
2020-04-13 06:55:08 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_dhcp_network
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_dhcp_network(self) -> None:
|
2020-04-13 07:00:35 +02:00
|
|
|
"""Get DHCP network data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["dhcp-network"] = parse_api(
|
|
|
|
data=self.ds["dhcp-network"],
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/dhcp-server/network"),
|
2020-04-13 06:55:08 +02:00
|
|
|
key="address",
|
|
|
|
vals=[
|
|
|
|
{"name": "address"},
|
|
|
|
{"name": "gateway", "default": ""},
|
|
|
|
{"name": "netmask", "default": ""},
|
|
|
|
{"name": "dns-server", "default": ""},
|
|
|
|
{"name": "domain", "default": ""},
|
|
|
|
],
|
2020-04-13 12:25:08 +02:00
|
|
|
ensure_vals=[{"name": "address"}, {"name": "IPv4Network", "default": ""}],
|
2020-04-13 06:55:08 +02:00
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["dhcp-network"].items():
|
2020-04-13 06:55:08 +02:00
|
|
|
if vals["IPv4Network"] == "":
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["dhcp-network"][uid]["IPv4Network"] = IPv4Network(
|
2020-04-13 06:55:08 +02:00
|
|
|
vals["address"]
|
|
|
|
)
|
|
|
|
|
2020-04-09 22:09:38 +02:00
|
|
|
# ---------------------------
|
2020-04-09 22:24:48 +02:00
|
|
|
# get_capsman_hosts
|
2020-04-09 22:09:38 +02:00
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_capsman_hosts(self) -> None:
|
2020-04-09 22:24:48 +02:00
|
|
|
"""Get CAPS-MAN hosts data from Mikrotik"""
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["capsman_hosts"] = parse_api(
|
2020-04-10 01:38:15 +02:00
|
|
|
data={},
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/caps-man/registration-table"),
|
2020-04-09 22:09:38 +02:00
|
|
|
key="mac-address",
|
|
|
|
vals=[
|
|
|
|
{"name": "mac-address"},
|
|
|
|
{"name": "interface", "default": "unknown"},
|
|
|
|
{"name": "ssid", "default": "unknown"},
|
2020-04-11 05:45:36 +02:00
|
|
|
],
|
2020-04-09 22:09:38 +02:00
|
|
|
)
|
|
|
|
|
2022-08-21 22:27:49 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_wireless
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_wireless(self) -> None:
|
2022-08-21 22:27:49 +02:00
|
|
|
"""Get wireless data from Mikrotik"""
|
|
|
|
wifimodule = "wifiwave2" if self.support_wifiwave2 else "wireless"
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["wireless"] = parse_api(
|
|
|
|
data=self.ds["wireless"],
|
2022-08-21 22:27:49 +02:00
|
|
|
source=self.api.query(f"/interface/{wifimodule}"),
|
|
|
|
key="name",
|
|
|
|
vals=[
|
|
|
|
{"name": "master-interface", "default": ""},
|
|
|
|
{"name": "mac-address", "default": "unknown"},
|
|
|
|
{"name": "ssid", "default": "unknown"},
|
|
|
|
{"name": "mode", "default": "unknown"},
|
|
|
|
{"name": "radio-name", "default": "unknown"},
|
|
|
|
{"name": "interface-type", "default": "unknown"},
|
|
|
|
{"name": "country", "default": "unknown"},
|
|
|
|
{"name": "installation", "default": "unknown"},
|
|
|
|
{"name": "antenna-gain", "default": "unknown"},
|
|
|
|
{"name": "frequency", "default": "unknown"},
|
|
|
|
{"name": "band", "default": "unknown"},
|
|
|
|
{"name": "channel-width", "default": "unknown"},
|
|
|
|
{"name": "secondary-frequency", "default": "unknown"},
|
|
|
|
{"name": "wireless-protocol", "default": "unknown"},
|
|
|
|
{"name": "rate-set", "default": "unknown"},
|
|
|
|
{"name": "distance", "default": "unknown"},
|
|
|
|
{"name": "tx-power-mode", "default": "unknown"},
|
|
|
|
{"name": "vlan-id", "default": "unknown"},
|
|
|
|
{"name": "wds-mode", "default": "unknown"},
|
|
|
|
{"name": "wds-default-bridge", "default": "unknown"},
|
|
|
|
{"name": "bridge-mode", "default": "unknown"},
|
|
|
|
{"name": "hide-ssid", "type": "bool"},
|
|
|
|
{"name": "running", "type": "bool"},
|
|
|
|
{"name": "disabled", "type": "bool"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid in self.ds["wireless"]:
|
|
|
|
if self.ds["wireless"][uid]["master-interface"]:
|
|
|
|
for tmp in self.ds["wireless"][uid]:
|
|
|
|
if self.ds["wireless"][uid][tmp] == "unknown":
|
|
|
|
self.ds["wireless"][uid][tmp] = self.ds["wireless"][
|
|
|
|
self.ds["wireless"][uid]["master-interface"]
|
2022-08-21 22:27:49 +02:00
|
|
|
][tmp]
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if uid in self.ds["interface"]:
|
|
|
|
for tmp in self.ds["wireless"][uid]:
|
|
|
|
self.ds["interface"][uid][tmp] = self.ds["wireless"][uid][tmp]
|
2022-08-21 22:27:49 +02:00
|
|
|
|
2020-04-09 22:24:48 +02:00
|
|
|
# ---------------------------
|
|
|
|
# get_wireless_hosts
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def get_wireless_hosts(self) -> None:
|
2020-04-09 22:24:48 +02:00
|
|
|
"""Get wireless hosts data from Mikrotik"""
|
2022-03-26 01:38:46 +01:00
|
|
|
wifimodule = "wifiwave2" if self.support_wifiwave2 else "wireless"
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["wireless_hosts"] = parse_api(
|
2020-04-10 01:38:15 +02:00
|
|
|
data={},
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query(f"/interface/{wifimodule}/registration-table"),
|
2020-04-09 22:24:48 +02:00
|
|
|
key="mac-address",
|
|
|
|
vals=[
|
|
|
|
{"name": "mac-address"},
|
|
|
|
{"name": "interface", "default": "unknown"},
|
2020-04-10 01:21:20 +02:00
|
|
|
{"name": "ap", "type": "bool"},
|
2020-04-10 01:38:15 +02:00
|
|
|
{"name": "uptime"},
|
2020-04-11 05:45:36 +02:00
|
|
|
],
|
2020-04-09 22:24:48 +02:00
|
|
|
)
|
|
|
|
|
2020-04-08 07:15:14 +02:00
|
|
|
# ---------------------------
|
2020-04-11 05:14:39 +02:00
|
|
|
# async_process_host
|
2020-04-08 07:15:14 +02:00
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
async def async_process_host(self) -> None:
|
2020-04-08 07:15:14 +02:00
|
|
|
"""Get host tracking data"""
|
2020-04-09 22:09:38 +02:00
|
|
|
# Add hosts from CAPS-MAN
|
2020-04-11 05:45:36 +02:00
|
|
|
capsman_detected = {}
|
2020-04-09 22:09:38 +02:00
|
|
|
if self.support_capsman:
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["capsman_hosts"].items():
|
|
|
|
if uid not in self.ds["host"]:
|
|
|
|
self.ds["host"][uid] = {"source": "capsman"}
|
|
|
|
elif self.ds["host"][uid]["source"] != "capsman":
|
2022-05-18 21:28:53 +02:00
|
|
|
continue
|
2020-04-09 22:09:38 +02:00
|
|
|
|
|
|
|
capsman_detected[uid] = True
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["available"] = True
|
|
|
|
self.ds["host"][uid]["last-seen"] = utcnow()
|
2020-04-11 00:02:50 +02:00
|
|
|
for key in ["mac-address", "interface"]:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid][key] = vals[key]
|
2020-04-09 22:09:38 +02:00
|
|
|
|
2020-04-09 22:24:48 +02:00
|
|
|
# Add hosts from wireless
|
2020-04-11 05:45:36 +02:00
|
|
|
wireless_detected = {}
|
2020-04-09 22:24:48 +02:00
|
|
|
if self.support_wireless:
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["wireless_hosts"].items():
|
2020-04-10 01:21:20 +02:00
|
|
|
if vals["ap"]:
|
2020-04-09 22:36:34 +02:00
|
|
|
continue
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if uid not in self.ds["host"]:
|
|
|
|
self.ds["host"][uid] = {"source": "wireless"}
|
|
|
|
elif self.ds["host"][uid]["source"] != "wireless":
|
2022-05-18 21:28:53 +02:00
|
|
|
continue
|
2020-04-09 22:24:48 +02:00
|
|
|
|
|
|
|
wireless_detected[uid] = True
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["available"] = True
|
|
|
|
self.ds["host"][uid]["last-seen"] = utcnow()
|
2020-04-11 00:02:50 +02:00
|
|
|
for key in ["mac-address", "interface"]:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid][key] = vals[key]
|
2020-04-09 22:24:48 +02:00
|
|
|
|
2020-04-08 09:17:25 +02:00
|
|
|
# Add hosts from DHCP
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["dhcp"].items():
|
2023-01-18 23:51:22 +01:00
|
|
|
if not vals["enabled"]:
|
2023-01-17 23:45:16 +01:00
|
|
|
continue
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
if uid not in self.ds["host"]:
|
|
|
|
self.ds["host"][uid] = {"source": "dhcp"}
|
|
|
|
elif self.ds["host"][uid]["source"] != "dhcp":
|
2022-05-18 21:28:53 +02:00
|
|
|
continue
|
|
|
|
|
|
|
|
for key in ["address", "mac-address", "interface"]:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid][key] = vals[key]
|
2020-04-08 10:32:02 +02:00
|
|
|
|
|
|
|
# Add hosts from ARP
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["arp"].items():
|
|
|
|
if uid not in self.ds["host"]:
|
|
|
|
self.ds["host"][uid] = {"source": "arp"}
|
|
|
|
elif self.ds["host"][uid]["source"] != "arp":
|
2022-05-18 21:28:53 +02:00
|
|
|
continue
|
|
|
|
|
|
|
|
for key in ["address", "mac-address", "interface"]:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid][key] = vals[key]
|
2020-04-08 09:17:25 +02:00
|
|
|
|
2020-04-11 00:02:50 +02:00
|
|
|
# Add restored hosts from hass registry
|
2020-04-09 02:53:43 +02:00
|
|
|
if not self.host_hass_recovered:
|
2020-04-11 00:02:50 +02:00
|
|
|
self.host_hass_recovered = True
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid in self.ds["host_hass"]:
|
|
|
|
if uid not in self.ds["host"]:
|
|
|
|
self.ds["host"][uid] = {"source": "restored"}
|
|
|
|
self.ds["host"][uid]["mac-address"] = uid
|
|
|
|
self.ds["host"][uid]["host-name"] = self.ds["host_hass"][uid]
|
2020-04-09 02:53:43 +02:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["host"].items():
|
2020-04-11 06:08:50 +02:00
|
|
|
# Add missing default values
|
|
|
|
for key, default in zip(
|
|
|
|
[
|
|
|
|
"address",
|
|
|
|
"mac-address",
|
|
|
|
"interface",
|
|
|
|
"host-name",
|
2020-12-03 16:41:06 +01:00
|
|
|
"manufacturer",
|
2020-04-11 06:08:50 +02:00
|
|
|
"last-seen",
|
|
|
|
"available",
|
|
|
|
],
|
2020-12-03 16:41:06 +01:00
|
|
|
["unknown", "unknown", "unknown", "unknown", "detect", False, False],
|
2020-04-11 06:08:50 +02:00
|
|
|
):
|
2023-08-09 23:00:00 +02:00
|
|
|
if key not in self.ds["host"][uid]:
|
|
|
|
self.ds["host"][uid][key] = default
|
2020-04-11 06:08:50 +02:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
# if not self.host_tracking_initialized:
|
|
|
|
# await self.async_ping_tracked_hosts()
|
2020-04-11 05:14:39 +02:00
|
|
|
|
2020-04-08 09:17:25 +02:00
|
|
|
# Process hosts
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["resource"]["clients_wired"] = 0
|
|
|
|
self.ds["resource"]["clients_wireless"] = 0
|
|
|
|
for uid, vals in self.ds["host"].items():
|
2022-05-26 09:41:00 +02:00
|
|
|
# Captive portal data
|
|
|
|
if self.option_sensor_client_captive:
|
2023-08-09 23:00:00 +02:00
|
|
|
if uid in self.ds["hostspot_host"]:
|
|
|
|
self.ds["host"][uid]["authorized"] = self.ds["hostspot_host"][uid][
|
|
|
|
"authorized"
|
|
|
|
]
|
|
|
|
self.ds["host"][uid]["bypassed"] = self.ds["hostspot_host"][uid][
|
|
|
|
"bypassed"
|
|
|
|
]
|
2022-05-26 09:41:00 +02:00
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
if "authorized" in self.ds["host"][uid]:
|
|
|
|
del self.ds["host"][uid]["authorized"]
|
|
|
|
del self.ds["host"][uid]["bypassed"]
|
2022-05-26 09:41:00 +02:00
|
|
|
|
2020-04-11 00:02:50 +02:00
|
|
|
# CAPS-MAN availability
|
2020-04-09 22:09:38 +02:00
|
|
|
if vals["source"] == "capsman" and uid not in capsman_detected:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["available"] = False
|
2020-04-09 22:09:38 +02:00
|
|
|
|
2020-04-11 00:02:50 +02:00
|
|
|
# Wireless availability
|
2020-04-09 22:24:48 +02:00
|
|
|
if vals["source"] == "wireless" and uid not in wireless_detected:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["available"] = False
|
2020-04-09 22:24:48 +02:00
|
|
|
|
2020-04-09 02:59:22 +02:00
|
|
|
# Update IP and interface (DHCP/returned host)
|
2023-01-20 09:35:59 +01:00
|
|
|
if (
|
2023-08-09 23:00:00 +02:00
|
|
|
uid in self.ds["dhcp"]
|
|
|
|
and self.ds["dhcp"][uid]["enabled"]
|
|
|
|
and "." in self.ds["dhcp"][uid]["address"]
|
2023-01-20 09:35:59 +01:00
|
|
|
):
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["dhcp"][uid]["address"] != self.ds["host"][uid]["address"]:
|
|
|
|
self.ds["host"][uid]["address"] = self.ds["dhcp"][uid]["address"]
|
2020-04-09 22:24:48 +02:00
|
|
|
if vals["source"] not in ["capsman", "wireless"]:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["source"] = "dhcp"
|
|
|
|
self.ds["host"][uid]["interface"] = self.ds["dhcp"][uid][
|
2020-04-11 05:45:36 +02:00
|
|
|
"interface"
|
|
|
|
]
|
|
|
|
|
|
|
|
elif (
|
2023-08-09 23:00:00 +02:00
|
|
|
uid in self.ds["arp"]
|
|
|
|
and "." in self.ds["arp"][uid]["address"]
|
|
|
|
and self.ds["arp"][uid]["address"] != self.ds["host"][uid]["address"]
|
2020-04-11 05:45:36 +02:00
|
|
|
):
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["address"] = self.ds["arp"][uid]["address"]
|
2020-04-09 22:24:48 +02:00
|
|
|
if vals["source"] not in ["capsman", "wireless"]:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["source"] = "arp"
|
|
|
|
self.ds["host"][uid]["interface"] = self.ds["arp"][uid]["interface"]
|
2020-04-09 22:09:38 +02:00
|
|
|
|
2020-04-08 16:53:20 +02:00
|
|
|
if vals["host-name"] == "unknown":
|
2020-04-11 00:02:50 +02:00
|
|
|
# Resolve hostname from static DNS
|
2020-04-08 09:17:25 +02:00
|
|
|
if vals["address"] != "unknown":
|
2023-08-09 23:00:00 +02:00
|
|
|
for dns_uid, dns_vals in self.ds["dns"].items():
|
2020-04-08 09:17:25 +02:00
|
|
|
if dns_vals["address"] == vals["address"]:
|
2021-09-24 22:07:35 +02:00
|
|
|
if dns_vals["comment"].split("#", 1)[0] != "":
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["host-name"] = dns_vals[
|
2021-04-12 13:23:05 +02:00
|
|
|
"comment"
|
|
|
|
].split("#", 1)[0]
|
2021-04-12 13:27:22 +02:00
|
|
|
elif (
|
2023-08-09 23:00:00 +02:00
|
|
|
uid in self.ds["dhcp"]
|
|
|
|
and self.ds["dhcp"][uid]["enabled"]
|
|
|
|
and self.ds["dhcp"][uid]["comment"].split("#", 1)[0]
|
2021-09-24 22:07:35 +02:00
|
|
|
!= ""
|
2021-04-12 13:27:22 +02:00
|
|
|
):
|
|
|
|
# Override name if DHCP comment exists
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["host-name"] = self.ds["dhcp"][
|
2021-04-12 13:27:22 +02:00
|
|
|
uid
|
|
|
|
]["comment"].split("#", 1)[0]
|
2021-04-12 13:23:05 +02:00
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["host-name"] = dns_vals[
|
2021-04-12 13:23:05 +02:00
|
|
|
"name"
|
|
|
|
].split(".")[0]
|
2020-04-08 09:17:25 +02:00
|
|
|
break
|
2021-04-12 13:27:22 +02:00
|
|
|
|
2020-04-11 00:02:50 +02:00
|
|
|
# Resolve hostname from DHCP comment
|
2020-04-11 05:45:36 +02:00
|
|
|
if (
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["host-name"] == "unknown"
|
|
|
|
and uid in self.ds["dhcp"]
|
|
|
|
and self.ds["dhcp"][uid]["enabled"]
|
|
|
|
and self.ds["dhcp"][uid]["comment"].split("#", 1)[0] != ""
|
2020-04-11 05:45:36 +02:00
|
|
|
):
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["host-name"] = self.ds["dhcp"][uid][
|
2020-04-11 05:45:36 +02:00
|
|
|
"comment"
|
2021-04-12 13:23:05 +02:00
|
|
|
].split("#", 1)[0]
|
2020-04-11 00:02:50 +02:00
|
|
|
# Resolve hostname from DHCP hostname
|
2020-04-11 05:45:36 +02:00
|
|
|
elif (
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["host-name"] == "unknown"
|
|
|
|
and uid in self.ds["dhcp"]
|
|
|
|
and self.ds["dhcp"][uid]["enabled"]
|
|
|
|
and self.ds["dhcp"][uid]["host-name"] != "unknown"
|
2020-04-11 05:45:36 +02:00
|
|
|
):
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["host-name"] = self.ds["dhcp"][uid][
|
2020-04-11 05:45:36 +02:00
|
|
|
"host-name"
|
|
|
|
]
|
2020-04-11 00:02:50 +02:00
|
|
|
# Fallback to mac address for hostname
|
2023-08-09 23:00:00 +02:00
|
|
|
elif self.ds["host"][uid]["host-name"] == "unknown":
|
|
|
|
self.ds["host"][uid]["host-name"] = uid
|
2020-04-08 09:17:25 +02:00
|
|
|
|
2020-12-03 16:41:06 +01:00
|
|
|
# Resolve manufacturer
|
2021-05-23 01:17:54 +02:00
|
|
|
if vals["manufacturer"] == "detect" and vals["mac-address"] != "unknown":
|
2020-12-03 16:41:06 +01:00
|
|
|
try:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid][
|
2020-12-03 16:41:06 +01:00
|
|
|
"manufacturer"
|
|
|
|
] = await self.async_mac_lookup.lookup(vals["mac-address"])
|
2022-04-06 16:19:56 +02:00
|
|
|
except Exception:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["manufacturer"] = ""
|
2020-12-03 16:41:06 +01:00
|
|
|
|
2021-05-23 01:17:54 +02:00
|
|
|
if vals["manufacturer"] == "detect":
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["host"][uid]["manufacturer"] = ""
|
2021-05-23 01:17:54 +02:00
|
|
|
|
2022-02-18 12:21:25 +01:00
|
|
|
# Count hosts
|
2023-08-09 23:00:00 +02:00
|
|
|
if self.ds["host"][uid]["available"]:
|
2022-02-18 12:21:25 +01:00
|
|
|
if vals["source"] in ["capsman", "wireless"]:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["resource"]["clients_wireless"] += 1
|
2022-02-18 12:21:25 +01:00
|
|
|
else:
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["resource"]["clients_wired"] += 1
|
2022-02-18 12:21:25 +01:00
|
|
|
|
2020-04-08 17:52:58 +02:00
|
|
|
# ---------------------------
|
2020-04-11 01:10:56 +02:00
|
|
|
# process_accounting
|
2020-04-08 17:52:58 +02:00
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def process_accounting(self) -> None:
|
2020-04-04 19:42:05 +02:00
|
|
|
"""Get Accounting data from Mikrotik"""
|
2020-04-07 14:50:26 +02:00
|
|
|
# Check if accounting and account-local-traffic is enabled
|
2020-04-11 05:45:36 +02:00
|
|
|
(
|
|
|
|
accounting_enabled,
|
|
|
|
local_traffic_enabled,
|
|
|
|
) = self.api.is_accounting_and_local_traffic_enabled()
|
2020-04-07 14:50:26 +02:00
|
|
|
|
2020-04-08 13:41:03 +02:00
|
|
|
# Build missing hosts from main hosts dict
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["host"].items():
|
|
|
|
if uid not in self.ds["client_traffic"]:
|
|
|
|
self.ds["client_traffic"][uid] = {
|
2020-04-11 05:45:36 +02:00
|
|
|
"address": vals["address"],
|
|
|
|
"mac-address": vals["mac-address"],
|
|
|
|
"host-name": vals["host-name"],
|
|
|
|
"available": False,
|
|
|
|
"local_accounting": False,
|
2020-04-07 14:50:26 +02:00
|
|
|
}
|
|
|
|
|
2022-01-20 12:11:33 +01:00
|
|
|
_LOGGER.debug(
|
2023-08-09 23:00:00 +02:00
|
|
|
f"Working with {len(self.ds['client_traffic'])} accounting devices"
|
2022-01-20 12:11:33 +01:00
|
|
|
)
|
2020-04-04 19:42:05 +02:00
|
|
|
|
2020-04-07 11:32:38 +02:00
|
|
|
# Build temp accounting values dict with ip address as key
|
2022-04-06 16:20:06 +02:00
|
|
|
tmp_accounting_values = {
|
|
|
|
vals["address"]: {
|
2020-04-05 16:04:17 +02:00
|
|
|
"wan-tx": 0,
|
|
|
|
"wan-rx": 0,
|
|
|
|
"lan-tx": 0,
|
2020-04-11 05:45:36 +02:00
|
|
|
"lan-rx": 0,
|
2020-04-05 16:04:17 +02:00
|
|
|
}
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["client_traffic"].items()
|
2022-04-06 16:20:06 +02:00
|
|
|
}
|
2020-04-04 19:42:05 +02:00
|
|
|
|
2022-01-01 17:26:40 +00:00
|
|
|
time_diff = self.api.take_client_traffic_snapshot(True)
|
2020-04-04 19:42:05 +02:00
|
|
|
if time_diff:
|
|
|
|
accounting_data = parse_api(
|
|
|
|
data={},
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/accounting/snapshot"),
|
2020-04-04 19:42:05 +02:00
|
|
|
key=".id",
|
|
|
|
vals=[
|
|
|
|
{"name": ".id"},
|
|
|
|
{"name": "src-address"},
|
|
|
|
{"name": "dst-address"},
|
|
|
|
{"name": "bytes", "default": 0},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2022-06-28 20:28:00 +02:00
|
|
|
threshold = self.api.query("/ip/accounting")[0].get("threshold")
|
2020-05-11 15:06:54 +02:00
|
|
|
entry_count = len(accounting_data)
|
|
|
|
|
2022-04-06 16:20:06 +02:00
|
|
|
if entry_count == threshold:
|
2020-05-11 15:18:17 +02:00
|
|
|
_LOGGER.warning(
|
|
|
|
f"Accounting entries count reached the threshold of {threshold}!"
|
|
|
|
" Some entries were not saved by Mikrotik so accounting calculation won't be correct."
|
|
|
|
" Consider shortening update interval or"
|
|
|
|
" increasing the accounting threshold value in Mikrotik."
|
|
|
|
)
|
2020-05-11 15:06:54 +02:00
|
|
|
elif entry_count > threshold * 0.9:
|
2020-05-11 15:18:17 +02:00
|
|
|
_LOGGER.info(
|
|
|
|
f"Accounting entries count ({entry_count} reached 90% of the threshold,"
|
|
|
|
f" currently set to {threshold}! Consider shortening update interval or"
|
|
|
|
" increasing the accounting threshold value in Mikrotik."
|
|
|
|
)
|
2020-05-11 15:06:54 +02:00
|
|
|
|
2020-04-04 19:42:05 +02:00
|
|
|
for item in accounting_data.values():
|
2020-04-11 05:45:36 +02:00
|
|
|
source_ip = str(item.get("src-address")).strip()
|
|
|
|
destination_ip = str(item.get("dst-address")).strip()
|
2023-08-08 00:50:09 +02:00
|
|
|
bits_count = int(str(item.get("bytes")).strip())
|
2020-04-04 19:42:05 +02:00
|
|
|
|
2020-04-11 05:45:36 +02:00
|
|
|
if self._address_part_of_local_network(
|
|
|
|
source_ip
|
|
|
|
) and self._address_part_of_local_network(destination_ip):
|
2020-04-04 19:42:05 +02:00
|
|
|
# LAN TX/RX
|
2020-04-05 18:32:48 +02:00
|
|
|
if source_ip in tmp_accounting_values:
|
2020-04-11 05:45:36 +02:00
|
|
|
tmp_accounting_values[source_ip]["lan-tx"] += bits_count
|
2020-04-05 18:32:48 +02:00
|
|
|
if destination_ip in tmp_accounting_values:
|
2020-04-11 05:45:36 +02:00
|
|
|
tmp_accounting_values[destination_ip]["lan-rx"] += bits_count
|
|
|
|
elif self._address_part_of_local_network(
|
|
|
|
source_ip
|
|
|
|
) and not self._address_part_of_local_network(destination_ip):
|
2020-04-04 19:42:05 +02:00
|
|
|
# WAN TX
|
2020-04-05 18:32:48 +02:00
|
|
|
if source_ip in tmp_accounting_values:
|
2020-04-11 05:45:36 +02:00
|
|
|
tmp_accounting_values[source_ip]["wan-tx"] += bits_count
|
|
|
|
elif (
|
|
|
|
not self._address_part_of_local_network(source_ip)
|
|
|
|
and self._address_part_of_local_network(destination_ip)
|
|
|
|
and destination_ip in tmp_accounting_values
|
|
|
|
):
|
2020-04-04 19:42:05 +02:00
|
|
|
# WAN RX
|
2020-04-11 05:45:36 +02:00
|
|
|
tmp_accounting_values[destination_ip]["wan-rx"] += bits_count
|
2020-04-08 13:41:03 +02:00
|
|
|
|
|
|
|
# Calculate real throughput and transform it to appropriate unit
|
|
|
|
# Also handle availability of accounting and local_accounting from Mikrotik
|
2020-04-11 00:02:50 +02:00
|
|
|
for addr, vals in tmp_accounting_values.items():
|
2020-04-08 15:44:28 +02:00
|
|
|
uid = self._get_accounting_uid_by_ip(addr)
|
2020-04-08 13:41:03 +02:00
|
|
|
if not uid:
|
2020-04-11 05:45:36 +02:00
|
|
|
_LOGGER.warning(
|
|
|
|
f"Address {addr} not found in accounting data, skipping update"
|
|
|
|
)
|
2020-04-08 13:41:03 +02:00
|
|
|
continue
|
2020-04-04 19:42:05 +02:00
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["client_traffic"][uid]["available"] = accounting_enabled
|
|
|
|
self.ds["client_traffic"][uid]["local_accounting"] = local_traffic_enabled
|
2020-04-07 11:32:38 +02:00
|
|
|
|
2020-04-08 13:41:03 +02:00
|
|
|
if not accounting_enabled:
|
2020-04-11 00:02:50 +02:00
|
|
|
# Skip calculation for WAN and LAN if accounting is disabled
|
2020-04-08 13:41:03 +02:00
|
|
|
continue
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["client_traffic"][uid]["wan-tx"] = (
|
2023-08-08 00:50:09 +02:00
|
|
|
round(vals["wan-tx"] / time_diff) if vals["wan-tx"] else 0.0
|
2020-04-11 05:45:36 +02:00
|
|
|
)
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["client_traffic"][uid]["wan-rx"] = (
|
2023-08-08 00:50:09 +02:00
|
|
|
round(vals["wan-rx"] / time_diff) if vals["wan-rx"] else 0.0
|
2020-04-11 05:45:36 +02:00
|
|
|
)
|
2020-04-08 15:44:28 +02:00
|
|
|
|
|
|
|
if not local_traffic_enabled:
|
2020-04-11 00:02:50 +02:00
|
|
|
# Skip calculation for LAN if LAN accounting is disabled
|
2020-04-08 15:44:28 +02:00
|
|
|
continue
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["client_traffic"][uid]["lan-tx"] = (
|
2023-08-08 00:50:09 +02:00
|
|
|
round(vals["lan-tx"] / time_diff) if vals["lan-tx"] else 0.0
|
2020-04-11 05:45:36 +02:00
|
|
|
)
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["client_traffic"][uid]["lan-rx"] = (
|
2023-08-08 00:50:09 +02:00
|
|
|
round(vals["lan-rx"] / time_diff) if vals["lan-rx"] else 0.0
|
2020-04-11 05:45:36 +02:00
|
|
|
)
|
2020-04-11 01:10:56 +02:00
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# _address_part_of_local_network
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def _address_part_of_local_network(self, address) -> bool:
|
2020-04-11 01:10:56 +02:00
|
|
|
address = ip_address(address)
|
2023-08-09 23:00:00 +02:00
|
|
|
for vals in self.ds["dhcp-network"].values():
|
2020-04-11 01:10:56 +02:00
|
|
|
if address in vals["IPv4Network"]:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# _get_accounting_uid_by_ip
|
|
|
|
# ---------------------------
|
|
|
|
def _get_accounting_uid_by_ip(self, requested_ip):
|
2023-08-09 23:00:00 +02:00
|
|
|
for mac, vals in self.ds["client_traffic"].items():
|
2020-04-11 05:45:36 +02:00
|
|
|
if vals.get("address") is requested_ip:
|
2020-04-11 01:10:56 +02:00
|
|
|
return mac
|
|
|
|
return None
|
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# _get_iface_from_entry
|
|
|
|
# ---------------------------
|
|
|
|
def _get_iface_from_entry(self, entry):
|
|
|
|
"""Get interface default-name using name from interface dict"""
|
|
|
|
uid = None
|
2023-08-09 23:00:00 +02:00
|
|
|
for ifacename in self.ds["interface"]:
|
|
|
|
if self.ds["interface"][ifacename]["name"] == entry["interface"]:
|
2020-04-11 01:10:56 +02:00
|
|
|
uid = ifacename
|
|
|
|
break
|
|
|
|
|
|
|
|
return uid
|
2022-01-01 17:26:40 +00:00
|
|
|
|
|
|
|
# ---------------------------
|
|
|
|
# process_kid_control
|
|
|
|
# ---------------------------
|
2023-08-08 00:50:09 +02:00
|
|
|
def process_kid_control_devices(self) -> None:
|
2022-01-01 17:26:40 +00:00
|
|
|
"""Get Kid Control Device data from Mikrotik"""
|
|
|
|
|
|
|
|
# Build missing hosts from main hosts dict
|
2023-08-09 23:00:00 +02:00
|
|
|
for uid, vals in self.ds["host"].items():
|
|
|
|
if uid not in self.ds["client_traffic"]:
|
|
|
|
self.ds["client_traffic"][uid] = {
|
2022-01-01 17:26:40 +00:00
|
|
|
"address": vals["address"],
|
|
|
|
"mac-address": vals["mac-address"],
|
|
|
|
"host-name": vals["host-name"],
|
|
|
|
"previous-bytes-up": 0.0,
|
|
|
|
"previous-bytes-down": 0.0,
|
2022-03-03 09:15:14 +01:00
|
|
|
"tx": 0.0,
|
|
|
|
"rx": 0.0,
|
2022-01-01 17:26:40 +00:00
|
|
|
"available": False,
|
|
|
|
"local_accounting": False,
|
|
|
|
}
|
|
|
|
|
2022-01-20 12:11:33 +01:00
|
|
|
_LOGGER.debug(
|
2023-08-09 23:00:00 +02:00
|
|
|
f"Working with {len(self.ds['client_traffic'])} kid control devices"
|
2022-01-20 12:11:33 +01:00
|
|
|
)
|
2022-01-01 17:26:40 +00:00
|
|
|
|
|
|
|
kid_control_devices_data = parse_api(
|
|
|
|
data={},
|
2022-06-28 20:28:00 +02:00
|
|
|
source=self.api.query("/ip/kid-control/device"),
|
2022-01-01 17:26:40 +00:00
|
|
|
key="mac-address",
|
|
|
|
vals=[
|
|
|
|
{"name": "mac-address"},
|
|
|
|
{"name": "bytes-down"},
|
|
|
|
{"name": "bytes-up"},
|
|
|
|
{
|
|
|
|
"name": "enabled",
|
|
|
|
"source": "disabled",
|
|
|
|
"type": "bool",
|
|
|
|
"reverse": True,
|
2022-01-20 12:11:33 +01:00
|
|
|
},
|
|
|
|
],
|
2022-01-01 17:26:40 +00:00
|
|
|
)
|
|
|
|
|
2022-01-03 13:22:15 +00:00
|
|
|
time_diff = self.api.take_client_traffic_snapshot(False)
|
|
|
|
|
2022-01-01 17:26:40 +00:00
|
|
|
if not kid_control_devices_data:
|
2022-01-03 08:06:25 +00:00
|
|
|
if "kid-control-devices" not in self.notified_flags:
|
2022-01-20 12:11:33 +01:00
|
|
|
_LOGGER.error(
|
2022-01-20 20:10:16 +01:00
|
|
|
"No kid control devices found on your Mikrotik device, make sure kid-control feature is configured"
|
2022-01-20 12:11:33 +01:00
|
|
|
)
|
2022-01-03 08:06:25 +00:00
|
|
|
self.notified_flags.append("kid-control-devices")
|
|
|
|
return
|
|
|
|
elif "kid-control-devices" in self.notified_flags:
|
|
|
|
self.notified_flags.remove("kid-control-devices")
|
2022-01-01 17:26:40 +00:00
|
|
|
|
|
|
|
for uid, vals in kid_control_devices_data.items():
|
2023-08-09 23:00:00 +02:00
|
|
|
if uid not in self.ds["client_traffic"]:
|
2022-01-01 17:26:40 +00:00
|
|
|
_LOGGER.debug(f"Skipping unknown device {uid}")
|
|
|
|
continue
|
|
|
|
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["client_traffic"][uid]["available"] = vals["enabled"]
|
2022-01-01 17:26:40 +00:00
|
|
|
|
2022-01-20 12:11:33 +01:00
|
|
|
current_tx = vals["bytes-up"]
|
2023-08-09 23:00:00 +02:00
|
|
|
previous_tx = self.ds["client_traffic"][uid]["previous-bytes-up"]
|
2022-01-03 13:22:15 +00:00
|
|
|
if time_diff:
|
2023-08-08 00:50:09 +02:00
|
|
|
delta_tx = max(0, current_tx - previous_tx)
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["client_traffic"][uid]["tx"] = round(delta_tx / time_diff)
|
|
|
|
self.ds["client_traffic"][uid]["previous-bytes-up"] = current_tx
|
2022-01-01 17:26:40 +00:00
|
|
|
|
2022-01-20 12:11:33 +01:00
|
|
|
current_rx = vals["bytes-down"]
|
2023-08-09 23:00:00 +02:00
|
|
|
previous_rx = self.ds["client_traffic"][uid]["previous-bytes-down"]
|
2022-01-03 13:22:15 +00:00
|
|
|
if time_diff:
|
2023-08-08 00:50:09 +02:00
|
|
|
delta_rx = max(0, current_rx - previous_rx)
|
2023-08-09 23:00:00 +02:00
|
|
|
self.ds["client_traffic"][uid]["rx"] = round(delta_rx / time_diff)
|
|
|
|
self.ds["client_traffic"][uid]["previous-bytes-down"] = current_rx
|