2021-09-30 12:38:29 +00:00
|
|
|
"""UniFi services."""
|
|
|
|
|
2021-10-18 06:06:06 +00:00
|
|
|
import voluptuous as vol
|
|
|
|
|
|
|
|
from homeassistant.const import ATTR_DEVICE_ID
|
2021-10-01 13:59:29 +00:00
|
|
|
from homeassistant.core import callback
|
2021-10-20 08:59:07 +00:00
|
|
|
from homeassistant.helpers import device_registry as dr
|
2021-10-18 06:06:06 +00:00
|
|
|
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
2021-09-30 12:38:29 +00:00
|
|
|
|
2021-10-01 13:59:29 +00:00
|
|
|
from .const import DOMAIN as UNIFI_DOMAIN
|
2021-09-30 12:38:29 +00:00
|
|
|
|
2021-10-18 06:06:06 +00:00
|
|
|
SERVICE_RECONNECT_CLIENT = "reconnect_client"
|
2021-09-30 12:38:29 +00:00
|
|
|
SERVICE_REMOVE_CLIENTS = "remove_clients"
|
|
|
|
|
2021-10-18 06:06:06 +00:00
|
|
|
SERVICE_RECONNECT_CLIENT_SCHEMA = vol.All(
|
|
|
|
vol.Schema({vol.Required(ATTR_DEVICE_ID): str})
|
|
|
|
)
|
|
|
|
|
|
|
|
SUPPORTED_SERVICES = (SERVICE_RECONNECT_CLIENT, SERVICE_REMOVE_CLIENTS)
|
|
|
|
|
|
|
|
SERVICE_TO_SCHEMA = {
|
|
|
|
SERVICE_RECONNECT_CLIENT: SERVICE_RECONNECT_CLIENT_SCHEMA,
|
|
|
|
}
|
|
|
|
|
2021-09-30 12:38:29 +00:00
|
|
|
|
2021-10-01 13:59:29 +00:00
|
|
|
@callback
|
|
|
|
def async_setup_services(hass) -> None:
|
2021-09-30 12:38:29 +00:00
|
|
|
"""Set up services for UniFi integration."""
|
|
|
|
|
2021-10-18 06:06:06 +00:00
|
|
|
services = {
|
|
|
|
SERVICE_RECONNECT_CLIENT: async_reconnect_client,
|
|
|
|
SERVICE_REMOVE_CLIENTS: async_remove_clients,
|
|
|
|
}
|
|
|
|
|
2021-09-30 12:38:29 +00:00
|
|
|
async def async_call_unifi_service(service_call) -> None:
|
|
|
|
"""Call correct UniFi service."""
|
2021-10-18 06:06:06 +00:00
|
|
|
await services[service_call.service](hass, service_call.data)
|
2021-09-30 12:38:29 +00:00
|
|
|
|
2021-10-18 06:06:06 +00:00
|
|
|
for service in SUPPORTED_SERVICES:
|
|
|
|
hass.services.async_register(
|
|
|
|
UNIFI_DOMAIN,
|
|
|
|
service,
|
|
|
|
async_call_unifi_service,
|
|
|
|
schema=SERVICE_TO_SCHEMA.get(service),
|
|
|
|
)
|
2021-09-30 12:38:29 +00:00
|
|
|
|
|
|
|
|
2021-10-01 13:59:29 +00:00
|
|
|
@callback
|
|
|
|
def async_unload_services(hass) -> None:
|
2021-09-30 12:38:29 +00:00
|
|
|
"""Unload UniFi services."""
|
2021-10-18 06:06:06 +00:00
|
|
|
for service in SUPPORTED_SERVICES:
|
|
|
|
hass.services.async_remove(UNIFI_DOMAIN, service)
|
|
|
|
|
|
|
|
|
|
|
|
async def async_reconnect_client(hass, data) -> None:
|
|
|
|
"""Try to get wireless client to reconnect to Wi-Fi."""
|
2021-10-20 08:59:07 +00:00
|
|
|
device_registry = dr.async_get(hass)
|
2021-10-18 06:06:06 +00:00
|
|
|
device_entry = device_registry.async_get(data[ATTR_DEVICE_ID])
|
|
|
|
|
|
|
|
mac = ""
|
|
|
|
for connection in device_entry.connections:
|
|
|
|
if connection[0] == CONNECTION_NETWORK_MAC:
|
|
|
|
mac = connection[1]
|
|
|
|
break
|
|
|
|
|
|
|
|
if mac == "":
|
|
|
|
return
|
|
|
|
|
|
|
|
for controller in hass.data[UNIFI_DOMAIN].values():
|
|
|
|
if (
|
|
|
|
not controller.available
|
|
|
|
or (client := controller.api.clients[mac]) is None
|
|
|
|
or client.is_wired
|
|
|
|
):
|
|
|
|
continue
|
|
|
|
|
|
|
|
await controller.api.clients.async_reconnect(mac)
|
2021-09-30 12:38:29 +00:00
|
|
|
|
|
|
|
|
2021-10-18 06:06:06 +00:00
|
|
|
async def async_remove_clients(hass, data) -> None:
|
2021-09-30 12:38:29 +00:00
|
|
|
"""Remove select clients from controller.
|
|
|
|
|
|
|
|
Validates based on:
|
|
|
|
- Total time between first seen and last seen is less than 15 minutes.
|
|
|
|
- Neither IP, hostname nor name is configured.
|
|
|
|
"""
|
2021-10-18 06:06:06 +00:00
|
|
|
for controller in hass.data[UNIFI_DOMAIN].values():
|
2021-09-30 12:38:29 +00:00
|
|
|
|
|
|
|
if not controller.available:
|
|
|
|
continue
|
|
|
|
|
|
|
|
clients_to_remove = []
|
|
|
|
|
|
|
|
for client in controller.api.clients_all.values():
|
|
|
|
|
|
|
|
if client.last_seen - client.first_seen > 900:
|
|
|
|
continue
|
|
|
|
|
|
|
|
if any({client.fixed_ip, client.hostname, client.name}):
|
|
|
|
continue
|
|
|
|
|
|
|
|
clients_to_remove.append(client.mac)
|
|
|
|
|
|
|
|
if clients_to_remove:
|
|
|
|
await controller.api.clients.remove_clients(macs=clients_to_remove)
|