mirror of
https://github.com/tomaae/homeassistant-mikrotik_router.git
synced 2025-07-16 12:24:30 +02:00
Accounting feature renamed to Client traffic. In case of older firmware fallback to accounting fetature for client traffic monitoring, instead use kid-control-devices
This commit is contained in:
parent
0120409382
commit
e93b02e71d
3 changed files with 225 additions and 100 deletions
|
@ -129,8 +129,8 @@ class MikrotikControllerData:
|
|||
"wireless_hosts": {},
|
||||
"host": {},
|
||||
"host_hass": {},
|
||||
"accounting": {},
|
||||
"environment": {},
|
||||
"client_traffic": {},
|
||||
"environment": {}
|
||||
}
|
||||
|
||||
self.listeners = []
|
||||
|
@ -612,12 +612,13 @@ class MikrotikControllerData:
|
|||
if self.api.connected():
|
||||
await self.hass.async_add_executor_job(self.get_system_resource)
|
||||
|
||||
if (
|
||||
self.api.connected()
|
||||
and self.option_sensor_client_traffic
|
||||
and 0 < self.major_fw_version < 7
|
||||
):
|
||||
if self.api.connected() and self.option_sensor_client_traffic:
|
||||
if 0 < self.major_fw_version < 7:
|
||||
_LOGGER.info("Using accounting feature for client traffic processing")
|
||||
await self.hass.async_add_executor_job(self.process_accounting)
|
||||
elif 0 < self.major_fw_version >= 7:
|
||||
_LOGGER.info("Using accounting kid control devices for client traffic processing")
|
||||
await self.hass.async_add_executor_job(self.process_kid_control_devices)
|
||||
|
||||
if self.api.connected() and self.option_sensor_simple_queues:
|
||||
await self.hass.async_add_executor_job(self.get_queue)
|
||||
|
@ -1875,8 +1876,8 @@ class MikrotikControllerData:
|
|||
|
||||
# Build missing hosts from main hosts dict
|
||||
for uid, vals in self.data["host"].items():
|
||||
if uid not in self.data["accounting"]:
|
||||
self.data["accounting"][uid] = {
|
||||
if uid not in self.data["client_traffic"]:
|
||||
self.data["client_traffic"][uid] = {
|
||||
"address": vals["address"],
|
||||
"mac-address": vals["mac-address"],
|
||||
"host-name": vals["host-name"],
|
||||
|
@ -1885,11 +1886,11 @@ class MikrotikControllerData:
|
|||
"local_accounting": False,
|
||||
}
|
||||
|
||||
_LOGGER.debug(f"Working with {len(self.data['accounting'])} accounting devices")
|
||||
_LOGGER.debug(f"Working with {len(self.data['client_traffic'])} accounting devices")
|
||||
|
||||
# Build temp accounting values dict with ip address as key
|
||||
tmp_accounting_values = {}
|
||||
for uid, vals in self.data["accounting"].items():
|
||||
for uid, vals in self.data["client_traffic"].items():
|
||||
tmp_accounting_values[vals["address"]] = {
|
||||
"wan-tx": 0,
|
||||
"wan-rx": 0,
|
||||
|
@ -1897,7 +1898,7 @@ class MikrotikControllerData:
|
|||
"lan-rx": 0,
|
||||
}
|
||||
|
||||
time_diff = self.api.take_accounting_snapshot()
|
||||
time_diff = self.api.take_client_traffic_snapshot(True)
|
||||
if time_diff:
|
||||
accounting_data = parse_api(
|
||||
data={},
|
||||
|
@ -1965,20 +1966,20 @@ class MikrotikControllerData:
|
|||
)
|
||||
continue
|
||||
|
||||
self.data["accounting"][uid]["tx-rx-attr"] = uom_type
|
||||
self.data["accounting"][uid]["available"] = accounting_enabled
|
||||
self.data["accounting"][uid]["local_accounting"] = local_traffic_enabled
|
||||
self.data["client_traffic"][uid]["tx-rx-attr"] = uom_type
|
||||
self.data["client_traffic"][uid]["available"] = accounting_enabled
|
||||
self.data["client_traffic"][uid]["local_accounting"] = local_traffic_enabled
|
||||
|
||||
if not accounting_enabled:
|
||||
# Skip calculation for WAN and LAN if accounting is disabled
|
||||
continue
|
||||
|
||||
self.data["accounting"][uid]["wan-tx"] = (
|
||||
self.data["client_traffic"][uid]["wan-tx"] = (
|
||||
round(vals["wan-tx"] / time_diff * uom_div, 2)
|
||||
if vals["wan-tx"]
|
||||
else 0.0
|
||||
)
|
||||
self.data["accounting"][uid]["wan-rx"] = (
|
||||
self.data["client_traffic"][uid]["wan-rx"] = (
|
||||
round(vals["wan-rx"] / time_diff * uom_div, 2)
|
||||
if vals["wan-rx"]
|
||||
else 0.0
|
||||
|
@ -1988,12 +1989,12 @@ class MikrotikControllerData:
|
|||
# Skip calculation for LAN if LAN accounting is disabled
|
||||
continue
|
||||
|
||||
self.data["accounting"][uid]["lan-tx"] = (
|
||||
self.data["client_traffic"][uid]["lan-tx"] = (
|
||||
round(vals["lan-tx"] / time_diff * uom_div, 2)
|
||||
if vals["lan-tx"]
|
||||
else 0.0
|
||||
)
|
||||
self.data["accounting"][uid]["lan-rx"] = (
|
||||
self.data["client_traffic"][uid]["lan-rx"] = (
|
||||
round(vals["lan-rx"] / time_diff * uom_div, 2)
|
||||
if vals["lan-rx"]
|
||||
else 0.0
|
||||
|
@ -2033,7 +2034,7 @@ class MikrotikControllerData:
|
|||
# _get_accounting_uid_by_ip
|
||||
# ---------------------------
|
||||
def _get_accounting_uid_by_ip(self, requested_ip):
|
||||
for mac, vals in self.data["accounting"].items():
|
||||
for mac, vals in self.data["client_traffic"].items():
|
||||
if vals.get("address") is requested_ip:
|
||||
return mac
|
||||
return None
|
||||
|
@ -2050,3 +2051,125 @@ class MikrotikControllerData:
|
|||
break
|
||||
|
||||
return uid
|
||||
|
||||
# ---------------------------
|
||||
# process_kid_control
|
||||
# ---------------------------
|
||||
def process_kid_control_devices(self):
|
||||
"""Get Kid Control Device data from Mikrotik"""
|
||||
|
||||
uom_type, uom_div = self._get_unit_of_measurement()
|
||||
|
||||
# Build missing hosts from main hosts dict
|
||||
for uid, vals in self.data["host"].items():
|
||||
if uid not in self.data["client_traffic"]:
|
||||
self.data["client_traffic"][uid] = {
|
||||
"address": vals["address"],
|
||||
"mac-address": vals["mac-address"],
|
||||
"host-name": vals["host-name"],
|
||||
"previous-bytes-up": 0.0,
|
||||
"previous-bytes-down": 0.0,
|
||||
"wan-tx": 0.0,
|
||||
"wan-rx": 0.0,
|
||||
"tx-rx-attr": uom_type,
|
||||
"available": False,
|
||||
"local_accounting": False,
|
||||
}
|
||||
|
||||
_LOGGER.debug(f"Working with {len(self.data['client_traffic'])} kid control devices")
|
||||
|
||||
time_diff = self.api.take_client_traffic_snapshot(False)
|
||||
if not time_diff:
|
||||
return
|
||||
|
||||
kid_control_devices_data = parse_api(
|
||||
data={},
|
||||
source=self.api.path("/ip/kid-control/device"),
|
||||
key="mac-address",
|
||||
vals=[
|
||||
{"name": "mac-address"},
|
||||
{"name": "bytes-down"},
|
||||
{"name": "bytes-up"},
|
||||
{
|
||||
"name": "enabled",
|
||||
"source": "disabled",
|
||||
"type": "bool",
|
||||
"reverse": True,
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
if not kid_control_devices_data:
|
||||
_LOGGER.debug("No kid control devices found, make sure kid-control feature is configured")
|
||||
|
||||
for uid, vals in kid_control_devices_data.items():
|
||||
if uid not in self.data["client_traffic"]:
|
||||
_LOGGER.debug(f"Skipping unknown device {uid}")
|
||||
continue
|
||||
|
||||
self.data["client_traffic"][uid]["available"] = vals['enabled']
|
||||
|
||||
current_tx = vals['bytes-up']
|
||||
previous_tx = self.data["client_traffic"][uid]['previous-bytes-up']
|
||||
delta_tx = max(0, current_tx - previous_tx)
|
||||
self.data["client_traffic"][uid]['wan-tx'] = round(delta_tx / time_diff * uom_div, 2)
|
||||
self.data["client_traffic"][uid]['previous-bytes-up'] = current_tx
|
||||
|
||||
current_rx = vals['bytes-down']
|
||||
previous_rx = self.data["client_traffic"][uid]['previous-bytes-down']
|
||||
delta_rx = max(0, current_rx - previous_rx)
|
||||
self.data["client_traffic"][uid]['wan-rx'] = round(delta_rx / time_diff * uom_div, 2)
|
||||
self.data["client_traffic"][uid]['previous-bytes-down'] = current_rx
|
||||
|
||||
# ---------------------------
|
||||
# _get_unit_of_measurement
|
||||
# ---------------------------
|
||||
def _get_unit_of_measurement(self):
|
||||
uom_type = self.option_unit_of_measurement
|
||||
if uom_type == "Kbps":
|
||||
uom_div = 0.001
|
||||
elif uom_type == "Mbps":
|
||||
uom_div = 0.000001
|
||||
elif uom_type == "B/s":
|
||||
uom_div = 0.125
|
||||
elif uom_type == "KB/s":
|
||||
uom_div = 0.000125
|
||||
elif uom_type == "MB/s":
|
||||
uom_div = 0.000000125
|
||||
else:
|
||||
uom_type = "bps"
|
||||
uom_div = 1
|
||||
return uom_type, uom_div
|
||||
|
||||
# ---------------------------
|
||||
# _address_part_of_local_network
|
||||
# ---------------------------
|
||||
def _address_part_of_local_network(self, address):
|
||||
address = ip_address(address)
|
||||
for vals in self.data["dhcp-network"].values():
|
||||
if address in vals["IPv4Network"]:
|
||||
return True
|
||||
return False
|
||||
|
||||
# ---------------------------
|
||||
# _get_accounting_uid_by_ip
|
||||
# ---------------------------
|
||||
def _get_accounting_uid_by_ip(self, requested_ip):
|
||||
for mac, vals in self.data["client_traffic"].items():
|
||||
if vals.get("address") is requested_ip:
|
||||
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
|
||||
for ifacename in self.data["interface"]:
|
||||
if self.data["interface"][ifacename]["name"] == entry["interface"]:
|
||||
uid = ifacename
|
||||
break
|
||||
|
||||
return uid
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ class MikrotikAPI:
|
|||
self._connection_retry_sec = 58
|
||||
self.error = None
|
||||
self.connection_error_reported = False
|
||||
self.accounting_last_run = None
|
||||
self.client_traffic_last_run = None
|
||||
|
||||
# Default ports
|
||||
if not self._port:
|
||||
|
@ -644,14 +644,15 @@ class MikrotikAPI:
|
|||
return True, True
|
||||
|
||||
# ---------------------------
|
||||
# take_accounting_snapshot
|
||||
# take_client_traffic_snapshot
|
||||
# Returns float -> period in seconds between last and current run
|
||||
# ---------------------------
|
||||
def take_accounting_snapshot(self) -> float:
|
||||
"""Get accounting data"""
|
||||
def take_client_traffic_snapshot(self, use_accounting) -> float:
|
||||
"""Tako accounting snapshot and return time diff"""
|
||||
if not self.connection_check():
|
||||
return 0
|
||||
|
||||
if use_accounting:
|
||||
accounting = self.path("/ip/accounting", return_list=False)
|
||||
|
||||
self.lock.acquire()
|
||||
|
@ -697,11 +698,11 @@ class MikrotikAPI:
|
|||
|
||||
# First request will be discarded because we cannot know when the last data was retrieved
|
||||
# prevents spikes in data
|
||||
if not self.accounting_last_run:
|
||||
self.accounting_last_run = self._current_milliseconds()
|
||||
if not self.client_traffic_last_run:
|
||||
self.client_traffic_last_run = self._current_milliseconds()
|
||||
return 0
|
||||
|
||||
# Calculate time difference in seconds and return
|
||||
time_diff = self._current_milliseconds() - self.accounting_last_run
|
||||
self.accounting_last_run = self._current_milliseconds()
|
||||
time_diff = self._current_milliseconds() - self.client_traffic_last_run
|
||||
self.client_traffic_last_run = self._current_milliseconds()
|
||||
return time_diff / 1000
|
||||
|
|
|
@ -186,49 +186,49 @@ SENSOR_TYPES = {
|
|||
ATTR_ATTR: "rx-bits-per-second",
|
||||
ATTR_CTGR: None,
|
||||
},
|
||||
"accounting_lan_tx": {
|
||||
"client_traffic_lan_tx": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:upload-network",
|
||||
ATTR_LABEL: "LAN TX",
|
||||
ATTR_UNIT: "ps",
|
||||
ATTR_UNIT_ATTR: "tx-rx-attr",
|
||||
ATTR_PATH: "accounting",
|
||||
ATTR_PATH: "client_traffic",
|
||||
ATTR_ATTR: "lan-tx",
|
||||
ATTR_CTGR: None,
|
||||
},
|
||||
"accounting_lan_rx": {
|
||||
"client_traffic_lan_rx": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:download-network",
|
||||
ATTR_LABEL: "LAN RX",
|
||||
ATTR_UNIT: "ps",
|
||||
ATTR_UNIT_ATTR: "tx-rx-attr",
|
||||
ATTR_PATH: "accounting",
|
||||
ATTR_PATH: "client_traffic",
|
||||
ATTR_ATTR: "lan-rx",
|
||||
ATTR_CTGR: None,
|
||||
},
|
||||
"accounting_wan_tx": {
|
||||
"client_traffic_wan_tx": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:upload-network",
|
||||
ATTR_LABEL: "WAN TX",
|
||||
ATTR_UNIT: "ps",
|
||||
ATTR_UNIT_ATTR: "tx-rx-attr",
|
||||
ATTR_PATH: "accounting",
|
||||
ATTR_PATH: "client_traffic",
|
||||
ATTR_ATTR: "wan-tx",
|
||||
ATTR_CTGR: None,
|
||||
},
|
||||
"accounting_wan_rx": {
|
||||
"client_traffic_wan_rx": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:download-network",
|
||||
ATTR_LABEL: "WAN RX",
|
||||
ATTR_UNIT: "ps",
|
||||
ATTR_UNIT_ATTR: "tx-rx-attr",
|
||||
ATTR_PATH: "accounting",
|
||||
ATTR_PATH: "client_traffic",
|
||||
ATTR_ATTR: "wan-rx",
|
||||
ATTR_CTGR: None,
|
||||
},
|
||||
}
|
||||
|
||||
DEVICE_ATTRIBUTES_ACCOUNTING = ["address", "mac-address", "host-name"]
|
||||
DEVICE_ATTRIBUTES_CLIENT_TRAFFIC = ["address", "mac-address", "host-name"]
|
||||
|
||||
|
||||
# ---------------------------
|
||||
|
@ -327,6 +327,26 @@ def update_items(inst, config_entry, mikrotik_controller, async_add_entities, se
|
|||
)
|
||||
new_sensors.append(sensors[item_id])
|
||||
|
||||
if "client_traffic_" in sensor:
|
||||
for uid in mikrotik_controller.data["client_traffic"]:
|
||||
item_id = f"{inst}-{sensor}-{mikrotik_controller.data['client_traffic'][uid]['mac-address']}"
|
||||
if item_id in sensors:
|
||||
if sensors[item_id].enabled:
|
||||
sensors[item_id].async_schedule_update_ha_state()
|
||||
continue
|
||||
|
||||
if (
|
||||
SENSOR_TYPES[sensor][ATTR_ATTR]
|
||||
in mikrotik_controller.data["client_traffic"][uid].keys()
|
||||
):
|
||||
sensors[item_id] = MikrotikClientTrafficSensor(
|
||||
mikrotik_controller=mikrotik_controller,
|
||||
inst=inst,
|
||||
sensor=sensor,
|
||||
uid=uid,
|
||||
)
|
||||
new_sensors.append(sensors[item_id])
|
||||
|
||||
if "traffic_" in sensor:
|
||||
if not config_entry.options.get(
|
||||
CONF_SENSOR_PORT_TRAFFIC, DEFAULT_SENSOR_PORT_TRAFFIC
|
||||
|
@ -350,25 +370,6 @@ def update_items(inst, config_entry, mikrotik_controller, async_add_entities, se
|
|||
)
|
||||
new_sensors.append(sensors[item_id])
|
||||
|
||||
if "accounting_" in sensor:
|
||||
for uid in mikrotik_controller.data["accounting"]:
|
||||
item_id = f"{inst}-{sensor}-{mikrotik_controller.data['accounting'][uid]['mac-address']}"
|
||||
if item_id in sensors:
|
||||
if sensors[item_id].enabled:
|
||||
sensors[item_id].async_schedule_update_ha_state()
|
||||
continue
|
||||
|
||||
if (
|
||||
SENSOR_TYPES[sensor][ATTR_ATTR]
|
||||
in mikrotik_controller.data["accounting"][uid].keys()
|
||||
):
|
||||
sensors[item_id] = MikrotikAccountingSensor(
|
||||
mikrotik_controller=mikrotik_controller,
|
||||
inst=inst,
|
||||
sensor=sensor,
|
||||
uid=uid,
|
||||
)
|
||||
new_sensors.append(sensors[item_id])
|
||||
|
||||
if new_sensors:
|
||||
async_add_entities(new_sensors, True)
|
||||
|
@ -532,10 +533,10 @@ class MikrotikControllerTrafficSensor(MikrotikControllerSensor):
|
|||
|
||||
|
||||
# ---------------------------
|
||||
# MikrotikAccountingSensor
|
||||
# MikrotikClientTrafficSensor
|
||||
# ---------------------------
|
||||
class MikrotikAccountingSensor(MikrotikControllerSensor):
|
||||
"""Define an Mikrotik Accounting sensor."""
|
||||
class MikrotikClientTrafficSensor(MikrotikControllerSensor):
|
||||
"""Define an Mikrotik MikrotikClientTrafficSensor sensor."""
|
||||
|
||||
def __init__(self, mikrotik_controller, inst, sensor, uid):
|
||||
"""Initialize."""
|
||||
|
@ -583,7 +584,7 @@ class MikrotikAccountingSensor(MikrotikControllerSensor):
|
|||
def extra_state_attributes(self) -> Dict[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
attributes = self._attrs
|
||||
for variable in DEVICE_ATTRIBUTES_ACCOUNTING:
|
||||
for variable in DEVICE_ATTRIBUTES_CLIENT_TRAFFIC:
|
||||
if variable in self._data:
|
||||
attributes[format_attribute(variable)] = self._data[variable]
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue