Merge remote-tracking branch 'origin/master'

This commit is contained in:
Ivan Pavlina 2020-04-08 12:32:48 +02:00
commit eee634cfc8
2 changed files with 167 additions and 29 deletions

View file

@ -11,6 +11,7 @@ from homeassistant.const import (
from homeassistant.core import callback
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.util.dt import get_age
from .const import (
DOMAIN,
@ -37,11 +38,10 @@ DEVICE_ATTRIBUTES_IFACE = [
]
DEVICE_ATTRIBUTES_HOST = [
"host-name",
"hostname",
"address",
"mac-address",
"interface",
"status",
"last-seen",
]
@ -91,7 +91,7 @@ def update_items(inst, mikrotik_controller, async_add_entities, tracked):
# Add switches
for sid, sid_uid, sid_func in zip(
["interface", "dhcp"],
["interface", "host"],
["default-name", "mac-address"],
[
MikrotikControllerPortDeviceTracker,
@ -218,7 +218,7 @@ class MikrotikControllerHostDeviceTracker(ScannerEntity):
"""Set up tracked port."""
self._inst = inst
self._ctrl = mikrotik_controller
self._data = mikrotik_controller.data["dhcp"][uid]
self._data = mikrotik_controller.data["host"][uid]
self._attrs = {
ATTR_ATTRIBUTION: ATTRIBUTION,
@ -234,7 +234,7 @@ class MikrotikControllerHostDeviceTracker(ScannerEntity):
_LOGGER.debug(
"New host tracker %s (%s - %s)",
self._inst,
self._data["host-name"],
self._data["hostname"],
self._data["mac-address"],
)
@ -254,7 +254,7 @@ class MikrotikControllerHostDeviceTracker(ScannerEntity):
@property
def name(self):
"""Return the name of the host."""
return f"{self._inst} {self._data['host-name']}"
return f"{self._data['hostname']}"
@property
def unique_id(self):
@ -302,6 +302,12 @@ class MikrotikControllerHostDeviceTracker(ScannerEntity):
for variable in DEVICE_ATTRIBUTES_HOST:
if variable in self._data:
attributes[format_attribute(variable)] = self._data[variable]
if variable == "last-seen":
if self._data[variable]:
attributes[format_attribute(variable)] = get_age(self._data[variable])
else:
attributes[format_attribute(variable)] = "unknown"
else:
attributes[format_attribute(variable)] = self._data[variable]
return attributes

View file

@ -8,6 +8,7 @@ from ipaddress import ip_address, IPv4Network
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.util.dt import utcnow
from .const import (
DOMAIN,
@ -55,12 +56,16 @@ class MikrotikControllerData:
"resource": {},
"interface": {},
"arp": {},
"arp_tmp": {},
"nat": {},
"fw-update": {},
"script": {},
"queue": {},
"dns": {},
"dhcp-server": {},
"dhcp-network": {},
"dhcp": {},
"host": {},
"accounting": {}
}
@ -194,6 +199,10 @@ class MikrotikControllerData:
await self.async_fwupdate_check()
await self.hass.async_add_executor_job(self.get_interface)
await self.hass.async_add_executor_job(self.get_arp)
await self.hass.async_add_executor_job(self.get_dns)
await self.hass.async_add_executor_job(self.get_dhcp)
await self.hass.async_add_executor_job(self.process_host)
await self.hass.async_add_executor_job(self.get_interface_traffic)
await self.hass.async_add_executor_job(self.get_interface_client)
await self.hass.async_add_executor_job(self.get_nat)
@ -204,6 +213,7 @@ class MikrotikControllerData:
await self.hass.async_add_executor_job(self.get_accounting)
async_dispatcher_send(self.hass, self.signal_update)
self.lock.release()
# ---------------------------
@ -310,7 +320,7 @@ class MikrotikControllerData:
# ---------------------------
def get_interface_client(self):
"""Get ARP data from Mikrotik"""
self.data["arp"] = {}
self.data["arp_tmp"] = {}
# Remove data if disabled
if not self.option_track_arp:
@ -328,14 +338,14 @@ class MikrotikControllerData:
# Map ARP to ifaces
for uid in self.data["interface"]:
if uid not in self.data["arp"]:
if uid not in self.data["arp_tmp"]:
continue
self.data["interface"][uid]["client-ip-address"] = from_entry(
self.data["arp"][uid], "address"
self.data["arp_tmp"][uid], "address"
)
self.data["interface"][uid]["client-mac-address"] = from_entry(
self.data["arp"][uid], "mac-address"
self.data["arp_tmp"][uid], "mac-address"
)
# ---------------------------
@ -372,16 +382,16 @@ class MikrotikControllerData:
_LOGGER.debug("Processing entry %s, entry %s", "/ip/arp", entry)
# Create uid arp dict
if uid not in self.data["arp"]:
self.data["arp"][uid] = {}
if uid not in self.data["arp_tmp"]:
self.data["arp_tmp"][uid] = {}
# Add data
self.data["arp"][uid]["interface"] = uid
self.data["arp"][uid]["mac-address"] = (
from_entry(entry, "mac-address") if "mac-address" not in self.data["arp"][uid] else "multiple"
self.data["arp_tmp"][uid]["interface"] = uid
self.data["arp_tmp"][uid]["mac-address"] = (
from_entry(entry, "mac-address") if "mac-address" not in self.data["arp_tmp"][uid] else "multiple"
)
self.data["arp"][uid]["address"] = (
from_entry(entry, "address") if "address" not in self.data["arp"][uid] else "multiple"
self.data["arp_tmp"][uid]["address"] = (
from_entry(entry, "address") if "address" not in self.data["arp_tmp"][uid] else "multiple"
)
return mac2ip, bridge_used
@ -409,20 +419,19 @@ class MikrotikControllerData:
"Processing entry %s, entry %s", "/interface/bridge/host", entry
)
# Create uid arp dict
if uid not in self.data["arp"]:
self.data["arp"][uid] = {}
if uid not in self.data["arp_tmp"]:
self.data["arp_tmp"][uid] = {}
# Add data
self.data["arp"][uid]["interface"] = uid
if "mac-address" in self.data["arp"][uid]:
self.data["arp"][uid]["mac-address"] = "multiple"
self.data["arp"][uid]["address"] = "multiple"
self.data["arp_tmp"][uid]["interface"] = uid
if "mac-address" in self.data["arp_tmp"][uid]:
self.data["arp_tmp"][uid]["mac-address"] = "multiple"
self.data["arp_tmp"][uid]["address"] = "multiple"
else:
self.data["arp"][uid]["mac-address"] = from_entry(entry,
"mac-address")
self.data["arp"][uid]["address"] = (
mac2ip[self.data["arp"][uid]["mac-address"]]
if self.data["arp"][uid]["mac-address"] in mac2ip
self.data["arp_tmp"][uid]["mac-address"] = from_entry(entry, "mac-address")
self.data["arp_tmp"][uid]["address"] = (
mac2ip[self.data["arp_tmp"][uid]["mac-address"]]
if self.data["arp_tmp"][uid]["mac-address"] in mac2ip
else ""
)
@ -666,12 +675,64 @@ class MikrotikControllerData:
self.data["queue"][uid]["upload-burst-time"] = upload_burst_time
self.data["queue"][uid]["download-burst-time"] = download_burst_time
# ---------------------------
# get_arp
# ---------------------------
def get_arp(self):
"""Get ARP data from Mikrotik"""
self.data["arp"] = parse_api(
data=self.data["arp"],
source=self.api.path("/ip/arp"),
key="mac-address",
vals=[
{"name": "mac-address"},
{"name": "address"},
{"name": "interface"},
],
)
# ---------------------------
# get_dns
# ---------------------------
def get_dns(self):
"""Get static DNS data from Mikrotik"""
self.data["dns"] = parse_api(
data=self.data["dns"],
source=self.api.path("/ip/dns/static"),
key="name",
vals=[
{"name": "name"},
{"name": "address"},
],
)
# ---------------------------
# get_dhcp
# ---------------------------
def get_dhcp(self):
"""Get DHCP data from Mikrotik"""
self.data["dhcp-network"] = parse_api(
data=self.data["dhcp-network"],
source=self.api.path("/ip/dhcp-server/network"),
key="address",
vals=[
{"name": "address"},
{"name": "gateway", "default": ""},
{"name": "netmask", "default": ""},
{"name": "dns-server", "default": ""},
{"name": "domain", "default": ""},
],
ensure_vals=[
{"name": "address"},
{"name": "IPv4Network", "default": ""},
]
)
for uid, vals in self.data["dhcp-network"].items():
if vals["IPv4Network"] == "":
self.data["dhcp-network"][uid]["IPv4Network"] = IPv4Network(vals["address"])
self.data["dhcp-server"] = parse_api(
data=self.data["dhcp-server"],
source=self.api.path("/ip/dhcp-server"),
@ -716,6 +777,77 @@ class MikrotikControllerData:
)
for uid in self.data["dhcp"]:
self.data["dhcp"][uid]["interface"] = \
self.data["dhcp-server"][self.data["dhcp"][uid]["server"]]["interface"]
# ---------------------------
# process_host
# ---------------------------
def process_host(self):
"""Get host tracking data"""
# Add hosts from DHCP
for uid, vals in self.data["dhcp"].items():
if uid not in self.data["host"]:
self.data["host"][uid] = {}
self.data["host"][uid]["source"] = "dhcp"
for key, key_data in zip(
["address", "mac-address", "interface"],
["address", "mac-address", "interface"],
):
if key not in self.data["host"][uid] or self.data["host"][uid][key] == "unknown":
self.data["host"][uid][key] = vals[key_data]
# Add hosts from ARP
for uid, vals in self.data["arp"].items():
if uid not in self.data["host"]:
self.data["host"][uid] = {}
self.data["host"][uid]["source"] = "arp"
for key, key_data in zip(
["address", "mac-address", "interface"],
["address", "mac-address", "interface"],
):
if key not in self.data["host"][uid] or self.data["host"][uid][key] == "unknown":
self.data["host"][uid][key] = vals[key_data]
# Process hosts
for uid, vals in self.data["host"].items():
# Add missing default values
for key, default in zip(
["address", "mac-address", "interface", "hostname", "last-seen", "available"],
["unknown", "unknown", "unknown", "unknown", False],
):
if key not in self.data["host"][uid]:
self.data["host"][uid][key] = default
# Resolve hostname
if vals["hostname"] == "unknown":
if vals["address"] != "unknown":
for dns_uid, dns_vals in self.data["dns"].items():
if dns_vals["address"] == vals["address"]:
self.data["host"][uid]["hostname"] = dns_vals["name"].split('.')[0]
break
if self.data["host"][uid]["hostname"] == "unknown" \
and uid in self.data["dhcp"] and self.data["dhcp"][uid]["comment"] != "":
self.data["host"][uid]["hostname"] = self.data["dhcp"][uid]["comment"]
elif self.data["host"][uid]["hostname"] == "unknown" \
and uid in self.data["dhcp"] and self.data["dhcp"][uid]["host-name"] != "unknown":
self.data["host"][uid]["hostname"] = self.data["dhcp"][uid]["host-name"]
elif self.data["host"][uid]["hostname"] == "unknown":
self.data["host"][uid]["hostname"] = uid
# Check host availability
if vals["address"] != "unknown" and vals["interface"] != "unknown":
self.data["host"][uid]["available"] = \
self.api.arp_ping(vals["address"], vals["interface"])
# Update last seen
if self.data["host"][uid]["available"]:
self.data["host"][uid]["last-seen"] = utcnow()
self.data["dhcp"][uid]['interface'] = \
self.data["dhcp-server"][self.data["dhcp"][uid]['server']]["interface"]