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}"
|
||||
)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -12,5 +12,5 @@
|
||||
"iot_class": "cloud_push",
|
||||
"issue_tracker": "https://github.com/dvd-dev/hilo/issues",
|
||||
"requirements": ["python-hilo>=2026.3.5"],
|
||||
"version": "2026.8.1"
|
||||
"version": "2026.8.2"
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ from homeassistant.components.sensor import (
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
CONF_SCAN_INTERVAL,
|
||||
CURRENCY_DOLLAR,
|
||||
PERCENTAGE,
|
||||
@@ -24,6 +23,7 @@ from homeassistant.const import (
|
||||
Platform,
|
||||
UnitOfEnergy,
|
||||
UnitOfPower,
|
||||
UnitOfRatio,
|
||||
UnitOfSoundPressure,
|
||||
UnitOfTemperature,
|
||||
__short_version__ as current_version,
|
||||
@@ -271,7 +271,7 @@ class Co2Sensor(HiloEntity, SensorEntity):
|
||||
"""Define a Co2 sensor entity."""
|
||||
|
||||
_attr_device_class = SensorDeviceClass.CO2
|
||||
_attr_native_unit_of_measurement = CONCENTRATION_PARTS_PER_MILLION
|
||||
_attr_native_unit_of_measurement = UnitOfRatio.PARTS_PER_MILLION
|
||||
_attr_state_class = SensorStateClass.MEASUREMENT
|
||||
|
||||
def __init__(self, hilo, device):
|
||||
|
||||
Reference in New Issue
Block a user