Files
localthings/tests/test_switch_device_class.py
T
Marc Billow 84465aee72 water_purifier, range: drop invalid device_class='lock' from switches
SwitchDeviceClass only ever supported 'outlet'/'switch', not 'lock'.
switch.py passes desc.device_class straight to SwitchDeviceClass(...),
so any board with these hrefs raised ValueError during switch platform
setup and lost every switch entity for the device, not just the lock
ones (issue #349, TP2X_WATERPURIFIER_20K).

KIDS_LOCK_GENERIC/_VS_FALLBACK dodged this same bug (issues #181/#183)
by moving to a read-only BinarySensorDesc, but the water-purifier and
cooktop locks are genuinely writable, so they stay SwitchDesc and just
drop the invalid device_class (with an mdi:lock icon standing in for
the one entity_category=config gave them for free).

Added a registry-wide test that instantiates SwitchDeviceClass for
every SwitchDesc.device_class across every by_type registry, so a
future capability can't reintroduce the same crash.
2026-08-10 16:33:39 +00:00

46 lines
1.8 KiB
Python

"""Guards against a SwitchDesc.device_class HA's SwitchDeviceClass doesn't
recognize -- switch.py passes it straight to SwitchDeviceClass(...), and an
invalid value raises out of the whole switch platform's async_setup_entry,
taking down every switch entity for the device, not just the offending one
(issue #349: water_purifier.py's lock switches used device_class='lock',
which SwitchDeviceClass only ever supported as 'outlet'/'switch').
Scans every by_type registry's declared capabilities rather than a specific
fixture, so a new capability introducing the same mistake fails here instead
of only surfacing as a live crash report.
"""
import importlib
import pkgutil
from homeassistant.components.switch import SwitchDeviceClass
from custom_components.localthings.registry import by_type
from custom_components.localthings.registry.entities import SwitchDesc
def _all_registries():
for mod_info in pkgutil.iter_modules(by_type.__path__):
if mod_info.name.startswith("_"):
continue
mod = importlib.import_module(
f"custom_components.localthings.registry.by_type.{mod_info.name}"
)
reg = getattr(mod, "REGISTRY", None)
if reg is not None:
yield reg
def test_every_switchdesc_device_class_is_valid_for_ha():
bad = []
for reg in _all_registries():
caps = [c for cs in reg.capabilities.values() for c in cs] + list(reg.pattern_capabilities)
for cap in caps:
for entity in cap.entities:
if isinstance(entity, SwitchDesc) and entity.device_class is not None:
try:
SwitchDeviceClass(entity.device_class)
except ValueError:
bad.append((reg.name, entity.key, entity.device_class))
assert bad == []