"""Support for the Mikrotik Router device tracker.""" from __future__ import annotations from logging import getLogger from collections.abc import Mapping from datetime import timedelta from typing import Any from homeassistant.components.device_tracker.config_entry import ScannerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.const import STATE_NOT_HOME from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util.dt import utcnow from homeassistant.components.device_tracker.const import SourceType from .device_tracker_types import SENSOR_TYPES, SENSOR_SERVICES from .entity import MikrotikEntity, async_add_entities from .helper import format_attribute from .const import ( CONF_TRACK_HOSTS, DEFAULT_TRACK_HOSTS, CONF_TRACK_HOSTS_TIMEOUT, DEFAULT_TRACK_HOST_TIMEOUT, ) _LOGGER = getLogger(__name__) # --------------------------- # async_setup_entry # --------------------------- async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, _async_add_entities: AddEntitiesCallback, ) -> None: """Set up entry for component""" dispatcher = { "MikrotikDeviceTracker": MikrotikDeviceTracker, "MikrotikHostDeviceTracker": MikrotikHostDeviceTracker, } await async_add_entities(hass, config_entry, dispatcher) # --------------------------- # MikrotikDeviceTracker # --------------------------- class MikrotikDeviceTracker(MikrotikEntity, ScannerEntity): """Representation of a device tracker.""" @property def ip_address(self) -> str: """Return the primary ip address of the device.""" return self._data["address"] if "address" in self._data else None @property def mac_address(self) -> str: """Return the mac address of the device.""" if self.entity_description.data_reference in self._data: return self._data[self.entity_description.data_reference] return "" @property def hostname(self) -> str: """Return hostname of the device.""" if self.entity_description.data_name in self._data: return self._data[self.entity_description.data_name] return "" @property def is_connected(self) -> bool: """Return true if device is connected.""" return self._data[self.entity_description.data_attribute] @property def source_type(self) -> str: """Return the source type of the port.""" return SourceType.ROUTER # --------------------------- # MikrotikHostDeviceTracker # --------------------------- class MikrotikHostDeviceTracker(MikrotikDeviceTracker): """Representation of a network device.""" @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) @property def option_track_network_hosts_timeout(self): """Config entry option scan interval.""" track_network_hosts_timeout = self._config_entry.options.get( CONF_TRACK_HOSTS_TIMEOUT, DEFAULT_TRACK_HOST_TIMEOUT ) return timedelta(seconds=track_network_hosts_timeout) @property def name(self) -> Any: """Return the name.""" return None @property def is_connected(self) -> bool: """Return true if the host is connected to the network.""" if not self.option_track_network_hosts: return False if self._data["source"] in ["capsman", "wireless"]: return self._data[self.entity_description.data_attribute] return bool( self._data["last-seen"] and utcnow() - self._data["last-seen"] < self.option_track_network_hosts_timeout ) @property def icon(self) -> str: """Return the icon.""" if self._data["source"] in ["capsman", "wireless"]: if self._data[self.entity_description.data_attribute]: return self.entity_description.icon_enabled else: return self.entity_description.icon_disabled if ( self._data["last-seen"] and (utcnow() - self._data["last-seen"]) < self.option_track_network_hosts_timeout ): return self.entity_description.icon_enabled return self.entity_description.icon_disabled @property def state(self) -> str: """Return the state of the device.""" return self.coordinator.option_zone if self.is_connected else STATE_NOT_HOME @property def extra_state_attributes(self) -> Mapping[str, Any]: """Return the state attributes.""" attributes = super().extra_state_attributes if self.is_connected: attributes[format_attribute("last-seen")] = "Now" if not attributes[format_attribute("last-seen")]: attributes[format_attribute("last-seen")] = "Unknown" return attributes