Migrate unique IDs of Rituals Perfume Genie (#92342)
* Migrate unique IDs of Rituals Perfume Genie * Fix doc stringpull/92350/head
parent
40896514eb
commit
a7088e767e
|
@ -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}",
|
||||
)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue