UniFi - Make POE control switches configurable (#32781)

* Allow control whether POE switches are to be created or not

* Fix options flow and test
This commit is contained in:
Robert Svensson 2020-04-02 17:53:33 +02:00 committed by GitHub
parent 23e091696e
commit 314bc07cee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 76 additions and 45 deletions

View file

@ -32,7 +32,8 @@
"client_control": { "client_control": {
"data": { "data": {
"block_client": "Network access controlled clients", "block_client": "Network access controlled clients",
"new_client": "Add new client for network access control" "new_client": "Add new client (MAC) for network access control",
"poe_clients": "Allow POE control of clients"
}, },
"description": "Configure client controls\n\nCreate switches for serial numbers you want to control network access for.", "description": "Configure client controls\n\nCreate switches for serial numbers you want to control network access for.",
"title": "UniFi options 2/3" "title": "UniFi options 2/3"

View file

@ -19,12 +19,14 @@ from .const import (
CONF_BLOCK_CLIENT, CONF_BLOCK_CLIENT,
CONF_CONTROLLER, CONF_CONTROLLER,
CONF_DETECTION_TIME, CONF_DETECTION_TIME,
CONF_POE_CLIENTS,
CONF_SITE_ID, CONF_SITE_ID,
CONF_SSID_FILTER, CONF_SSID_FILTER,
CONF_TRACK_CLIENTS, CONF_TRACK_CLIENTS,
CONF_TRACK_DEVICES, CONF_TRACK_DEVICES,
CONF_TRACK_WIRED_CLIENTS, CONF_TRACK_WIRED_CLIENTS,
CONTROLLER_ID, CONTROLLER_ID,
DEFAULT_POE_CLIENTS,
DOMAIN, DOMAIN,
LOGGER, LOGGER,
) )
@ -262,6 +264,10 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow):
step_id="client_control", step_id="client_control",
data_schema=vol.Schema( data_schema=vol.Schema(
{ {
vol.Optional(
CONF_POE_CLIENTS,
default=self.options.get(CONF_POE_CLIENTS, DEFAULT_POE_CLIENTS),
): bool,
vol.Optional( vol.Optional(
CONF_BLOCK_CLIENT, default=self.options[CONF_BLOCK_CLIENT] CONF_BLOCK_CLIENT, default=self.options[CONF_BLOCK_CLIENT]
): cv.multi_select(clients_to_block), ): cv.multi_select(clients_to_block),

View file

@ -14,12 +14,14 @@ UNIFI_WIRELESS_CLIENTS = "unifi_wireless_clients"
CONF_ALLOW_BANDWIDTH_SENSORS = "allow_bandwidth_sensors" CONF_ALLOW_BANDWIDTH_SENSORS = "allow_bandwidth_sensors"
CONF_BLOCK_CLIENT = "block_client" CONF_BLOCK_CLIENT = "block_client"
CONF_DETECTION_TIME = "detection_time" CONF_DETECTION_TIME = "detection_time"
CONF_POE_CLIENTS = "poe_clients"
CONF_TRACK_CLIENTS = "track_clients" CONF_TRACK_CLIENTS = "track_clients"
CONF_TRACK_DEVICES = "track_devices" CONF_TRACK_DEVICES = "track_devices"
CONF_TRACK_WIRED_CLIENTS = "track_wired_clients" CONF_TRACK_WIRED_CLIENTS = "track_wired_clients"
CONF_SSID_FILTER = "ssid_filter" CONF_SSID_FILTER = "ssid_filter"
DEFAULT_ALLOW_BANDWIDTH_SENSORS = False DEFAULT_ALLOW_BANDWIDTH_SENSORS = False
DEFAULT_POE_CLIENTS = True
DEFAULT_TRACK_CLIENTS = True DEFAULT_TRACK_CLIENTS = True
DEFAULT_TRACK_DEVICES = True DEFAULT_TRACK_DEVICES = True
DEFAULT_TRACK_WIRED_CLIENTS = True DEFAULT_TRACK_WIRED_CLIENTS = True

View file

@ -24,6 +24,7 @@ from .const import (
CONF_BLOCK_CLIENT, CONF_BLOCK_CLIENT,
CONF_CONTROLLER, CONF_CONTROLLER,
CONF_DETECTION_TIME, CONF_DETECTION_TIME,
CONF_POE_CLIENTS,
CONF_SITE_ID, CONF_SITE_ID,
CONF_SSID_FILTER, CONF_SSID_FILTER,
CONF_TRACK_CLIENTS, CONF_TRACK_CLIENTS,
@ -32,6 +33,7 @@ from .const import (
CONTROLLER_ID, CONTROLLER_ID,
DEFAULT_ALLOW_BANDWIDTH_SENSORS, DEFAULT_ALLOW_BANDWIDTH_SENSORS,
DEFAULT_DETECTION_TIME, DEFAULT_DETECTION_TIME,
DEFAULT_POE_CLIENTS,
DEFAULT_TRACK_CLIENTS, DEFAULT_TRACK_CLIENTS,
DEFAULT_TRACK_DEVICES, DEFAULT_TRACK_DEVICES,
DEFAULT_TRACK_WIRED_CLIENTS, DEFAULT_TRACK_WIRED_CLIENTS,
@ -98,6 +100,11 @@ class UniFiController:
"""Config entry option with list of clients to control network access.""" """Config entry option with list of clients to control network access."""
return self.config_entry.options.get(CONF_BLOCK_CLIENT, []) return self.config_entry.options.get(CONF_BLOCK_CLIENT, [])
@property
def option_poe_clients(self):
"""Config entry option to control poe clients."""
return self.config_entry.options.get(CONF_POE_CLIENTS, DEFAULT_POE_CLIENTS)
@property @property
def option_track_clients(self): def option_track_clients(self):
"""Config entry option to not track clients.""" """Config entry option to not track clients."""

View file

@ -43,7 +43,8 @@
"client_control": { "client_control": {
"data": { "data": {
"block_client": "Network access controlled clients", "block_client": "Network access controlled clients",
"new_client": "Add new client for network access control" "new_client": "Add new client for network access control",
"poe_clients": "Allow POE control of clients"
}, },
"description": "Configure client controls\n\nCreate switches for serial numbers you want to control network access for.", "description": "Configure client controls\n\nCreate switches for serial numbers you want to control network access for.",
"title": "UniFi options 2/3" "title": "UniFi options 2/3"

View file

@ -30,6 +30,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
switches_off = [] switches_off = []
option_block_clients = controller.option_block_clients option_block_clients = controller.option_block_clients
option_poe_clients = controller.option_poe_clients
entity_registry = await hass.helpers.entity_registry.async_get_registry() entity_registry = await hass.helpers.entity_registry.async_get_registry()
@ -66,6 +67,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
def options_updated(): def options_updated():
"""Manage entities affected by config entry options.""" """Manage entities affected by config entry options."""
nonlocal option_block_clients nonlocal option_block_clients
nonlocal option_poe_clients
update = set() update = set()
remove = set() remove = set()
@ -82,16 +84,26 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
else: else:
remove.add(block_client_id) remove.add(block_client_id)
for block_client_id in remove: if option_poe_clients != controller.option_poe_clients:
entity = switches.pop(block_client_id) option_poe_clients = controller.option_poe_clients
if entity_registry.async_is_registered(entity.entity_id): if option_poe_clients:
entity_registry.async_remove(entity.entity_id) update.add("poe_clients_enabled")
else:
for poe_client_id, entity in switches.items():
if isinstance(entity, UniFiPOEClientSwitch):
remove.add(poe_client_id)
hass.async_create_task(entity.async_remove()) for client_id in remove:
entity = switches.pop(client_id)
if len(update) != len(option_block_clients): if entity_registry.async_is_registered(entity.entity_id):
update_controller() entity_registry.async_remove(entity.entity_id)
hass.async_create_task(entity.async_remove())
if len(update) != len(option_block_clients):
update_controller()
controller.listeners.append( controller.listeners.append(
async_dispatcher_connect( async_dispatcher_connect(
@ -109,7 +121,6 @@ def add_entities(controller, async_add_entities, switches, switches_off):
new_switches = [] new_switches = []
devices = controller.api.devices devices = controller.api.devices
# block client
for client_id in controller.option_block_clients: for client_id in controller.option_block_clients:
client = None client = None
@ -130,49 +141,49 @@ def add_entities(controller, async_add_entities, switches, switches_off):
switches[block_client_id] = UniFiBlockClientSwitch(client, controller) switches[block_client_id] = UniFiBlockClientSwitch(client, controller)
new_switches.append(switches[block_client_id]) new_switches.append(switches[block_client_id])
# control POE if controller.option_poe_clients:
for client_id in controller.api.clients: for client_id in controller.api.clients:
poe_client_id = f"poe-{client_id}" poe_client_id = f"poe-{client_id}"
if poe_client_id in switches: if poe_client_id in switches:
continue continue
client = controller.api.clients[client_id] client = controller.api.clients[client_id]
if poe_client_id in switches_off:
pass
# Network device with active POE
elif (
client_id in controller.wireless_clients
or client.sw_mac not in devices
or not devices[client.sw_mac].ports[client.sw_port].port_poe
or not devices[client.sw_mac].ports[client.sw_port].poe_enable
or controller.mac == client.mac
):
continue
# Multiple POE-devices on same port means non UniFi POE driven switch
multi_clients_on_port = False
for client2 in controller.api.clients.values():
if poe_client_id in switches_off: if poe_client_id in switches_off:
break pass
# Network device with active POE
if ( elif (
client2.is_wired client_id in controller.wireless_clients
and client.mac != client2.mac or client.sw_mac not in devices
and client.sw_mac == client2.sw_mac or not devices[client.sw_mac].ports[client.sw_port].port_poe
and client.sw_port == client2.sw_port or not devices[client.sw_mac].ports[client.sw_port].poe_enable
or controller.mac == client.mac
): ):
multi_clients_on_port = True continue
break
if multi_clients_on_port: # Multiple POE-devices on same port means non UniFi POE driven switch
continue multi_clients_on_port = False
for client2 in controller.api.clients.values():
switches[poe_client_id] = UniFiPOEClientSwitch(client, controller) if poe_client_id in switches_off:
new_switches.append(switches[poe_client_id]) break
if (
client2.is_wired
and client.mac != client2.mac
and client.sw_mac == client2.sw_mac
and client.sw_port == client2.sw_port
):
multi_clients_on_port = True
break
if multi_clients_on_port:
continue
switches[poe_client_id] = UniFiPOEClientSwitch(client, controller)
new_switches.append(switches[poe_client_id])
if new_switches: if new_switches:
async_add_entities(new_switches) async_add_entities(new_switches)

View file

@ -11,6 +11,7 @@ from homeassistant.components.unifi.const import (
CONF_BLOCK_CLIENT, CONF_BLOCK_CLIENT,
CONF_CONTROLLER, CONF_CONTROLLER,
CONF_DETECTION_TIME, CONF_DETECTION_TIME,
CONF_POE_CLIENTS,
CONF_SITE_ID, CONF_SITE_ID,
CONF_SSID_FILTER, CONF_SSID_FILTER,
CONF_TRACK_CLIENTS, CONF_TRACK_CLIENTS,
@ -287,6 +288,7 @@ async def test_option_flow(hass):
user_input={ user_input={
CONF_BLOCK_CLIENT: clients_to_block, CONF_BLOCK_CLIENT: clients_to_block,
CONF_NEW_CLIENT: "00:00:00:00:00:01", CONF_NEW_CLIENT: "00:00:00:00:00:01",
CONF_POE_CLIENTS: False,
}, },
) )
@ -327,5 +329,6 @@ async def test_option_flow(hass):
CONF_DETECTION_TIME: 100, CONF_DETECTION_TIME: 100,
CONF_SSID_FILTER: ["SSID 1"], CONF_SSID_FILTER: ["SSID 1"],
CONF_BLOCK_CLIENT: ["00:00:00:00:00:01"], CONF_BLOCK_CLIENT: ["00:00:00:00:00:01"],
CONF_POE_CLIENTS: False,
CONF_ALLOW_BANDWIDTH_SENSORS: True, CONF_ALLOW_BANDWIDTH_SENSORS: True,
} }