Migrate unique ID in vesync switches (#137099)

This commit is contained in:
cdnninja 2025-02-03 07:44:49 -07:00 committed by GitHub
parent b5662ded2c
commit 37461d727a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 117 additions and 2 deletions

View File

@ -7,6 +7,7 @@ from pyvesync import VeSync
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.dispatcher import async_dispatcher_send
from .common import async_generate_device_list
@ -91,3 +92,37 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.pop(DOMAIN)
return unload_ok
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Migrate old entry."""
_LOGGER.debug(
"Migrating VeSync config entry: %s minor version: %s",
config_entry.version,
config_entry.minor_version,
)
if config_entry.minor_version == 1:
# Migrate switch/outlets entity to a new unique ID
_LOGGER.debug("Migrating VeSync config entry from version 1 to version 2")
entity_registry = er.async_get(hass)
registry_entries = er.async_entries_for_config_entry(
entity_registry, config_entry.entry_id
)
for reg_entry in registry_entries:
if "-" not in reg_entry.unique_id and reg_entry.entity_id.startswith(
Platform.SWITCH
):
_LOGGER.debug(
"Migrating switch/outlet entity from unique_id: %s to unique_id: %s",
reg_entry.unique_id,
reg_entry.unique_id + "-device_status",
)
entity_registry.async_update_entity(
reg_entry.entity_id,
new_unique_id=reg_entry.unique_id + "-device_status",
)
else:
_LOGGER.debug("Skipping entity with unique_id: %s", reg_entry.unique_id)
hass.config_entries.async_update_entry(config_entry, minor_version=2)
return True

View File

@ -24,6 +24,7 @@ class VeSyncFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a config flow."""
VERSION = 1
MINOR_VERSION = 2
@callback
def _show_form(self, errors: dict[str, str] | None = None) -> ConfigFlowResult:

View File

@ -83,6 +83,7 @@ class VeSyncSwitchHA(VeSyncBaseSwitch, SwitchEntity):
) -> None:
"""Initialize the VeSync switch device."""
super().__init__(plug, coordinator)
self._attr_unique_id = f"{super().unique_id}-device_status"
self.smartplug = plug
@ -94,4 +95,5 @@ class VeSyncLightSwitch(VeSyncBaseSwitch, SwitchEntity):
) -> None:
"""Initialize Light Switch device class."""
super().__init__(switch, coordinator)
self._attr_unique_id = f"{super().unique_id}-device_status"
self.switch = switch

View File

@ -153,3 +153,25 @@ async def humidifier_config_entry(
await hass.async_block_till_done()
return entry
@pytest.fixture(name="switch_old_id_config_entry")
async def switch_old_id_config_entry(
hass: HomeAssistant, requests_mock: requests_mock.Mocker, config
) -> MockConfigEntry:
"""Create a mock VeSync config entry for `switch` with the old unique ID approach."""
entry = MockConfigEntry(
title="VeSync",
domain=DOMAIN,
data=config[DOMAIN],
version=1,
minor_version=1,
)
entry.add_to_hass(hass)
wall_switch = "Wall Switch"
humidifer = "Humidifier 200s"
mock_multiple_device_responses(requests_mock, [wall_switch, humidifer])
return entry

View File

@ -367,7 +367,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'outlet',
'unique_id': 'outlet-device_status',
'unit_of_measurement': None,
}),
])
@ -525,7 +525,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'switch',
'unique_id': 'switch-device_status',
'unit_of_measurement': None,
}),
])

View File

@ -10,6 +10,9 @@ from homeassistant.components.vesync.const import DOMAIN, VS_DEVICES, VS_MANAGER
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry
async def test_async_setup_entry__not_login(
@ -125,3 +128,55 @@ async def test_async_new_device_discovery(
assert manager.login.call_count == 1
assert hass.data[DOMAIN][VS_MANAGER] == manager
assert hass.data[DOMAIN][VS_DEVICES] == [fan, humidifier]
async def test_migrate_config_entry(
hass: HomeAssistant,
switch_old_id_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test migration of config entry. Only migrates switches to a new unique_id."""
switch: er.RegistryEntry = entity_registry.async_get_or_create(
domain="switch",
platform="vesync",
unique_id="switch",
config_entry=switch_old_id_config_entry,
suggested_object_id="switch",
)
humidifer: er.RegistryEntry = entity_registry.async_get_or_create(
domain="humidifer",
platform="vesync",
unique_id="humidifer",
config_entry=switch_old_id_config_entry,
suggested_object_id="humidifer",
)
assert switch.unique_id == "switch"
assert switch_old_id_config_entry.minor_version == 1
assert humidifer.unique_id == "humidifer"
await hass.config_entries.async_setup(switch_old_id_config_entry.entry_id)
await hass.async_block_till_done()
assert switch_old_id_config_entry.minor_version == 2
migrated_switch = entity_registry.async_get(switch.entity_id)
assert migrated_switch is not None
assert migrated_switch.entity_id.startswith("switch")
assert migrated_switch.unique_id == "switch-device_status"
# Confirm humidifer was not impacted
migrated_humidifer = entity_registry.async_get(humidifer.entity_id)
assert migrated_humidifer is not None
assert migrated_humidifer.unique_id == "humidifer"
# Assert that only one entity exists in the switch domain
switch_entities = [
e for e in entity_registry.entities.values() if e.domain == "switch"
]
assert len(switch_entities) == 1
humidifer_entities = [
e for e in entity_registry.entities.values() if e.domain == "humidifer"
]
assert len(humidifer_entities) == 1