mirror of
https://github.com/home-assistant/core.git
synced 2025-07-09 14:27:07 +00:00
Fix data in old SkyConnect integration config entries or delete them (#141959)
* Delete old SkyConnect integration config entries * Try migrating, if possible * Do not delete config entries, log a failure
This commit is contained in:
parent
a7c43f9b49
commit
2b9c903429
@ -6,14 +6,29 @@ import logging
|
|||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
from homeassistant.components.homeassistant_hardware.util import guess_firmware_info
|
from homeassistant.components.homeassistant_hardware.util import guess_firmware_info
|
||||||
from homeassistant.components.usb import USBDevice, async_register_port_event_callback
|
from homeassistant.components.usb import (
|
||||||
|
USBDevice,
|
||||||
|
async_register_port_event_callback,
|
||||||
|
scan_serial_ports,
|
||||||
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import DESCRIPTION, DEVICE, DOMAIN, FIRMWARE, FIRMWARE_VERSION, PRODUCT
|
from .const import (
|
||||||
|
DESCRIPTION,
|
||||||
|
DEVICE,
|
||||||
|
DOMAIN,
|
||||||
|
FIRMWARE,
|
||||||
|
FIRMWARE_VERSION,
|
||||||
|
MANUFACTURER,
|
||||||
|
PID,
|
||||||
|
PRODUCT,
|
||||||
|
SERIAL_NUMBER,
|
||||||
|
VID,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -73,7 +88,7 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
|
|||||||
"""Migrate old entry."""
|
"""Migrate old entry."""
|
||||||
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Migrating from version %s:%s", config_entry.version, config_entry.minor_version
|
"Migrating from version %s.%s", config_entry.version, config_entry.minor_version
|
||||||
)
|
)
|
||||||
|
|
||||||
if config_entry.version == 1:
|
if config_entry.version == 1:
|
||||||
@ -108,6 +123,43 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
|
|||||||
minor_version=3,
|
minor_version=3,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if config_entry.minor_version == 3:
|
||||||
|
# Old SkyConnect config entries were missing keys
|
||||||
|
if any(
|
||||||
|
key not in config_entry.data
|
||||||
|
for key in (VID, PID, MANUFACTURER, PRODUCT, SERIAL_NUMBER)
|
||||||
|
):
|
||||||
|
serial_ports = await hass.async_add_executor_job(scan_serial_ports)
|
||||||
|
serial_ports_info = {port.device: port for port in serial_ports}
|
||||||
|
device = config_entry.data[DEVICE]
|
||||||
|
|
||||||
|
if not (usb_info := serial_ports_info.get(device)):
|
||||||
|
raise HomeAssistantError(
|
||||||
|
f"USB device {device} is missing, cannot migrate"
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
config_entry,
|
||||||
|
data={
|
||||||
|
**config_entry.data,
|
||||||
|
VID: usb_info.vid,
|
||||||
|
PID: usb_info.pid,
|
||||||
|
MANUFACTURER: usb_info.manufacturer,
|
||||||
|
PRODUCT: usb_info.description,
|
||||||
|
DESCRIPTION: usb_info.description,
|
||||||
|
SERIAL_NUMBER: usb_info.serial_number,
|
||||||
|
},
|
||||||
|
version=1,
|
||||||
|
minor_version=4,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Existing entries are migrated by just incrementing the version
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
config_entry,
|
||||||
|
version=1,
|
||||||
|
minor_version=4,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Migration to version %s.%s successful",
|
"Migration to version %s.%s successful",
|
||||||
config_entry.version,
|
config_entry.version,
|
||||||
|
@ -81,7 +81,7 @@ class HomeAssistantSkyConnectConfigFlow(
|
|||||||
"""Handle a config flow for Home Assistant SkyConnect."""
|
"""Handle a config flow for Home Assistant SkyConnect."""
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
MINOR_VERSION = 3
|
MINOR_VERSION = 4
|
||||||
|
|
||||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
"""Initialize the config flow."""
|
"""Initialize the config flow."""
|
||||||
|
@ -9,7 +9,15 @@ from homeassistant.components.homeassistant_hardware.util import (
|
|||||||
ApplicationType,
|
ApplicationType,
|
||||||
FirmwareInfo,
|
FirmwareInfo,
|
||||||
)
|
)
|
||||||
from homeassistant.components.homeassistant_sky_connect.const import DOMAIN
|
from homeassistant.components.homeassistant_sky_connect.const import (
|
||||||
|
DESCRIPTION,
|
||||||
|
DOMAIN,
|
||||||
|
MANUFACTURER,
|
||||||
|
PID,
|
||||||
|
PRODUCT,
|
||||||
|
SERIAL_NUMBER,
|
||||||
|
VID,
|
||||||
|
)
|
||||||
from homeassistant.components.usb import USBDevice
|
from homeassistant.components.usb import USBDevice
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
|
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
|
||||||
@ -57,7 +65,7 @@ async def test_config_entry_migration_v2(hass: HomeAssistant) -> None:
|
|||||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
|
||||||
assert config_entry.version == 1
|
assert config_entry.version == 1
|
||||||
assert config_entry.minor_version == 3
|
assert config_entry.minor_version == 4
|
||||||
assert config_entry.data == {
|
assert config_entry.data == {
|
||||||
"description": "SkyConnect v1.0",
|
"description": "SkyConnect v1.0",
|
||||||
"device": "/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0",
|
"device": "/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0",
|
||||||
@ -187,3 +195,102 @@ async def test_usb_device_reactivity(hass: HomeAssistant) -> None:
|
|||||||
# The integration has reloaded and is now in a failed state
|
# The integration has reloaded and is now in a failed state
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
assert config_entry.state is ConfigEntryState.SETUP_RETRY
|
assert config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
|
|
||||||
|
async def test_bad_config_entry_fixing(hass: HomeAssistant) -> None:
|
||||||
|
"""Test fixing/deleting config entries with bad data."""
|
||||||
|
|
||||||
|
# Newly-added ZBT-1
|
||||||
|
new_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
unique_id="some_unique_id-9e2adbd75b8beb119fe564a0f320645d",
|
||||||
|
data={
|
||||||
|
"device": "/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0",
|
||||||
|
"vid": "10C4",
|
||||||
|
"pid": "EA60",
|
||||||
|
"serial_number": "9e2adbd75b8beb119fe564a0f320645d",
|
||||||
|
"manufacturer": "Nabu Casa",
|
||||||
|
"product": "SkyConnect v1.0",
|
||||||
|
"firmware": "ezsp",
|
||||||
|
"firmware_version": "7.4.4.0 (build 123)",
|
||||||
|
},
|
||||||
|
version=1,
|
||||||
|
minor_version=3,
|
||||||
|
)
|
||||||
|
|
||||||
|
new_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
# Old config entry, without firmware info
|
||||||
|
old_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
unique_id="some_unique_id-3c0ed67c628beb11b1cd64a0f320645d",
|
||||||
|
data={
|
||||||
|
"device": "/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_3c0ed67c628beb11b1cd64a0f320645d-if00-port0",
|
||||||
|
"vid": "10C4",
|
||||||
|
"pid": "EA60",
|
||||||
|
"serial_number": "3c0ed67c628beb11b1cd64a0f320645d",
|
||||||
|
"manufacturer": "Nabu Casa",
|
||||||
|
"description": "SkyConnect v1.0",
|
||||||
|
},
|
||||||
|
version=1,
|
||||||
|
minor_version=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
old_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
# Bad config entry, missing most keys
|
||||||
|
bad_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
unique_id="some_unique_id-9f6c4bba657cc9a4f0cea48bc5948562",
|
||||||
|
data={
|
||||||
|
"device": "/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9f6c4bba657cc9a4f0cea48bc5948562-if00-port0",
|
||||||
|
},
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
bad_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
# Bad config entry, missing most keys, but fixable since the device is present
|
||||||
|
fixable_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
unique_id="some_unique_id-4f5f3b26d59f8714a78b599690741999",
|
||||||
|
data={
|
||||||
|
"device": "/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_4f5f3b26d59f8714a78b599690741999-if00-port0",
|
||||||
|
},
|
||||||
|
version=1,
|
||||||
|
minor_version=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
fixable_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.homeassistant_sky_connect.scan_serial_ports",
|
||||||
|
return_value=[
|
||||||
|
USBDevice(
|
||||||
|
device="/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_4f5f3b26d59f8714a78b599690741999-if00-port0",
|
||||||
|
vid="10C4",
|
||||||
|
pid="EA60",
|
||||||
|
serial_number="4f5f3b26d59f8714a78b599690741999",
|
||||||
|
manufacturer="Nabu Casa",
|
||||||
|
description="SkyConnect v1.0",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
):
|
||||||
|
await async_setup_component(hass, "homeassistant_sky_connect", {})
|
||||||
|
|
||||||
|
assert hass.config_entries.async_get_entry(new_entry.entry_id) is not None
|
||||||
|
assert hass.config_entries.async_get_entry(old_entry.entry_id) is not None
|
||||||
|
assert hass.config_entries.async_get_entry(fixable_entry.entry_id) is not None
|
||||||
|
|
||||||
|
updated_entry = hass.config_entries.async_get_entry(fixable_entry.entry_id)
|
||||||
|
assert updated_entry is not None
|
||||||
|
assert updated_entry.data[VID] == "10C4"
|
||||||
|
assert updated_entry.data[PID] == "EA60"
|
||||||
|
assert updated_entry.data[SERIAL_NUMBER] == "4f5f3b26d59f8714a78b599690741999"
|
||||||
|
assert updated_entry.data[MANUFACTURER] == "Nabu Casa"
|
||||||
|
assert updated_entry.data[PRODUCT] == "SkyConnect v1.0"
|
||||||
|
assert updated_entry.data[DESCRIPTION] == "SkyConnect v1.0"
|
||||||
|
|
||||||
|
untouched_bad_entry = hass.config_entries.async_get_entry(bad_entry.entry_id)
|
||||||
|
assert untouched_bad_entry.minor_version == 3
|
||||||
|
Loading…
x
Reference in New Issue
Block a user