Skip to content

Coerce null identifier to empty string - #462

Closed
SBoudrias wants to merge 2 commits into
dvd-dev:mainfrom
SBoudrias:fix/null-identifier
Closed

SBoudrias wants to merge 2 commits into
dvd-dev:mainfrom
SBoudrias:fix/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. HiloDevice.update() stores identifier=None, and the HA integration (dvd-dev/hilo) builds every entity unique id as f"{device.identifier.lower()}-...". On None this raises AttributeError: 'NoneType' object has no attribute 'lower', and because the gateway entities are constructed first, the entire hilo sensor platform fails to set up — every Hilo sensor (challenge, rates, reward, weather, meter power) goes unavailable until the integration is fixed.

Observed on our host every boot from 2026-09-10 to 2026-10-09 (14 boots), on python-hilo 2026.3.5 + hilo 2026.8.3:

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'

Fix

Coerce None back to the empty string in HiloDevice.update(). 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.

Also checked: _build_gateway_dict in graphql.py can still emit identifier: None when the MAC can't be extracted from hiloId; with this guard in place that path is safe too.

Testing

  • New regression test: TestHiloDevice::test_identifier_none_coerces_to_empty_string
  • Full suite: 67 passed
  • Validated live on our host since 2026-10-09 (the equivalent coercion patch in the HA integration): all 35 Hilo sensors restored, no crash in the boot log since.

The Hilo cloud returns a null serial/identifier for some devices
(observed for the Hilo gateway since ~2026-09-10). HiloDevice.update()
stored None, and the HA integration builds entity unique ids as
f"{device.identifier.lower()}-...", crashing with AttributeError and
taking down the whole hilo sensor platform on every boot.

Coerce None back to the empty string, which is what the cloud used to
return and what existing device registries / unique ids were built
from.
Comment thread pyhilo/device/__init__.py
new_val = val # type: ignore
if att == "identifier" and new_val is None:
# The cloud returns a null serial for some devices (observed
# for the Hilo gateway since ~2026-09-10). Consumers build

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this date "2026-09-10" is today? Or is it September 10th?

If it is today, I'd strongly recommend always using ISO date format to avoid confusion.

The declared kwargs type excludes None, but the cloud really does
send nulls (that is the bug being guarded). Explain and ignore
locally on the assignment.
@SBoudrias
SBoudrias force-pushed the fix/null-identifier branch from 6723bb4 to 6ffa3ec Compare October 9, 2026 20:54
@SBoudrias

Copy link
Copy Markdown
Author

Hi — the mypy 'unreachable' failure from the first push is fixed (commit 6ffa3ec: explained type: ignore[unreachable] on the guard, since the cloud genuinely sends nulls beyond the declared kwargs type; mypy, ruff and the 67-test suite pass locally). The new CI runs sit in 'action_required' awaiting first-time-contributor workflow approval — could you approve/re-run when you get a chance? Merci!

@SBoudrias

Copy link
Copy Markdown
Author

Merci for the review over on dvd-dev/hilo#1070 — closing this companion PR as moot. We upgraded to hilo 2026.10.1 (+ python-hilo 2026.9.1) today, so the deprecated REST gateway path (the null dsn source) is no longer used and the guard isn't needed. Our legacy empty-keyed entities didn't migrate automatically; we resolved them with a one-time entity-registry migration onto the historic entity_ids, and everything has been clean since — no duplicates, no orphans. Thanks again for the detailed explanation of the graphql path!

@SBoudrias SBoudrias closed this Oct 9, 2026
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