mirror of https://github.com/home-assistant/core
Move DPI Group logic to UniFi switch platform (#58761)
* Library has normalized management of DPI apps and groups, move logic to UniFi integration * Bump dependency to v29 * Use a generator instead of a list - Pylint * Minor improvements * Improve doc strings
This commit is contained in:
parent
b71a22557d
commit
e37456fb36
|
@ -246,11 +246,7 @@ class UniFiController:
|
||||||
)
|
)
|
||||||
|
|
||||||
elif DATA_DPI_GROUP in data:
|
elif DATA_DPI_GROUP in data:
|
||||||
for key in data[DATA_DPI_GROUP]:
|
async_dispatcher_send(self.hass, self.signal_update)
|
||||||
if self.api.dpi_groups[key].dpiapp_ids:
|
|
||||||
async_dispatcher_send(self.hass, self.signal_update)
|
|
||||||
else:
|
|
||||||
async_dispatcher_send(self.hass, self.signal_remove, {key})
|
|
||||||
|
|
||||||
elif DATA_DPI_GROUP_REMOVED in data:
|
elif DATA_DPI_GROUP_REMOVED in data:
|
||||||
async_dispatcher_send(
|
async_dispatcher_send(
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/unifi",
|
"documentation": "https://www.home-assistant.io/integrations/unifi",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"aiounifi==28"
|
"aiounifi==29"
|
||||||
],
|
],
|
||||||
"codeowners": [
|
"codeowners": [
|
||||||
"@Kane610"
|
"@Kane610"
|
||||||
|
|
|
@ -4,6 +4,8 @@ Support for controlling power supply of clients which are powered over Ethernet
|
||||||
Support for controlling network access of clients selected in option flow.
|
Support for controlling network access of clients selected in option flow.
|
||||||
Support for controlling deep packet inspection (DPI) restriction groups.
|
Support for controlling deep packet inspection (DPI) restriction groups.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aiounifi.api import SOURCE_EVENT
|
from aiounifi.api import SOURCE_EVENT
|
||||||
|
@ -332,11 +334,57 @@ class UniFiDPIRestrictionSwitch(UniFiBase, SwitchEntity):
|
||||||
|
|
||||||
_attr_entity_category = EntityCategory.CONFIG
|
_attr_entity_category = EntityCategory.CONFIG
|
||||||
|
|
||||||
|
def __init__(self, dpi_group, controller):
|
||||||
|
"""Set up dpi switch."""
|
||||||
|
super().__init__(dpi_group, controller)
|
||||||
|
|
||||||
|
self._is_enabled = self.calculate_enabled()
|
||||||
|
self._known_app_ids = dpi_group.dpiapp_ids
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def key(self) -> Any:
|
def key(self) -> Any:
|
||||||
"""Return item key."""
|
"""Return item key."""
|
||||||
return self._item.id
|
return self._item.id
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Register callback to known apps."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
|
apps = self.controller.api.dpi_apps
|
||||||
|
for app_id in self._item.dpiapp_ids:
|
||||||
|
apps[app_id].register_callback(self.async_update_callback)
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Remove registered callbacks."""
|
||||||
|
apps = self.controller.api.dpi_apps
|
||||||
|
for app_id in self._item.dpiapp_ids:
|
||||||
|
apps[app_id].remove_callback(self.async_update_callback)
|
||||||
|
|
||||||
|
await super().async_will_remove_from_hass()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_callback(self) -> None:
|
||||||
|
"""Update the DPI switch state.
|
||||||
|
|
||||||
|
Remove entity when no apps are paired with group.
|
||||||
|
Register callbacks to new apps.
|
||||||
|
Calculate and update entity state if it has changed.
|
||||||
|
"""
|
||||||
|
if not self._item.dpiapp_ids:
|
||||||
|
self.hass.loop.create_task(self.remove_item({self.key}))
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._known_app_ids != self._item.dpiapp_ids:
|
||||||
|
self._known_app_ids = self._item.dpiapp_ids
|
||||||
|
|
||||||
|
apps = self.controller.api.dpi_apps
|
||||||
|
for app_id in self._item.dpiapp_ids:
|
||||||
|
apps[app_id].register_callback(self.async_update_callback)
|
||||||
|
|
||||||
|
if (enabled := self.calculate_enabled()) != self._is_enabled:
|
||||||
|
self._is_enabled = enabled
|
||||||
|
super().async_update_callback()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Return a unique identifier for this switch."""
|
"""Return a unique identifier for this switch."""
|
||||||
|
@ -344,28 +392,46 @@ class UniFiDPIRestrictionSwitch(UniFiBase, SwitchEntity):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
"""Return the name of the client."""
|
"""Return the name of the DPI group."""
|
||||||
return self._item.name
|
return self._item.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self):
|
def icon(self):
|
||||||
"""Return the icon to use in the frontend."""
|
"""Return the icon to use in the frontend."""
|
||||||
if self._item.enabled:
|
if self._is_enabled:
|
||||||
return "mdi:network"
|
return "mdi:network"
|
||||||
return "mdi:network-off"
|
return "mdi:network-off"
|
||||||
|
|
||||||
|
def calculate_enabled(self) -> bool:
|
||||||
|
"""Calculate if all apps are enabled."""
|
||||||
|
return all(
|
||||||
|
self.controller.api.dpi_apps[app_id].enabled
|
||||||
|
for app_id in self._item.dpiapp_ids
|
||||||
|
if app_id in self.controller.api.dpi_apps
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Return true if client is allowed to connect."""
|
"""Return true if DPI group app restriction is enabled."""
|
||||||
return self._item.enabled
|
return self._is_enabled
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs):
|
||||||
"""Turn on connectivity for client."""
|
"""Restrict access of apps related to DPI group."""
|
||||||
await self.controller.api.dpi_groups.async_enable(self._item)
|
return await asyncio.gather(
|
||||||
|
*[
|
||||||
|
self.controller.api.dpi_apps.async_enable(app_id)
|
||||||
|
for app_id in self._item.dpiapp_ids
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs):
|
async def async_turn_off(self, **kwargs):
|
||||||
"""Turn off connectivity for client."""
|
"""Remove restriction of apps related to DPI group."""
|
||||||
await self.controller.api.dpi_groups.async_disable(self._item)
|
return await asyncio.gather(
|
||||||
|
*[
|
||||||
|
self.controller.api.dpi_apps.async_disable(app_id)
|
||||||
|
for app_id in self._item.dpiapp_ids
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
async def options_updated(self) -> None:
|
async def options_updated(self) -> None:
|
||||||
"""Config entry options are updated, remove entity if option is disabled."""
|
"""Config entry options are updated, remove entity if option is disabled."""
|
||||||
|
|
|
@ -272,7 +272,7 @@ aiosyncthing==0.5.1
|
||||||
aiotractive==0.5.2
|
aiotractive==0.5.2
|
||||||
|
|
||||||
# homeassistant.components.unifi
|
# homeassistant.components.unifi
|
||||||
aiounifi==28
|
aiounifi==29
|
||||||
|
|
||||||
# homeassistant.components.vlc_telnet
|
# homeassistant.components.vlc_telnet
|
||||||
aiovlc==0.1.0
|
aiovlc==0.1.0
|
||||||
|
|
|
@ -204,7 +204,7 @@ aiosyncthing==0.5.1
|
||||||
aiotractive==0.5.2
|
aiotractive==0.5.2
|
||||||
|
|
||||||
# homeassistant.components.unifi
|
# homeassistant.components.unifi
|
||||||
aiounifi==28
|
aiounifi==29
|
||||||
|
|
||||||
# homeassistant.components.vlc_telnet
|
# homeassistant.components.vlc_telnet
|
||||||
aiovlc==0.1.0
|
aiovlc==0.1.0
|
||||||
|
|
|
@ -288,7 +288,42 @@ DPI_GROUP_REMOVED_EVENT = {
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"_id": "5f976f4ae3c58f018ec7dff6",
|
"_id": "5f976f4ae3c58f018ec7dff6",
|
||||||
"name": "dpi group",
|
"name": "Block Media Streaming",
|
||||||
|
"site_id": "name",
|
||||||
|
"dpiapp_ids": [],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
DPI_GROUP_CREATED_EVENT = {
|
||||||
|
"meta": {"rc": "ok", "message": "dpigroup:add"},
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"name": "Block Media Streaming",
|
||||||
|
"site_id": "name",
|
||||||
|
"_id": "5f976f4ae3c58f018ec7dff6",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
DPI_GROUP_ADDED_APP = {
|
||||||
|
"meta": {"rc": "ok", "message": "dpigroup:sync"},
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"_id": "5f976f4ae3c58f018ec7dff6",
|
||||||
|
"name": "Block Media Streaming",
|
||||||
|
"site_id": "name",
|
||||||
|
"dpiapp_ids": ["5f976f62e3c58f018ec7e17d"],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
DPI_GROUP_REMOVE_APP = {
|
||||||
|
"meta": {"rc": "ok", "message": "dpigroup:sync"},
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"_id": "5f976f4ae3c58f018ec7dff6",
|
||||||
|
"name": "Block Media Streaming",
|
||||||
"site_id": "name",
|
"site_id": "name",
|
||||||
"dpiapp_ids": [],
|
"dpiapp_ids": [],
|
||||||
}
|
}
|
||||||
|
@ -599,6 +634,82 @@ async def test_dpi_switches(hass, aioclient_mock, mock_unifi_websocket):
|
||||||
|
|
||||||
assert hass.states.get("switch.block_media_streaming").state == STATE_OFF
|
assert hass.states.get("switch.block_media_streaming").state == STATE_OFF
|
||||||
|
|
||||||
|
mock_unifi_websocket(data=DPI_GROUP_REMOVE_APP)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert hass.states.get("switch.block_media_streaming") is None
|
||||||
|
assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_dpi_switches_add_second_app(hass, aioclient_mock, mock_unifi_websocket):
|
||||||
|
"""Test the update_items function with some clients."""
|
||||||
|
await setup_unifi_integration(
|
||||||
|
hass,
|
||||||
|
aioclient_mock,
|
||||||
|
dpigroup_response=DPI_GROUPS,
|
||||||
|
dpiapp_response=DPI_APPS,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 1
|
||||||
|
assert hass.states.get("switch.block_media_streaming").state == STATE_ON
|
||||||
|
|
||||||
|
second_app_event = {
|
||||||
|
"meta": {"rc": "ok", "message": "dpiapp:add"},
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"apps": [524292],
|
||||||
|
"blocked": False,
|
||||||
|
"cats": [],
|
||||||
|
"enabled": False,
|
||||||
|
"log": False,
|
||||||
|
"site_id": "name",
|
||||||
|
"_id": "61783e89c1773a18c0c61f00",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
mock_unifi_websocket(data=second_app_event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert hass.states.get("switch.block_media_streaming").state == STATE_ON
|
||||||
|
|
||||||
|
add_second_app_to_group = {
|
||||||
|
"meta": {"rc": "ok", "message": "dpigroup:sync"},
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"_id": "5f976f4ae3c58f018ec7dff6",
|
||||||
|
"name": "Block Media Streaming",
|
||||||
|
"site_id": "name",
|
||||||
|
"dpiapp_ids": ["5f976f62e3c58f018ec7e17d", "61783e89c1773a18c0c61f00"],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_unifi_websocket(data=add_second_app_to_group)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert hass.states.get("switch.block_media_streaming").state == STATE_OFF
|
||||||
|
|
||||||
|
second_app_event_enabled = {
|
||||||
|
"meta": {"rc": "ok", "message": "dpiapp:sync"},
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"apps": [524292],
|
||||||
|
"blocked": False,
|
||||||
|
"cats": [],
|
||||||
|
"enabled": True,
|
||||||
|
"log": False,
|
||||||
|
"site_id": "name",
|
||||||
|
"_id": "61783e89c1773a18c0c61f00",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
mock_unifi_websocket(data=second_app_event_enabled)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert hass.states.get("switch.block_media_streaming").state == STATE_ON
|
||||||
|
|
||||||
|
|
||||||
async def test_new_client_discovered_on_block_control(
|
async def test_new_client_discovered_on_block_control(
|
||||||
hass, aioclient_mock, mock_unifi_websocket
|
hass, aioclient_mock, mock_unifi_websocket
|
||||||
|
|
Loading…
Reference in New Issue