core/homeassistant/components/unifi/device_tracker.py

182 lines
5.9 KiB
Python

"""Support for Unifi WAP controllers."""
from datetime import timedelta
import logging
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components import unifi
from homeassistant.components.device_tracker import PLATFORM_SCHEMA
from homeassistant.components.device_tracker.config_entry import ScannerEntity
from homeassistant.components.device_tracker.const import SOURCE_TYPE_ROUTER
from homeassistant.core import callback
from homeassistant.const import (
CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_PORT, CONF_VERIFY_SSL)
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.dispatcher import async_dispatcher_connect
import homeassistant.helpers.config_validation as cv
import homeassistant.util.dt as dt_util
from .const import (
CONF_CONTROLLER, CONF_DETECTION_TIME, CONF_SITE_ID, CONF_SSID_FILTER,
CONTROLLER_ID, DOMAIN as UNIFI_DOMAIN)
LOGGER = logging.getLogger(__name__)
DEVICE_ATTRIBUTES = [
'_is_guest_by_uap', 'ap_mac', 'authorized', 'bssid', 'ccq',
'channel', 'essid', 'hostname', 'ip', 'is_11r', 'is_guest', 'is_wired',
'mac', 'name', 'noise', 'noted', 'oui', 'qos_policy_applied', 'radio',
'radio_proto', 'rssi', 'signal', 'site_id', 'vlan'
]
CONF_DT_SITE_ID = 'site_id'
DEFAULT_HOST = 'localhost'
DEFAULT_PORT = 8443
DEFAULT_VERIFY_SSL = True
DEFAULT_DETECTION_TIME = timedelta(seconds=300)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_DT_SITE_ID, default='default'): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): vol.Any(
cv.boolean, cv.isfile)
}, extra=vol.ALLOW_EXTRA)
async def async_setup_scanner(hass, config, sync_see, discovery_info):
"""Set up the Unifi integration."""
config[CONF_SITE_ID] = config.pop(CONF_DT_SITE_ID) # Current from legacy
exist = False
for entry in hass.config_entries.async_entries(UNIFI_DOMAIN):
if config[CONF_HOST] == entry.data[CONF_CONTROLLER][CONF_HOST] and \
config[CONF_SITE_ID] == \
entry.data[CONF_CONTROLLER][CONF_SITE_ID]:
exist = True
break
if not exist:
hass.async_create_task(hass.config_entries.flow.async_init(
UNIFI_DOMAIN, context={'source': config_entries.SOURCE_IMPORT},
data=config
))
return True
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up device tracker for UniFi component."""
controller_id = CONTROLLER_ID.format(
host=config_entry.data[CONF_CONTROLLER][CONF_HOST],
site=config_entry.data[CONF_CONTROLLER][CONF_SITE_ID],
)
controller = hass.data[unifi.DOMAIN][controller_id]
tracked = {}
@callback
def update_controller():
"""Update the values of the controller."""
update_items(controller, async_add_entities, tracked)
async_dispatcher_connect(hass, controller.event_update, update_controller)
update_controller()
@callback
def update_items(controller, async_add_entities, tracked):
"""Update tracked device state from the controller."""
new_tracked = []
for client_id in controller.api.clients:
if client_id in tracked:
LOGGER.debug("Updating UniFi tracked device %s (%s)",
tracked[client_id].entity_id,
tracked[client_id].client.mac)
tracked[client_id].async_schedule_update_ha_state()
continue
client = controller.api.clients[client_id]
if not client.is_wired and \
CONF_SSID_FILTER in controller.unifi_config and \
client.essid not in controller.unifi_config[CONF_SSID_FILTER]:
continue
tracked[client_id] = UniFiClientTracker(client, controller)
new_tracked.append(tracked[client_id])
LOGGER.debug("New UniFi switch %s (%s)", client.hostname, client.mac)
if new_tracked:
async_add_entities(new_tracked)
class UniFiClientTracker(ScannerEntity):
"""Representation of a network device."""
def __init__(self, client, controller):
"""Set up tracked device."""
self.client = client
self.controller = controller
async def async_update(self):
"""Synchronize state with controller."""
await self.controller.request_update()
@property
def is_connected(self):
"""Return true if the device is connected to the network."""
detection_time = self.controller.unifi_config.get(
CONF_DETECTION_TIME, DEFAULT_DETECTION_TIME)
if (dt_util.utcnow() - dt_util.utc_from_timestamp(float(
self.client.last_seen))) < detection_time:
return True
return False
@property
def source_type(self):
"""Return the source type of the device."""
return SOURCE_TYPE_ROUTER
@property
def name(self) -> str:
"""Return the name of the device."""
return self.client.name or self.client.hostname
@property
def unique_id(self) -> str:
"""Return a unique identifier for this client."""
return '{}-{}'.format(self.client.mac, self.controller.site)
@property
def available(self) -> bool:
"""Return if controller is available."""
return self.controller.available
@property
def device_info(self):
"""Return a device description for device registry."""
return {
'connections': {(CONNECTION_NETWORK_MAC, self.client.mac)}
}
@property
def device_state_attributes(self):
"""Return the device state attributes."""
attributes = {}
for variable in DEVICE_ATTRIBUTES:
if variable in self.client.raw:
attributes[variable] = self.client.raw[variable]
return attributes