mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
UniFi POE control restore clients (#25558)
* Restore POE controls on restart
This commit is contained in:
parent
2e300aec5a
commit
dc722adbb5
@ -5,8 +5,10 @@ from homeassistant.components import unifi
|
|||||||
from homeassistant.components.switch import SwitchDevice
|
from homeassistant.components.switch import SwitchDevice
|
||||||
from homeassistant.const import CONF_HOST
|
from homeassistant.const import CONF_HOST
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers import entity_registry
|
||||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
|
|
||||||
from .const import CONF_CONTROLLER, CONF_SITE_ID, CONTROLLER_ID
|
from .const import CONF_CONTROLLER, CONF_SITE_ID, CONTROLLER_ID
|
||||||
|
|
||||||
@ -34,19 +36,39 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
return
|
return
|
||||||
|
|
||||||
switches = {}
|
switches = {}
|
||||||
|
switches_off = []
|
||||||
|
|
||||||
|
registry = await entity_registry.async_get_registry(hass)
|
||||||
|
|
||||||
|
# Restore clients that is not a part of active clients list.
|
||||||
|
for entity in registry.entities.values():
|
||||||
|
|
||||||
|
if entity.config_entry_id == config_entry.entry_id and \
|
||||||
|
entity.unique_id.startswith('poe-'):
|
||||||
|
|
||||||
|
_, mac = entity.unique_id.split('-', 1)
|
||||||
|
|
||||||
|
if mac in controller.api.clients or \
|
||||||
|
mac not in controller.api.clients_all:
|
||||||
|
continue
|
||||||
|
|
||||||
|
client = controller.api.clients_all[mac]
|
||||||
|
controller.api.clients.process_raw([client.raw])
|
||||||
|
switches_off.append(entity.unique_id)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def update_controller():
|
def update_controller():
|
||||||
"""Update the values of the controller."""
|
"""Update the values of the controller."""
|
||||||
update_items(controller, async_add_entities, switches)
|
update_items(controller, async_add_entities, switches, switches_off)
|
||||||
|
|
||||||
async_dispatcher_connect(hass, controller.event_update, update_controller)
|
async_dispatcher_connect(hass, controller.event_update, update_controller)
|
||||||
|
|
||||||
update_controller()
|
update_controller()
|
||||||
|
switches_off.clear()
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def update_items(controller, async_add_entities, switches):
|
def update_items(controller, async_add_entities, switches, switches_off):
|
||||||
"""Update POE port state from the controller."""
|
"""Update POE port state from the controller."""
|
||||||
new_switches = []
|
new_switches = []
|
||||||
devices = controller.api.devices
|
devices = controller.api.devices
|
||||||
@ -85,17 +107,23 @@ def update_items(controller, async_add_entities, 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
|
# Network device with active POE
|
||||||
if not client.is_wired or client.sw_mac not in devices or \
|
elif not client.is_wired 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].port_poe or \
|
||||||
not devices[client.sw_mac].ports[client.sw_port].poe_enable or \
|
controller.mac == client.mac:
|
||||||
controller.mac == client.mac:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Multiple POE-devices on same port means non UniFi POE driven switch
|
# Multiple POE-devices on same port means non UniFi POE driven switch
|
||||||
multi_clients_on_port = False
|
multi_clients_on_port = False
|
||||||
for client2 in controller.api.clients.values():
|
for client2 in controller.api.clients.values():
|
||||||
if client.mac != client2.mac and \
|
|
||||||
|
if poe_client_id in switches_off:
|
||||||
|
break
|
||||||
|
|
||||||
|
if client2.is_wired and client.mac != client2.mac and \
|
||||||
client.sw_mac == client2.sw_mac and \
|
client.sw_mac == client2.sw_mac and \
|
||||||
client.sw_port == client2.sw_port:
|
client.sw_port == client2.sw_port:
|
||||||
multi_clients_on_port = True
|
multi_clients_on_port = True
|
||||||
@ -138,16 +166,32 @@ class UniFiClient:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class UniFiPOEClientSwitch(UniFiClient, SwitchDevice):
|
class UniFiPOEClientSwitch(UniFiClient, SwitchDevice, RestoreEntity):
|
||||||
"""Representation of a client that uses POE."""
|
"""Representation of a client that uses POE."""
|
||||||
|
|
||||||
def __init__(self, client, controller):
|
def __init__(self, client, controller):
|
||||||
"""Set up POE switch."""
|
"""Set up POE switch."""
|
||||||
super().__init__(client, controller)
|
super().__init__(client, controller)
|
||||||
self.poe_mode = None
|
self.poe_mode = None
|
||||||
if self.port.poe_mode != 'off':
|
if self.client.sw_port and self.port.poe_mode != 'off':
|
||||||
self.poe_mode = self.port.poe_mode
|
self.poe_mode = self.port.poe_mode
|
||||||
|
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
"""Call when entity about to be added to Home Assistant."""
|
||||||
|
state = await self.async_get_last_state()
|
||||||
|
|
||||||
|
if state is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.poe_mode is None:
|
||||||
|
self.poe_mode = state.attributes['poe_mode']
|
||||||
|
|
||||||
|
if not self.client.sw_mac:
|
||||||
|
self.client.raw['sw_mac'] = state.attributes['switch']
|
||||||
|
|
||||||
|
if not self.client.sw_port:
|
||||||
|
self.client.raw['sw_port'] = state.attributes['port']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Return a unique identifier for this switch."""
|
"""Return a unique identifier for this switch."""
|
||||||
@ -160,9 +204,14 @@ class UniFiPOEClientSwitch(UniFiClient, SwitchDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self):
|
def available(self):
|
||||||
"""Return if switch is available."""
|
"""Return if switch is available.
|
||||||
return self.controller.available or \
|
|
||||||
self.client.sw_mac in self.controller.api.devices
|
Poe_mode None means its poe state is unknown.
|
||||||
|
Sw_mac unavailable means restored client.
|
||||||
|
"""
|
||||||
|
return self.poe_mode is None or self.client.sw_mac and (
|
||||||
|
self.controller.available or
|
||||||
|
self.client.sw_mac in self.controller.api.devices)
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs):
|
||||||
"""Enable POE for client."""
|
"""Enable POE for client."""
|
||||||
|
@ -14,6 +14,7 @@ from homeassistant import config_entries
|
|||||||
from homeassistant.components import unifi
|
from homeassistant.components import unifi
|
||||||
from homeassistant.components.unifi.const import (
|
from homeassistant.components.unifi.const import (
|
||||||
CONF_CONTROLLER, CONF_SITE_ID, UNIFI_CONFIG)
|
CONF_CONTROLLER, CONF_SITE_ID, UNIFI_CONFIG)
|
||||||
|
from homeassistant.helpers import entity_registry
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME, CONF_VERIFY_SSL)
|
CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME, CONF_VERIFY_SSL)
|
||||||
@ -249,7 +250,7 @@ async def setup_controller(hass, mock_controller):
|
|||||||
hass.data[unifi.DOMAIN] = {CONTROLLER_ID: mock_controller}
|
hass.data[unifi.DOMAIN] = {CONTROLLER_ID: mock_controller}
|
||||||
config_entry = config_entries.ConfigEntry(
|
config_entry = config_entries.ConfigEntry(
|
||||||
1, unifi.DOMAIN, 'Mock Title', ENTRY_CONFIG, 'test',
|
1, unifi.DOMAIN, 'Mock Title', ENTRY_CONFIG, 'test',
|
||||||
config_entries.CONN_CLASS_LOCAL_POLL)
|
config_entries.CONN_CLASS_LOCAL_POLL, entry_id=1)
|
||||||
mock_controller.config_entry = config_entry
|
mock_controller.config_entry = config_entry
|
||||||
|
|
||||||
await mock_controller.async_update()
|
await mock_controller.async_update()
|
||||||
@ -312,7 +313,7 @@ async def test_switches(hass, mock_controller):
|
|||||||
|
|
||||||
await setup_controller(hass, mock_controller)
|
await setup_controller(hass, mock_controller)
|
||||||
assert len(mock_controller.mock_requests) == 3
|
assert len(mock_controller.mock_requests) == 3
|
||||||
assert len(hass.states.async_all()) == 4
|
assert len(hass.states.async_all()) == 5
|
||||||
|
|
||||||
switch_1 = hass.states.get('switch.poe_client_1')
|
switch_1 = hass.states.get('switch.poe_client_1')
|
||||||
assert switch_1 is not None
|
assert switch_1 is not None
|
||||||
@ -450,3 +451,30 @@ async def test_ignore_multiple_poe_clients_on_same_port(hass, mock_controller):
|
|||||||
switch_2 = hass.states.get('switch.poe_client_2')
|
switch_2 = hass.states.get('switch.poe_client_2')
|
||||||
assert switch_1 is None
|
assert switch_1 is None
|
||||||
assert switch_2 is None
|
assert switch_2 is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_restoring_client(hass, mock_controller):
|
||||||
|
"""Test the update_items function with some clients."""
|
||||||
|
mock_controller.mock_client_responses.append([CLIENT_2])
|
||||||
|
mock_controller.mock_device_responses.append([DEVICE_1])
|
||||||
|
mock_controller.mock_client_all_responses.append([CLIENT_1])
|
||||||
|
mock_controller.unifi_config = {
|
||||||
|
unifi.CONF_BLOCK_CLIENT: ['random mac']
|
||||||
|
}
|
||||||
|
|
||||||
|
registry = await entity_registry.async_get_registry(hass)
|
||||||
|
registry.async_get_or_create(
|
||||||
|
switch.DOMAIN, unifi.DOMAIN,
|
||||||
|
'poe-{}'.format(CLIENT_1['mac']),
|
||||||
|
suggested_object_id=CLIENT_1['hostname'], config_entry_id=1)
|
||||||
|
registry.async_get_or_create(
|
||||||
|
switch.DOMAIN, unifi.DOMAIN,
|
||||||
|
'poe-{}'.format(CLIENT_2['mac']),
|
||||||
|
suggested_object_id=CLIENT_2['hostname'], config_entry_id=1)
|
||||||
|
|
||||||
|
await setup_controller(hass, mock_controller)
|
||||||
|
assert len(mock_controller.mock_requests) == 3
|
||||||
|
assert len(hass.states.async_all()) == 3
|
||||||
|
|
||||||
|
device_1 = hass.states.get('switch.client_1')
|
||||||
|
assert device_1 is not None
|
||||||
|
Loading…
x
Reference in New Issue
Block a user