Skip to content

Coerce null gateway identifier to empty string - #1070

Closed
SBoudrias wants to merge 1 commit into
dvd-dev:mainfrom
SBoudrias:fix/gateway-null-identifier
Closed

SBoudrias wants to merge 1 commit into
dvd-dev:mainfrom
SBoudrias:fix/gateway-null-identifier

Conversation

@SBoudrias

Copy link
Copy Markdown

Problem

Since ~2026-09-10, the Hilo cloud returns a null serial/identifier for the Hilo gateway in the device payloads. HiloDevice.update() stores identifier=None, and every entity constructor in this integration builds unique ids as f"{device.identifier.lower()}-...". On None this raises:

2026-10-09 14:18:38.825 ERROR (MainThread) [homeassistant.components.sensor] Error while setting up hilo platform for sensor: 'NoneType' object has no attribute 'lower'
Traceback (most recent call last):
  File "/config/custom_components/hilo/sensor.py", line 193, in async_setup_entry
    new_entities.extend(generate_entities_from_device(d, hilo, scan_interval))
  File "/config/custom_components/hilo/sensor.py", line 116, in generate_entities_from_device
    HiloChallengeSensor(hilo, device, scan_interval),
  File "/config/custom_components/hilo/sensor.py", line 988, in __init__
    self._attr_unique_id = f"{device.identifier.lower()}-{slugify(self._attr_name)}"
AttributeError: 'NoneType' object has no attribute 'lower'

Because the gateway entities are constructed first, the entire sensor platform fails: every Hilo sensor (challenge, rates, reward, weather, meter power) goes unavailable. Additionally, check_tarif() then can't find sensor.hilo_energy_total_low and warns hourly.

Reproduced on every boot from 2026-09-10 to 2026-10-09 (14 boots), hilo 2026.8.3 / python-hilo 2026.3.5. A user in #1044 hit a neighbouring breakage after the 2026.9.3 release.

The same class of hole exists in _build_gateway_dict (python-hilo), which emits identifier: None when the MAC can't be extracted from hiloId.

Fix

After devices.update() in Hilo.async_init, coerce identifier is None to the empty string. The empty string is what the cloud used to return, so existing device-registry entries and entity unique_ids (e.g. -defi_hilo) keep matching — no duplicate devices or entities are created.

Companion guard at the library layer: dvd-dev/python-hilo#462.

Testing

  • Full suite passes: 24 passed
  • ruff check/format clean
  • Validated live on our host since 2026-10-09 (this exact patch on top of 2026.8.3): all 35 Hilo sensors restored, the AttributeError is gone from the boot log, no duplicate registry entries.

The Hilo cloud returns a null identifier for some devices (observed
for the gateway since ~2026-09-10). Every entity constructor builds
unique_ids as f"{device.identifier.lower()}-..." and would crash with
AttributeError, killing the whole hilo sensor platform at boot.

Coerce None to the empty string after devices.update() in async_init:
the empty string is what the cloud used to return, so existing
device-registry entries and entity unique_ids keep matching. Companion
guard in python-hilo (dvd-dev/python-hilo#462) covers the library
layer for older payloads.
@ic-dev21

ic-dev21 commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

I'll edit my original answer to be more precise:

Thanks for the detailed write-up. This comes from the versions you’re running, not from a current bug.

On hilo 2026.8.3 / python-hilo 2026.3.5, the gateway identifier comes from the REST Gateways/Info call (dsn field). That path has been deprecated, and a null dsn there gives exactly the traceback you posted. Since hilo 2026.9.1 (python-hilo ≥ 2026.9.1), the REST call is no longer used: the gateway is built from the initial GraphQL query, with its MAC address (from hiloId) as the identifier. I’ve tested fresh installs and restarts on current releases and can’t reproduce this.

I won’t merge this PR as-is, for two reasons:

On main, the gateway is created in graphql_helper.async_init(), which runs after the loop this PR adds. So the coercion never reaches the gateway.
Coercing to "" would stop the DSN→MAC registry migration from running, because an empty legacy identifier is treated as “nothing to migrate.”

Please upgrade to 2026.10.1. Since your existing entities are keyed on an empty identifier (-defi_hilo etc.), they may not migrate automatically. If you end up with duplicates or orphaned entities after upgrading, please open an issue and we’ll handle that case properly.

@SBoudrias

Copy link
Copy Markdown
Author

Merci — you're right on both counts, and thanks for the precise explanation of the ordering (graphql_helper.async_init runs after the loop) and the '' coercion blocking the DSN→MAC migration.

We took your suggested path today: upgraded to hilo 2026.10.1 (+ python-hilo 2026.9.1). Our existing entities were indeed keyed on the empty identifier (-defi_hilo etc.) and did not migrate automatically — the upgrade created MAC-keyed entities alongside the legacy ones. We resolved it with a one-time entity-registry migration (delete the legacy-keyed entries, rename the MAC-keyed ones onto the historic entity_ids), after which everything is live and clean: all sensors restored, check_tarif tariff sensors created, zero duplicates and zero orphaned entities since. Closing this PR as moot — no issue to open since the duplicates case didn't occur. Thanks again for the quick and thorough review!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants