Migrate unique IDs of Rituals Perfume Genie (#92342)

* Migrate unique IDs of Rituals Perfume Genie

* Fix doc string
pull/92350/head
Franck Nijhof 2023-05-01 22:46:38 +02:00 committed by GitHub
parent 40896514eb
commit a7088e767e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 126 additions and 22 deletions

View File

@ -2,12 +2,13 @@
import asyncio
import aiohttp
from pyrituals import Account
from pyrituals import Account, Diffuser
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import ACCOUNT_HASH, DOMAIN
@ -32,6 +33,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except aiohttp.ClientError as err:
raise ConfigEntryNotReady from err
# Migrate old unique_ids to the new format
async_migrate_entities_unique_ids(hass, entry, account_devices)
# Create a coordinator for each diffuser
coordinators = {
diffuser.hublot: RitualsDataUpdateCoordinator(hass, diffuser)
@ -59,3 +63,38 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
@callback
def async_migrate_entities_unique_ids(
hass: HomeAssistant, config_entry: ConfigEntry, diffusers: list[Diffuser]
) -> None:
"""Migrate unique_ids in the entity registry to the new format."""
entity_registry = er.async_get(hass)
registry_entries = er.async_entries_for_config_entry(
entity_registry, config_entry.entry_id
)
conversion: dict[tuple[str, str], str] = {
(Platform.BINARY_SENSOR, " Battery Charging"): "charging",
(Platform.NUMBER, " Perfume Amount"): "perfume_amount",
(Platform.SELECT, " Room Size"): "room_size_square_meter",
(Platform.SENSOR, " Battery"): "battery_percentage",
(Platform.SENSOR, " Fill"): "fill",
(Platform.SENSOR, " Perfume"): "perfume",
(Platform.SENSOR, " Wifi"): "wifi_percentage",
(Platform.SWITCH, ""): "is_on",
}
for diffuser in diffusers:
for registry_entry in registry_entries:
if new_unique_id := conversion.get(
(
registry_entry.domain,
registry_entry.unique_id.removeprefix(diffuser.hublot),
)
):
entity_registry.async_update_entity(
registry_entry.entity_id,
new_unique_id=f"{diffuser.hublot}-{new_unique_id}",
)

View File

@ -43,6 +43,7 @@ class DiffuserBatteryChargingBinarySensor(DiffuserEntity, BinarySensorEntity):
def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None:
"""Initialize the battery charging binary sensor."""
super().__init__(coordinator, CHARGING_SUFFIX)
self._attr_unique_id = f"{coordinator.diffuser.hublot}-charging"
@property
def is_on(self) -> bool:

View File

@ -26,7 +26,6 @@ class DiffuserEntity(CoordinatorEntity[RitualsDataUpdateCoordinator]):
hubname = coordinator.diffuser.name
self._attr_name = f"{hubname}{entity_suffix}"
self._attr_unique_id = f"{hublot}{entity_suffix}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, hublot)},
manufacturer=MANUFACTURER,

View File

@ -40,6 +40,7 @@ class DiffuserPerfumeAmount(DiffuserEntity, NumberEntity):
def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None:
"""Initialize the diffuser perfume amount number."""
super().__init__(coordinator, PERFUME_AMOUNT_SUFFIX)
self._attr_unique_id = f"{coordinator.diffuser.hublot}-perfume_amount"
@property
def native_value(self) -> int:

View File

@ -43,6 +43,7 @@ class DiffuserRoomSize(DiffuserEntity, SelectEntity):
self._attr_entity_registry_enabled_default = (
self.coordinator.diffuser.has_battery
)
self._attr_unique_id = f"{coordinator.diffuser.hublot}-room_size_square_meter"
@property
def current_option(self) -> str:

View File

@ -48,6 +48,7 @@ class DiffuserPerfumeSensor(DiffuserEntity, SensorEntity):
def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None:
"""Initialize the perfume sensor."""
super().__init__(coordinator, PERFUME_SUFFIX)
self._attr_unique_id = f"{coordinator.diffuser.hublot}-perfume"
@property
def icon(self) -> str:
@ -68,6 +69,7 @@ class DiffuserFillSensor(DiffuserEntity, SensorEntity):
def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None:
"""Initialize the fill sensor."""
super().__init__(coordinator, FILL_SUFFIX)
self._attr_unique_id = f"{coordinator.diffuser.hublot}-fill"
@property
def icon(self) -> str:
@ -92,6 +94,7 @@ class DiffuserBatterySensor(DiffuserEntity, SensorEntity):
def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None:
"""Initialize the battery sensor."""
super().__init__(coordinator, BATTERY_SUFFIX)
self._attr_unique_id = f"{coordinator.diffuser.hublot}-battery_percentage"
@property
def native_value(self) -> int:
@ -108,6 +111,7 @@ class DiffuserWifiSensor(DiffuserEntity, SensorEntity):
def __init__(self, coordinator: RitualsDataUpdateCoordinator) -> None:
"""Initialize the wifi sensor."""
super().__init__(coordinator, WIFI_SUFFIX)
self._attr_unique_id = f"{coordinator.diffuser.hublot}-wifi_percentage"
@property
def native_value(self) -> int:

View File

@ -37,6 +37,7 @@ class DiffuserSwitch(DiffuserEntity, SwitchEntity):
"""Initialize the diffuser switch."""
super().__init__(coordinator, "")
self._attr_is_on = self.coordinator.diffuser.is_on
self._attr_unique_id = f"{coordinator.diffuser.hublot}-is_on"
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on."""

View File

@ -1,6 +1,5 @@
"""Tests for the Rituals Perfume Genie binary sensor platform."""
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.rituals_perfume_genie.binary_sensor import CHARGING_SUFFIX
from homeassistant.const import ATTR_DEVICE_CLASS, STATE_ON, EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
@ -30,5 +29,5 @@ async def test_binary_sensors(
entry = entity_registry.async_get("binary_sensor.genie_battery_charging")
assert entry
assert entry.unique_id == f"{hublot}{CHARGING_SUFFIX}"
assert entry.unique_id == f"{hublot}-charging"
assert entry.entity_category == EntityCategory.DIAGNOSTIC

View File

@ -6,8 +6,13 @@ import aiohttp
from homeassistant.components.rituals_perfume_genie.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from .common import init_integration, mock_config_entry
from .common import (
init_integration,
mock_config_entry,
mock_diffuser_v1_battery_cartridge,
)
async def test_config_entry_not_ready(hass: HomeAssistant) -> None:
@ -32,3 +37,65 @@ async def test_config_entry_unload(hass: HomeAssistant) -> None:
assert config_entry.state is ConfigEntryState.NOT_LOADED
assert config_entry.entry_id not in hass.data[DOMAIN]
async def test_entity_id_migration(
hass: HomeAssistant, entity_registry: er.RegistryEntry
) -> None:
"""Test the migration of unique IDs on config entry setup."""
config_entry = mock_config_entry(unique_id="binary_sensor_test_diffuser_v1")
# Pre-create old style unique IDs
charging = entity_registry.async_get_or_create(
"binary_sensor", DOMAIN, "lot123v1 Battery Charging", config_entry=config_entry
)
perfume_amount = entity_registry.async_get_or_create(
"number", DOMAIN, "lot123v1 Perfume Amount", config_entry=config_entry
)
room_size = entity_registry.async_get_or_create(
"select", DOMAIN, "lot123v1 Room Size", config_entry=config_entry
)
battery = entity_registry.async_get_or_create(
"sensor", DOMAIN, "lot123v1 Battery", config_entry=config_entry
)
fill = entity_registry.async_get_or_create(
"sensor", DOMAIN, "lot123v1 Fill", config_entry=config_entry
)
perfume = entity_registry.async_get_or_create(
"sensor", DOMAIN, "lot123v1 Perfume", config_entry=config_entry
)
wifi = entity_registry.async_get_or_create(
"sensor", DOMAIN, "lot123v1 Wifi", config_entry=config_entry
)
switch = entity_registry.async_get_or_create(
"switch", DOMAIN, "lot123v1", config_entry=config_entry
)
# Set up integration
diffuser = mock_diffuser_v1_battery_cartridge()
await init_integration(hass, config_entry, [diffuser])
# Check that old style unique IDs have been migrated
entry = entity_registry.async_get(charging.entity_id)
assert entry.unique_id == "lot123v1-charging"
entry = entity_registry.async_get(perfume_amount.entity_id)
assert entry.unique_id == "lot123v1-perfume_amount"
entry = entity_registry.async_get(room_size.entity_id)
assert entry.unique_id == "lot123v1-room_size_square_meter"
entry = entity_registry.async_get(battery.entity_id)
assert entry.unique_id == "lot123v1-battery_percentage"
entry = entity_registry.async_get(fill.entity_id)
assert entry.unique_id == "lot123v1-fill"
entry = entity_registry.async_get(perfume.entity_id)
assert entry.unique_id == "lot123v1-perfume"
entry = entity_registry.async_get(wifi.entity_id)
assert entry.unique_id == "lot123v1-wifi_percentage"
entry = entity_registry.async_get(switch.entity_id)
assert entry.unique_id == "lot123v1-is_on"

View File

@ -14,7 +14,6 @@ from homeassistant.components.number import (
from homeassistant.components.rituals_perfume_genie.number import (
MAX_PERFUME_AMOUNT,
MIN_PERFUME_AMOUNT,
PERFUME_AMOUNT_SUFFIX,
)
from homeassistant.const import ATTR_ENTITY_ID, ATTR_ICON
from homeassistant.core import HomeAssistant
@ -46,7 +45,7 @@ async def test_number_entity(
entry = entity_registry.async_get("number.genie_perfume_amount")
assert entry
assert entry.unique_id == f"{diffuser.hublot}{PERFUME_AMOUNT_SUFFIX}"
assert entry.unique_id == f"{diffuser.hublot}-perfume_amount"
async def test_set_number_value(hass: HomeAssistant) -> None:

View File

@ -2,7 +2,6 @@
import pytest
from homeassistant.components.homeassistant import SERVICE_UPDATE_ENTITY
from homeassistant.components.rituals_perfume_genie.select import ROOM_SIZE_SUFFIX
from homeassistant.components.select import (
ATTR_OPTION,
ATTR_OPTIONS,
@ -38,7 +37,7 @@ async def test_select_entity(
entry = entity_registry.async_get("select.genie_room_size")
assert entry
assert entry.unique_id == f"{diffuser.hublot}{ROOM_SIZE_SUFFIX}"
assert entry.unique_id == f"{diffuser.hublot}-room_size_square_meter"
assert entry.unit_of_measurement == AREA_SQUARE_METERS
assert entry.entity_category == EntityCategory.CONFIG

View File

@ -1,11 +1,5 @@
"""Tests for the Rituals Perfume Genie sensor platform."""
from homeassistant.components.rituals_perfume_genie.sensor import (
BATTERY_SUFFIX,
FILL_SUFFIX,
PERFUME_SUFFIX,
WIFI_SUFFIX,
SensorDeviceClass,
)
from homeassistant.components.rituals_perfume_genie.sensor import SensorDeviceClass
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_ICON,
@ -40,7 +34,7 @@ async def test_sensors_diffuser_v1_battery_cartridge(
entry = entity_registry.async_get("sensor.genie_perfume")
assert entry
assert entry.unique_id == f"{hublot}{PERFUME_SUFFIX}"
assert entry.unique_id == f"{hublot}-perfume"
state = hass.states.get("sensor.genie_fill")
assert state
@ -49,7 +43,7 @@ async def test_sensors_diffuser_v1_battery_cartridge(
entry = entity_registry.async_get("sensor.genie_fill")
assert entry
assert entry.unique_id == f"{hublot}{FILL_SUFFIX}"
assert entry.unique_id == f"{hublot}-fill"
state = hass.states.get("sensor.genie_battery")
assert state
@ -59,7 +53,7 @@ async def test_sensors_diffuser_v1_battery_cartridge(
entry = entity_registry.async_get("sensor.genie_battery")
assert entry
assert entry.unique_id == f"{hublot}{BATTERY_SUFFIX}"
assert entry.unique_id == f"{hublot}-battery_percentage"
assert entry.entity_category == EntityCategory.DIAGNOSTIC
state = hass.states.get("sensor.genie_wifi")
@ -70,7 +64,7 @@ async def test_sensors_diffuser_v1_battery_cartridge(
entry = entity_registry.async_get("sensor.genie_wifi")
assert entry
assert entry.unique_id == f"{hublot}{WIFI_SUFFIX}"
assert entry.unique_id == f"{hublot}-wifi_percentage"
assert entry.entity_category == EntityCategory.DIAGNOSTIC

View File

@ -38,7 +38,7 @@ async def test_switch_entity(
entry = entity_registry.async_get("switch.genie")
assert entry
assert entry.unique_id == diffuser.hublot
assert entry.unique_id == f"{diffuser.hublot}-is_on"
async def test_switch_handle_coordinator_update(hass: HomeAssistant) -> None: