182 files
This commit is contained in:
@@ -80,6 +80,25 @@ PLATFORMS = COORDINATOR_AWARE_PLATFORMS + [
|
||||
Platform.SWITCH,
|
||||
]
|
||||
|
||||
# Suffixes used when building gateway-attached entity unique_ids in sensor.py
|
||||
# (e.g. f"{device.identifier.lower()}-{suffix}"). Used to migrate those
|
||||
# unique_ids when the gateway's identifier changes from DSN-based to MAC-based.
|
||||
GATEWAY_ENTITY_SUFFIXES = [
|
||||
"defi_hilo",
|
||||
"recompenses_hilo",
|
||||
"notifications_hilo",
|
||||
"outdoor_weather_hilo",
|
||||
"hilo_gateway",
|
||||
"hilo_rate_current",
|
||||
"hilo_rate_low",
|
||||
"hilo_rate_medium",
|
||||
"hilo_rate_high",
|
||||
"hilo_rate_access",
|
||||
"hilo_rate_low_threshold",
|
||||
"hilo_rate_reward_rate",
|
||||
"hilo_cost_total",
|
||||
]
|
||||
|
||||
|
||||
@callback
|
||||
def _async_standardize_config_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
@@ -124,9 +143,39 @@ def _async_register_custom_device(
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry( # noqa: C901
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
) -> bool:
|
||||
@callback
|
||||
def _async_migrate_gateway_device_identifier(
|
||||
hass: HomeAssistant, entry: ConfigEntry, old_dsn: str | None, new_mac: str
|
||||
) -> None:
|
||||
"""Migrate the gateway device registry entry from DSN-based to MAC-based identity.
|
||||
|
||||
Preserves the device's registry entry (and thus its device_id, so entities,
|
||||
dashboards, and automations referencing it stay attached) by renaming its
|
||||
identifier in place instead of letting a new device get created.
|
||||
"""
|
||||
if not old_dsn or old_dsn == new_mac:
|
||||
return
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
old_device = device_registry.async_get_device(identifiers={(DOMAIN, old_dsn)})
|
||||
if old_device is None:
|
||||
return # fresh install, or already migrated
|
||||
|
||||
new_device = device_registry.async_get_device(identifiers={(DOMAIN, new_mac)})
|
||||
if new_device is not None and new_device.id != old_device.id:
|
||||
LOG.warning(
|
||||
"Gateway device already registered under new identifier %s, skipping device migration",
|
||||
new_mac,
|
||||
)
|
||||
return
|
||||
|
||||
device_registry.async_update_device(
|
||||
old_device.id, new_identifiers={(DOMAIN, new_mac)}
|
||||
)
|
||||
LOG.info("Migrated gateway device identifier %s -> %s", old_dsn, new_mac)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Hilo as config entry."""
|
||||
HiloFlowHandler.async_register_implementation(
|
||||
hass, AuthCodeWithPKCEImplementation(hass)
|
||||
@@ -160,9 +209,7 @@ async def async_setup_entry( # noqa: C901
|
||||
|
||||
_async_standardize_config_entry(hass, entry)
|
||||
scan_interval = current_options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
|
||||
scan_interval = (
|
||||
scan_interval if scan_interval >= MIN_SCAN_INTERVAL else MIN_SCAN_INTERVAL
|
||||
)
|
||||
scan_interval = max(scan_interval, MIN_SCAN_INTERVAL)
|
||||
|
||||
hilo = Hilo(hass, entry, api)
|
||||
try:
|
||||
@@ -176,9 +223,7 @@ async def async_setup_entry( # noqa: C901
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = hilo
|
||||
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
# Note (ic-dev21): This is a bit of a hack to rename some entities that were created with non-standard names in early versions
|
||||
# HA has changed the way they name entities linked to a device by default and this breaks the naming scheme of the gateway entities.
|
||||
@@ -731,6 +776,29 @@ class Hilo:
|
||||
)
|
||||
return self._events[event_id]
|
||||
|
||||
async def _fetch_legacy_gateway_dsn(self, new_mac: str) -> str | None:
|
||||
"""This function looks up the Hilo gateway device in the device registry and
|
||||
returns its old DSN-based identifier if it exists. If it doesn't,
|
||||
it returns Noneto use the MAC address instead."""
|
||||
device_registry = dr.async_get(self._hass)
|
||||
for device in device_registry.devices.values():
|
||||
if device.manufacturer != "Hilo" or device.model != "EQ000017":
|
||||
continue
|
||||
for domain, identifier in device.identifiers:
|
||||
if domain == DOMAIN and identifier != new_mac:
|
||||
return identifier
|
||||
return None
|
||||
|
||||
@callback
|
||||
def async_migrate_gateway_entities(self, old_dsn: str | None, new_mac: str) -> None:
|
||||
"""Migrate gateway-attached entity unique_ids from DSN-based to MAC-based."""
|
||||
if not old_dsn or old_dsn == new_mac:
|
||||
return
|
||||
for suffix in GATEWAY_ENTITY_SUFFIXES:
|
||||
old_unique_id = f"{old_dsn.lower()}-{suffix}"
|
||||
new_unique_id = f"{new_mac.lower()}-{suffix}"
|
||||
self.async_migrate_unique_id(old_unique_id, new_unique_id, Platform.SENSOR)
|
||||
|
||||
async def async_init(self, scan_interval: int) -> None:
|
||||
"""Initialize the Hilo "manager" class.
|
||||
|
||||
@@ -772,10 +840,18 @@ class Hilo:
|
||||
)
|
||||
)
|
||||
|
||||
# Step 6: Register custom devices in HA
|
||||
_async_register_custom_device(
|
||||
self._hass, self.entry, self.devices.find_device(1)
|
||||
)
|
||||
# Step 6: Migrate gateway identity (DSN -> MAC) if needed, then register
|
||||
# custom devices in HA.
|
||||
gateway = self.devices.find_device(1)
|
||||
if gateway:
|
||||
old_dsn = await self._fetch_legacy_gateway_dsn(gateway.identifier)
|
||||
if old_dsn:
|
||||
_async_migrate_gateway_device_identifier(
|
||||
self._hass, self.entry, old_dsn, gateway.identifier
|
||||
)
|
||||
self.async_migrate_gateway_entities(old_dsn, gateway.identifier)
|
||||
_async_register_custom_device(self._hass, self.entry, gateway)
|
||||
|
||||
if self.track_unknown_sources:
|
||||
if not self.unknown_tracker_device:
|
||||
self.unknown_tracker_device = self.devices.generate_device(
|
||||
@@ -1122,7 +1198,7 @@ class Hilo:
|
||||
ATTR_UNIT_OF_MEASUREMENT: parent_unit, # note ic-dev21: now uses parent_unit directly
|
||||
ATTR_DEVICE_CLASS: SensorDeviceClass.ENERGY,
|
||||
}
|
||||
if not all(a in attrs.keys() for a in new_attrs.keys()):
|
||||
if not all(a in attrs.keys() for a in new_attrs):
|
||||
LOG.warning(
|
||||
f"Fixing utility sensor: {entity} {current_state} new_attrs: {new_attrs}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user