1
mirror of https://github.com/home-assistant/core synced 2024-09-28 03:04:04 +02:00

Rework some UniFi unique IDs (#104390)

This commit is contained in:
Robert Svensson 2023-11-24 11:18:55 +01:00 committed by GitHub
parent d4450c6c55
commit 6271fe333d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 144 additions and 14 deletions

View File

@ -17,14 +17,15 @@ from aiounifi.models.client import Client
from aiounifi.models.device import Device
from aiounifi.models.event import Event, EventKey
from homeassistant.components.device_tracker import ScannerEntity, SourceType
from homeassistant.components.device_tracker import DOMAIN, ScannerEntity, SourceType
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Event as core_Event, HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
import homeassistant.helpers.entity_registry as er
import homeassistant.util.dt as dt_util
from .controller import UniFiController
from .controller import UNIFI_DOMAIN, UniFiController
from .entity import (
HandlerT,
UnifiEntity,
@ -175,7 +176,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiTrackerEntityDescription, ...] = (
object_fn=lambda api, obj_id: api.clients[obj_id],
should_poll=False,
supported_fn=lambda controller, obj_id: True,
unique_id_fn=lambda controller, obj_id: f"{obj_id}-{controller.site}",
unique_id_fn=lambda controller, obj_id: f"{controller.site}-{obj_id}",
ip_address_fn=lambda api, obj_id: api.clients[obj_id].ip,
hostname_fn=lambda api, obj_id: api.clients[obj_id].hostname,
),
@ -201,12 +202,37 @@ ENTITY_DESCRIPTIONS: tuple[UnifiTrackerEntityDescription, ...] = (
)
@callback
def async_update_unique_id(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
"""Normalize client unique ID to have a prefix rather than suffix.
Introduced with release 2023.12.
"""
controller: UniFiController = hass.data[UNIFI_DOMAIN][config_entry.entry_id]
ent_reg = er.async_get(hass)
@callback
def update_unique_id(obj_id: str) -> None:
"""Rework unique ID."""
new_unique_id = f"{controller.site}-{obj_id}"
if ent_reg.async_get_entity_id(DOMAIN, UNIFI_DOMAIN, new_unique_id):
return
unique_id = f"{obj_id}-{controller.site}"
if entity_id := ent_reg.async_get_entity_id(DOMAIN, UNIFI_DOMAIN, unique_id):
ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id)
for obj_id in list(controller.api.clients) + list(controller.api.clients_all):
update_unique_id(obj_id)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up device tracker for UniFi Network integration."""
async_update_unique_id(hass, config_entry)
UniFiController.register_platform(
hass, config_entry, async_add_entities, UnifiScannerEntity, ENTITY_DESCRIPTIONS
)

View File

@ -42,9 +42,10 @@ from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
import homeassistant.helpers.entity_registry as er
from .const import ATTR_MANUFACTURER
from .controller import UniFiController
from .controller import UNIFI_DOMAIN, UniFiController
from .entity import (
HandlerT,
SubscriptionT,
@ -199,12 +200,6 @@ class UnifiSwitchEntityDescription(
only_event_for_state_change: bool = False
def _make_unique_id(obj_id: str, type_name: str) -> str:
"""Split an object id by the first underscore and interpose the given type."""
prefix, _, suffix = obj_id.partition("_")
return f"{prefix}-{type_name}-{suffix}"
ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = (
UnifiSwitchEntityDescription[Clients, Client](
key="Block client",
@ -262,7 +257,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = (
object_fn=lambda api, obj_id: api.outlets[obj_id],
should_poll=False,
supported_fn=async_outlet_supports_switching_fn,
unique_id_fn=lambda controller, obj_id: _make_unique_id(obj_id, "outlet"),
unique_id_fn=lambda controller, obj_id: f"outlet-{obj_id}",
),
UnifiSwitchEntityDescription[PortForwarding, PortForward](
key="Port forward control",
@ -303,7 +298,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = (
object_fn=lambda api, obj_id: api.ports[obj_id],
should_poll=False,
supported_fn=lambda controller, obj_id: controller.api.ports[obj_id].port_poe,
unique_id_fn=lambda controller, obj_id: _make_unique_id(obj_id, "poe"),
unique_id_fn=lambda controller, obj_id: f"poe-{obj_id}",
),
UnifiSwitchEntityDescription[Wlans, Wlan](
key="WLAN control",
@ -328,12 +323,41 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = (
)
@callback
def async_update_unique_id(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
"""Normalize switch unique ID to have a prefix rather than midfix.
Introduced with release 2023.12.
"""
controller: UniFiController = hass.data[UNIFI_DOMAIN][config_entry.entry_id]
ent_reg = er.async_get(hass)
@callback
def update_unique_id(obj_id: str, type_name: str) -> None:
"""Rework unique ID."""
new_unique_id = f"{type_name}-{obj_id}"
if ent_reg.async_get_entity_id(DOMAIN, UNIFI_DOMAIN, new_unique_id):
return
prefix, _, suffix = obj_id.partition("_")
unique_id = f"{prefix}-{type_name}-{suffix}"
if entity_id := ent_reg.async_get_entity_id(DOMAIN, UNIFI_DOMAIN, unique_id):
ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id)
for obj_id in controller.api.outlets:
update_unique_id(obj_id, "outlet")
for obj_id in controller.api.ports:
update_unique_id(obj_id, "poe")
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up switches for UniFi Network integration."""
async_update_unique_id(hass, config_entry)
UniFiController.register_platform(
hass,
config_entry,

View File

@ -939,13 +939,20 @@ async def test_restoring_client(
)
registry = er.async_get(hass)
registry.async_get_or_create(
registry.async_get_or_create( # Unique ID updated
TRACKER_DOMAIN,
UNIFI_DOMAIN,
f'{restored["mac"]}-site_id',
suggested_object_id=restored["hostname"],
config_entry=config_entry,
)
registry.async_get_or_create( # Unique ID already updated
TRACKER_DOMAIN,
UNIFI_DOMAIN,
f'site_id-{client["mac"]}',
suggested_object_id=client["hostname"],
config_entry=config_entry,
)
await setup_unifi_integration(
hass,

View File

@ -5,6 +5,7 @@ from datetime import timedelta
from aiounifi.models.message import MessageKey
import pytest
from homeassistant import config_entries
from homeassistant.components.switch import (
DOMAIN as SWITCH_DOMAIN,
SERVICE_TURN_OFF,
@ -32,7 +33,12 @@ from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_registry import RegistryEntryDisabler
from homeassistant.util import dt as dt_util
from .test_controller import CONTROLLER_HOST, SITE, setup_unifi_integration
from .test_controller import (
CONTROLLER_HOST,
ENTRY_CONFIG,
SITE,
setup_unifi_integration,
)
from tests.common import async_fire_time_changed
from tests.test_util.aiohttp import AiohttpClientMocker
@ -1585,3 +1591,70 @@ async def test_port_forwarding_switches(
mock_unifi_websocket(message=MessageKey.PORT_FORWARD_DELETED, data=_data)
await hass.async_block_till_done()
assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 0
async def test_updating_unique_id(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Verify outlet control and poe control unique ID update works."""
poe_device = {
"board_rev": 3,
"device_id": "mock-id",
"ip": "10.0.0.1",
"last_seen": 1562600145,
"mac": "00:00:00:00:01:01",
"model": "US16P150",
"name": "switch",
"state": 1,
"type": "usw",
"version": "4.0.42.10433",
"port_table": [
{
"media": "GE",
"name": "Port 1",
"port_idx": 1,
"poe_caps": 7,
"poe_class": "Class 4",
"poe_enable": True,
"poe_mode": "auto",
"poe_power": "2.56",
"poe_voltage": "53.40",
"portconf_id": "1a1",
"port_poe": True,
"up": True,
},
],
}
config_entry = config_entries.ConfigEntry(
version=1,
domain=UNIFI_DOMAIN,
title="Mock Title",
data=ENTRY_CONFIG,
source="test",
options={},
entry_id="1",
)
registry = er.async_get(hass)
registry.async_get_or_create(
SWITCH_DOMAIN,
UNIFI_DOMAIN,
f'{poe_device["mac"]}-poe-1',
suggested_object_id="switch_port_1_poe",
config_entry=config_entry,
)
registry.async_get_or_create(
SWITCH_DOMAIN,
UNIFI_DOMAIN,
f'{OUTLET_UP1["mac"]}-outlet-1',
suggested_object_id="plug_outlet_1",
config_entry=config_entry,
)
await setup_unifi_integration(
hass, aioclient_mock, devices_response=[poe_device, OUTLET_UP1]
)
assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 2
assert hass.states.get("switch.switch_port_1_poe")
assert hass.states.get("switch.plug_outlet_1")