Change AsusWRT entities unique id (#97066)

Migrate AsusWRT entities unique id
This commit is contained in:
ollo69 2023-07-24 19:53:58 +02:00 committed by GitHub
parent 17e757af36
commit 345df715d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 55 deletions

View file

@ -20,7 +20,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util
from homeassistant.util import dt as dt_util, slugify
from .bridge import AsusWrtBridge, WrtDevice
from .const import (
@ -39,7 +39,6 @@ from .const import (
)
CONF_REQ_RELOAD = [CONF_DNSMASQ, CONF_INTERFACE, CONF_REQUIRE_IP]
DEFAULT_NAME = "Asuswrt"
SCAN_INTERVAL = timedelta(seconds=30)
@ -179,6 +178,44 @@ class AsusWrtRouter:
self.hass, dict(self._entry.data), self._options
)
def _migrate_entities_unique_id(self) -> None:
"""Migrate router entities to new unique id format."""
_ENTITY_MIGRATION_ID = {
"sensor_connected_device": "Devices Connected",
"sensor_rx_bytes": "Download",
"sensor_tx_bytes": "Upload",
"sensor_rx_rates": "Download Speed",
"sensor_tx_rates": "Upload Speed",
"sensor_load_avg1": "Load Avg (1m)",
"sensor_load_avg5": "Load Avg (5m)",
"sensor_load_avg15": "Load Avg (15m)",
"2.4GHz": "2.4GHz Temperature",
"5.0GHz": "5GHz Temperature",
"CPU": "CPU Temperature",
}
entity_reg = er.async_get(self.hass)
router_entries = er.async_entries_for_config_entry(
entity_reg, self._entry.entry_id
)
migrate_entities: dict[str, str] = {}
for entry in router_entries:
if entry.domain == TRACKER_DOMAIN:
continue
old_unique_id = entry.unique_id
if not old_unique_id.startswith(DOMAIN):
continue
for new_id, old_id in _ENTITY_MIGRATION_ID.items():
if old_unique_id.endswith(old_id):
migrate_entities[entry.entity_id] = slugify(
f"{self.unique_id}_{new_id}"
)
break
for entity_id, unique_id in migrate_entities.items():
entity_reg.async_update_entity(entity_id, new_unique_id=unique_id)
async def setup(self) -> None:
"""Set up a AsusWrt router."""
try:
@ -215,6 +252,9 @@ class AsusWrtRouter:
self._devices[device_mac] = AsusWrtDevInfo(device_mac, entry.original_name)
# Migrate entities to new unique id format
self._migrate_entities_unique_id()
# Update devices
await self.update_devices()
@ -364,14 +404,9 @@ class AsusWrtRouter:
return self._api.host
@property
def unique_id(self) -> str | None:
def unique_id(self) -> str:
"""Return router unique id."""
return self._entry.unique_id
@property
def name(self) -> str:
"""Return router name."""
return self.host if self.unique_id else DEFAULT_NAME
return self._entry.unique_id or self._entry.entry_id
@property
def devices(self) -> dict[str, AsusWrtDevInfo]:

View file

@ -22,6 +22,7 @@ from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from homeassistant.util import slugify
from .const import (
DATA_ASUSWRT,
@ -182,6 +183,9 @@ async def async_setup_entry(
class AsusWrtSensor(CoordinatorEntity, SensorEntity):
"""Representation of a AsusWrt sensor."""
entity_description: AsusWrtSensorEntityDescription
_attr_has_entity_name = True
def __init__(
self,
coordinator: DataUpdateCoordinator,
@ -190,13 +194,9 @@ class AsusWrtSensor(CoordinatorEntity, SensorEntity):
) -> None:
"""Initialize a AsusWrt sensor."""
super().__init__(coordinator)
self.entity_description: AsusWrtSensorEntityDescription = description
self.entity_description = description
self._attr_name = f"{router.name} {description.name}"
if router.unique_id:
self._attr_unique_id = f"{DOMAIN} {router.unique_id} {description.name}"
else:
self._attr_unique_id = f"{DOMAIN} {self.name}"
self._attr_unique_id = slugify(f"{router.unique_id}_{description.key}")
self._attr_device_info = router.device_info
self._attr_extra_state_attributes = {"hostname": router.host}

View file

@ -9,9 +9,13 @@ from homeassistant.components import device_tracker, sensor
from homeassistant.components.asuswrt.const import (
CONF_INTERFACE,
DOMAIN,
MODE_ROUTER,
PROTOCOL_TELNET,
SENSORS_BYTES,
SENSORS_LOAD_AVG,
SENSORS_RATES,
SENSORS_TEMPERATURES,
)
from homeassistant.components.asuswrt.router import DEFAULT_NAME
from homeassistant.components.device_tracker import CONF_CONSIDER_HOME
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import (
@ -43,7 +47,7 @@ CONFIG_DATA = {
CONF_PROTOCOL: PROTOCOL_TELNET,
CONF_USERNAME: "user",
CONF_PASSWORD: "pwd",
CONF_MODE: "router",
CONF_MODE: MODE_ROUTER,
}
MAC_ADDR = "a1:b2:c3:d4:e5:f6"
@ -57,26 +61,8 @@ MOCK_MAC_2 = "A2:B2:C2:D2:E2:F2"
MOCK_MAC_3 = "A3:B3:C3:D3:E3:F3"
MOCK_MAC_4 = "A4:B4:C4:D4:E4:F4"
SENSORS_DEFAULT = [
"Download Speed",
"Download",
"Upload Speed",
"Upload",
]
SENSORS_LOADAVG = [
"Load Avg (1m)",
"Load Avg (5m)",
"Load Avg (15m)",
]
SENSORS_TEMP = [
"2.4GHz Temperature",
"5GHz Temperature",
"CPU Temperature",
]
SENSORS_ALL = [*SENSORS_DEFAULT, *SENSORS_LOADAVG, *SENSORS_TEMP]
SENSORS_DEFAULT = [*SENSORS_BYTES, *SENSORS_RATES]
SENSORS_ALL = [*SENSORS_DEFAULT, *SENSORS_LOAD_AVG, *SENSORS_TEMPERATURES]
PATCH_SETUP_ENTRY = patch(
"homeassistant.components.asuswrt.async_setup_entry",
@ -105,7 +91,7 @@ def mock_available_temps_fixture():
@pytest.fixture(name="create_device_registry_devices")
def create_device_registry_devices_fixture(hass):
def create_device_registry_devices_fixture(hass: HomeAssistant):
"""Create device registry devices so the device tracker entities are enabled when added."""
dev_reg = dr.async_get(hass)
config_entry = MockConfigEntry(domain="something_else")
@ -182,7 +168,7 @@ def mock_controller_connect_sens_fail():
yield service_mock
def _setup_entry(hass, config, sensors, unique_id=None):
def _setup_entry(hass: HomeAssistant, config, sensors, unique_id=None):
"""Create mock config entry with enabled sensors."""
entity_reg = er.async_get(hass)
@ -195,16 +181,17 @@ def _setup_entry(hass, config, sensors, unique_id=None):
)
# init variable
obj_prefix = slugify(HOST if unique_id else DEFAULT_NAME)
obj_prefix = slugify(HOST)
sensor_prefix = f"{sensor.DOMAIN}.{obj_prefix}"
unique_id_prefix = slugify(unique_id or config_entry.entry_id)
# Pre-enable the status sensor
for sensor_name in sensors:
sensor_id = slugify(sensor_name)
for sensor_key in sensors:
sensor_id = slugify(sensor_key)
entity_reg.async_get_or_create(
sensor.DOMAIN,
DOMAIN,
f"{DOMAIN} {unique_id or DEFAULT_NAME} {sensor_name}",
f"{unique_id_prefix}_{sensor_id}",
suggested_object_id=f"{obj_prefix}_{sensor_id}",
config_entry=config_entry,
disabled_by=None,
@ -255,10 +242,10 @@ async def test_sensors(
assert hass.states.get(f"{device_tracker.DOMAIN}.test").state == STATE_HOME
assert hass.states.get(f"{device_tracker.DOMAIN}.testtwo").state == STATE_HOME
assert hass.states.get(f"{sensor_prefix}_download_speed").state == "160.0"
assert hass.states.get(f"{sensor_prefix}_download").state == "60.0"
assert hass.states.get(f"{sensor_prefix}_upload_speed").state == "80.0"
assert hass.states.get(f"{sensor_prefix}_upload").state == "50.0"
assert hass.states.get(f"{sensor_prefix}_sensor_rx_rates").state == "160.0"
assert hass.states.get(f"{sensor_prefix}_sensor_rx_bytes").state == "60.0"
assert hass.states.get(f"{sensor_prefix}_sensor_tx_rates").state == "80.0"
assert hass.states.get(f"{sensor_prefix}_sensor_tx_bytes").state == "50.0"
assert hass.states.get(f"{sensor_prefix}_devices_connected").state == "2"
# remove first tracked device
@ -296,7 +283,7 @@ async def test_loadavg_sensors(
connect,
) -> None:
"""Test creating an AsusWRT load average sensors."""
config_entry, sensor_prefix = _setup_entry(hass, CONFIG_DATA, SENSORS_LOADAVG)
config_entry, sensor_prefix = _setup_entry(hass, CONFIG_DATA, SENSORS_LOAD_AVG)
config_entry.add_to_hass(hass)
# initial devices setup
@ -306,9 +293,9 @@ async def test_loadavg_sensors(
await hass.async_block_till_done()
# assert temperature sensor available
assert hass.states.get(f"{sensor_prefix}_load_avg_1m").state == "1.1"
assert hass.states.get(f"{sensor_prefix}_load_avg_5m").state == "1.2"
assert hass.states.get(f"{sensor_prefix}_load_avg_15m").state == "1.3"
assert hass.states.get(f"{sensor_prefix}_sensor_load_avg1").state == "1.1"
assert hass.states.get(f"{sensor_prefix}_sensor_load_avg5").state == "1.2"
assert hass.states.get(f"{sensor_prefix}_sensor_load_avg15").state == "1.3"
async def test_temperature_sensors(
@ -316,7 +303,7 @@ async def test_temperature_sensors(
connect,
) -> None:
"""Test creating a AsusWRT temperature sensors."""
config_entry, sensor_prefix = _setup_entry(hass, CONFIG_DATA, SENSORS_TEMP)
config_entry, sensor_prefix = _setup_entry(hass, CONFIG_DATA, SENSORS_TEMPERATURES)
config_entry.add_to_hass(hass)
# initial devices setup
@ -326,9 +313,9 @@ async def test_temperature_sensors(
await hass.async_block_till_done()
# assert temperature sensor available
assert hass.states.get(f"{sensor_prefix}_2_4ghz_temperature").state == "40.0"
assert not hass.states.get(f"{sensor_prefix}_5ghz_temperature")
assert hass.states.get(f"{sensor_prefix}_cpu_temperature").state == "71.2"
assert hass.states.get(f"{sensor_prefix}_2_4ghz").state == "40.0"
assert not hass.states.get(f"{sensor_prefix}_5_0ghz")
assert hass.states.get(f"{sensor_prefix}_cpu").state == "71.2"
@pytest.mark.parametrize(
@ -396,3 +383,31 @@ async def test_options_reload(hass: HomeAssistant, connect) -> None:
assert setup_entry_call.called
assert config_entry.state is ConfigEntryState.LOADED
async def test_unique_id_migration(hass: HomeAssistant, connect) -> None:
"""Test AsusWRT entities unique id format migration."""
config_entry = MockConfigEntry(
domain=DOMAIN,
data=CONFIG_DATA,
unique_id=MAC_ADDR,
)
config_entry.add_to_hass(hass)
entity_reg = er.async_get(hass)
obj_entity_id = slugify(f"{HOST} Upload")
entity_reg.async_get_or_create(
sensor.DOMAIN,
DOMAIN,
f"{DOMAIN} {MAC_ADDR} Upload",
suggested_object_id=obj_entity_id,
config_entry=config_entry,
disabled_by=None,
)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
migr_entity = entity_reg.async_get(f"{sensor.DOMAIN}.{obj_entity_id}")
assert migr_entity is not None
assert migr_entity.unique_id == slugify(f"{MAC_ADDR}_sensor_tx_bytes")