mirror of
https://github.com/tomaae/homeassistant-mikrotik_router.git
synced 2025-06-28 19:50:06 +02:00
Removed redundant get_dhcp call from update method. Removed unnecessary calls to Mikrotik API for accounting hosts build, reusing new hosts dict. Removed unnecessary variables from mikrotik_controller.py. Reused new dhcp-networks dict. Simplified accounting throughput calculation. Added accounting 'available' and 'local_enabled' parameters for each host. Implemented new parameters on sensor availability.
This commit is contained in:
parent
eee634cfc8
commit
32b93cc8cc
2 changed files with 52 additions and 97 deletions
|
@ -69,14 +69,11 @@ class MikrotikControllerData:
|
||||||
"accounting": {}
|
"accounting": {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.local_dhcp_networks = []
|
|
||||||
|
|
||||||
self.listeners = []
|
self.listeners = []
|
||||||
self.lock = asyncio.Lock()
|
self.lock = asyncio.Lock()
|
||||||
|
|
||||||
self.api = MikrotikAPI(host, username, password, port, use_ssl)
|
self.api = MikrotikAPI(host, username, password, port, use_ssl)
|
||||||
|
|
||||||
self.raw_arp_entries = []
|
|
||||||
self.nat_removed = {}
|
self.nat_removed = {}
|
||||||
|
|
||||||
async_track_time_interval(
|
async_track_time_interval(
|
||||||
|
@ -209,7 +206,7 @@ class MikrotikControllerData:
|
||||||
await self.hass.async_add_executor_job(self.get_system_resource)
|
await self.hass.async_add_executor_job(self.get_system_resource)
|
||||||
await self.hass.async_add_executor_job(self.get_script)
|
await self.hass.async_add_executor_job(self.get_script)
|
||||||
await self.hass.async_add_executor_job(self.get_queue)
|
await self.hass.async_add_executor_job(self.get_queue)
|
||||||
await self.hass.async_add_executor_job(self.get_dhcp)
|
# await self.hass.async_add_executor_job(self.get_dhcp)
|
||||||
await self.hass.async_add_executor_job(self.get_accounting)
|
await self.hass.async_add_executor_job(self.get_accounting)
|
||||||
|
|
||||||
async_dispatcher_send(self.hass, self.signal_update)
|
async_dispatcher_send(self.hass, self.signal_update)
|
||||||
|
@ -354,7 +351,6 @@ class MikrotikControllerData:
|
||||||
def update_arp(self, mac2ip, bridge_used):
|
def update_arp(self, mac2ip, bridge_used):
|
||||||
"""Get list of hosts in ARP for interface client data from Mikrotik"""
|
"""Get list of hosts in ARP for interface client data from Mikrotik"""
|
||||||
data = self.api.path("/ip/arp")
|
data = self.api.path("/ip/arp")
|
||||||
self.raw_arp_entries = data
|
|
||||||
if not data:
|
if not data:
|
||||||
return mac2ip, bridge_used
|
return mac2ip, bridge_used
|
||||||
|
|
||||||
|
@ -856,7 +852,7 @@ class MikrotikControllerData:
|
||||||
|
|
||||||
def _address_part_of_local_network(self, address):
|
def _address_part_of_local_network(self, address):
|
||||||
address = ip_address(address)
|
address = ip_address(address)
|
||||||
for network in self.local_dhcp_networks:
|
for network in self.data['dhcp-networks']:
|
||||||
if address in network:
|
if address in network:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -874,80 +870,36 @@ class MikrotikControllerData:
|
||||||
traffic_type, traffic_div = self._get_traffic_type_and_div()
|
traffic_type, traffic_div = self._get_traffic_type_and_div()
|
||||||
|
|
||||||
if not accounting_enabled:
|
if not accounting_enabled:
|
||||||
# If any hosts were created return counters to 0 so sensors wont get stuck on last value
|
# If any hosts were created set them as unavailable
|
||||||
for mac, vals in self.data["accounting"].items():
|
for uid, vals in self.data["accounting"].items():
|
||||||
self.data['accounting'][mac]["tx-rx-attr"] = traffic_type
|
self.data['accounting'][uid]["tx-rx-attr"] = traffic_type
|
||||||
if 'wan-tx' in vals:
|
self.data['accounting'][uid]["available"] = False
|
||||||
self.data["accounting"][mac]['wan-tx'] = 0.0
|
self.data['accounting'][uid]["local_accounting"] = False
|
||||||
if 'wan-rx' in vals:
|
|
||||||
self.data["accounting"][mac]['wan-rx'] = 0.0
|
|
||||||
if 'lan-tx' in vals:
|
|
||||||
self.data["accounting"][mac]['lan-tx'] = 0.0
|
|
||||||
if 'lan-rx' in vals:
|
|
||||||
self.data["accounting"][mac]['lan-rx'] = 0.0
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Build missing hosts from already retrieved DHCP Server leases
|
# Build missing hosts from main hosts dict
|
||||||
for mac, vals in self.data["dhcp"].items():
|
for uid, vals in self.data["host"].items():
|
||||||
if mac not in self.data["accounting"]:
|
if uid not in self.data["accounting"]:
|
||||||
self.data["accounting"][mac] = {
|
self.data["accounting"][uid] = {
|
||||||
'address': vals['address'],
|
'address': vals['address'],
|
||||||
'mac-address': vals['mac-address'],
|
'mac-address': vals['mac-address'],
|
||||||
'host-name': vals['host-name'],
|
'host-name': vals['host-name'],
|
||||||
'comment': vals['comment']
|
'tx-rx-attr': traffic_type,
|
||||||
|
'available': False,
|
||||||
|
'local_accounting': False
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build missing hosts from already retrieved ARP list
|
|
||||||
host_update_from_arp = False
|
|
||||||
for entry in self.raw_arp_entries:
|
|
||||||
if entry['mac-address'] not in self.data["accounting"]:
|
|
||||||
self.data["accounting"][entry['mac-address']] = {
|
|
||||||
'address': entry['address'],
|
|
||||||
'mac-address': entry['mac-address'],
|
|
||||||
'host-name': '',
|
|
||||||
'comment': ''
|
|
||||||
}
|
|
||||||
host_update_from_arp = True
|
|
||||||
|
|
||||||
# If some host was added from ARP table build new host-name for it from static DNS entry. Fallback to MAC
|
|
||||||
if host_update_from_arp:
|
|
||||||
dns_data = parse_api(
|
|
||||||
data={},
|
|
||||||
source=self.api.path("/ip/dns/static"),
|
|
||||||
key="address",
|
|
||||||
vals=[
|
|
||||||
{"name": "address"},
|
|
||||||
{"name": "name"},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
# Try to build hostname from DNS static entry
|
|
||||||
for mac, vals in self.data["accounting"].items():
|
|
||||||
if not str(vals.get('host-name', '')).strip() or vals['host-name'] is 'unknown':
|
|
||||||
if vals['address'] in dns_data and str(dns_data[vals['address']].get('name', '')).strip():
|
|
||||||
self.data["accounting"][mac]['host-name'] = dns_data[vals['address']]['name']
|
|
||||||
|
|
||||||
# Check if any host still have empty 'host-name'. Default it to MAC.
|
|
||||||
# Same check for 'comment' (pretty name)
|
|
||||||
for mac, vals in self.data["accounting"].items():
|
|
||||||
if not str(vals.get('host-name', '')).strip() or vals['host-name'] is 'unknown':
|
|
||||||
self.data["accounting"][mac]['host-name'] = mac
|
|
||||||
if not str(vals.get('comment', '')).strip() or vals['host-name'] is 'comment':
|
|
||||||
self.data["accounting"][mac]['comment'] = mac
|
|
||||||
|
|
||||||
_LOGGER.debug(f"Working with {len(self.data['accounting'])} accounting devices")
|
_LOGGER.debug(f"Working with {len(self.data['accounting'])} accounting devices")
|
||||||
|
|
||||||
# Build temp accounting values dict with ip address as key
|
# Build temp accounting values dict with ip address as key
|
||||||
# Also set traffic type for each item
|
|
||||||
tmp_accounting_values = {}
|
tmp_accounting_values = {}
|
||||||
for mac, vals in self.data['accounting'].items():
|
for uid, vals in self.data['accounting'].items():
|
||||||
tmp_accounting_values[vals['address']] = {
|
tmp_accounting_values[vals['address']] = {
|
||||||
"wan-tx": 0,
|
"wan-tx": 0,
|
||||||
"wan-rx": 0,
|
"wan-rx": 0,
|
||||||
"lan-tx": 0,
|
"lan-tx": 0,
|
||||||
"lan-rx": 0
|
"lan-rx": 0
|
||||||
}
|
}
|
||||||
self.data['accounting'][mac]["tx-rx-attr"] = traffic_type
|
|
||||||
|
|
||||||
time_diff = self.api.take_accounting_snapshot()
|
time_diff = self.api.take_accounting_snapshot()
|
||||||
if time_diff:
|
if time_diff:
|
||||||
|
@ -984,42 +936,35 @@ class MikrotikControllerData:
|
||||||
# WAN RX
|
# WAN RX
|
||||||
if destination_ip in tmp_accounting_values:
|
if destination_ip in tmp_accounting_values:
|
||||||
tmp_accounting_values[destination_ip]['wan-rx'] += bits_count
|
tmp_accounting_values[destination_ip]['wan-rx'] += bits_count
|
||||||
|
else:
|
||||||
|
# No data to calculate, publish accounting as unavailable
|
||||||
|
accounting_enabled = False
|
||||||
|
|
||||||
# Now that we have sum of all traffic in bytes for given period
|
# Calculate real throughput and transform it to appropriate unit
|
||||||
# calculate real throughput and transform it to appropriate unit
|
# Also handle availability of accounting and local_accounting from Mikrotik
|
||||||
for addr in tmp_accounting_values:
|
for addr in tmp_accounting_values:
|
||||||
mac = self._get_accounting_mac_by_ip(addr)
|
uid = self._get_accounting_mac_by_ip(addr)
|
||||||
if not mac:
|
if not uid:
|
||||||
_LOGGER.debug(f"Address {addr} not found in accounting data, skipping update")
|
_LOGGER.warning(f"Address {addr} not found in accounting data, skipping update")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.data['accounting'][mac]['wan-tx'] = round(
|
self.data['accounting'][uid]['tx-rx-attr'] = traffic_type
|
||||||
|
self.data['accounting'][uid]['available'] = accounting_enabled
|
||||||
|
self.data['accounting'][uid]['local_accounting'] = local_traffic_enabled
|
||||||
|
|
||||||
|
if not accounting_enabled:
|
||||||
|
# No data present, do not calculate throughout
|
||||||
|
continue
|
||||||
|
|
||||||
|
if tmp_accounting_values[addr]['wan-tx']:
|
||||||
|
self.data['accounting'][uid]['wan-tx'] = round(
|
||||||
tmp_accounting_values[addr]['wan-tx'] / time_diff * traffic_div, 2)
|
tmp_accounting_values[addr]['wan-tx'] / time_diff * traffic_div, 2)
|
||||||
self.data['accounting'][mac]['wan-rx'] = round(
|
if tmp_accounting_values[addr]['wan-rx']:
|
||||||
|
self.data['accounting'][uid]['wan-rx'] = round(
|
||||||
tmp_accounting_values[addr]['wan-rx'] / time_diff * traffic_div, 2)
|
tmp_accounting_values[addr]['wan-rx'] / time_diff * traffic_div, 2)
|
||||||
|
|
||||||
if local_traffic_enabled:
|
if local_traffic_enabled:
|
||||||
self.data['accounting'][mac]['lan-tx'] = round(
|
self.data['accounting'][uid]['lan-tx'] = round(
|
||||||
tmp_accounting_values[addr]['lan-tx'] / time_diff * traffic_div, 2)
|
tmp_accounting_values[addr]['lan-tx'] / time_diff * traffic_div, 2)
|
||||||
self.data['accounting'][mac]['lan-rx'] = round(
|
self.data['accounting'][uid]['lan-rx'] = round(
|
||||||
tmp_accounting_values[addr]['lan-rx'] / time_diff * traffic_div, 2)
|
tmp_accounting_values[addr]['lan-rx'] / time_diff * traffic_div, 2)
|
||||||
else:
|
|
||||||
# If local traffic was enabled earlier and then disabled return counters for LAN traffic to 0
|
|
||||||
if 'lan-tx' in self.data['accounting'][mac]:
|
|
||||||
self.data['accounting'][mac]['lan-tx'] = 0.0
|
|
||||||
if 'lan-rx' in self.data['accounting'][mac]:
|
|
||||||
self.data['accounting'][mac]['lan-rx'] = 0.0
|
|
||||||
else:
|
|
||||||
# No time diff, just initialize/return counters to 0 for all
|
|
||||||
for addr in tmp_accounting_values:
|
|
||||||
mac = self._get_accounting_mac_by_ip(addr)
|
|
||||||
if not mac:
|
|
||||||
_LOGGER.debug(f"Address {addr} not found in accounting data, skipping update")
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.data['accounting'][mac]['wan-tx'] = 0.0
|
|
||||||
self.data['accounting'][mac]['wan-rx'] = 0.0
|
|
||||||
|
|
||||||
if local_traffic_enabled:
|
|
||||||
self.data['accounting'][mac]['lan-tx'] = 0.0
|
|
||||||
self.data['accounting'][mac]['lan-rx'] = 0.0
|
|
||||||
|
|
|
@ -378,6 +378,16 @@ class MikrotikAccountingSensor(MikrotikControllerSensor):
|
||||||
"""Return a unique_id for this entity."""
|
"""Return a unique_id for this entity."""
|
||||||
return f"{self._inst.lower()}-{self._sensor.lower()}-{self._data['mac-address'].lower()}"
|
return f"{self._inst.lower()}-{self._sensor.lower()}-{self._data['mac-address'].lower()}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self) -> bool:
|
||||||
|
"""Return if controller and accounting feature in Mikrotik is available.
|
||||||
|
Additional check for lan-tx/rx sensors
|
||||||
|
"""
|
||||||
|
if self._sensor in ['lan-tx, lan-rx']:
|
||||||
|
return self._ctrl.connected() and self._data['available'] and self._data['local_accounting']
|
||||||
|
else:
|
||||||
|
return self._ctrl.connected() and self._data['available']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self):
|
def device_info(self):
|
||||||
"""Return a accounting description for device registry."""
|
"""Return a accounting description for device registry."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue