Coverage for custom_components/appliance_autonotify/discovery.py: 96%
47 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-10-12 15:40 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-10-12 15:40 +0000
1"""Find appliances with an identifiable cycle, and set each up or offer it as a discovered config entry"""
3from __future__ import annotations
5from dataclasses import dataclass
6from typing import TYPE_CHECKING
8from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_INTEGRATION_DISCOVERY
9from homeassistant.const import CONF_DEVICE_ID, CONF_NAME, CONF_TYPE
10from homeassistant.core import Event, HomeAssistant, callback
11from homeassistant.helpers import device_registry as dr
12from homeassistant.helpers import discovery_flow
13from homeassistant.helpers import entity_registry as er
15from .const import CONF_AUTO_DISCOVER, CYCLE_KEYS, DOMAIN, TYPE_DISCOVERY
17if TYPE_CHECKING:
18 from homeassistant.helpers.entity_registry import EventEntityRegistryUpdatedData
21@dataclass(frozen=True)
22class Appliance:
23 device_id: str
24 name: str
27@callback
28def find_appliances(hass: HomeAssistant) -> list[Appliance]:
29 """Devices of the known platforms that have a cycle state entity"""
30 entity_registry = er.async_get(hass)
31 device_registry = dr.async_get(hass)
32 found: dict[str, Appliance] = {}
33 for entity in entity_registry.entities.values():
34 if entity.device_id is None or entity.disabled or entity.translation_key is None:
35 continue
36 if CYCLE_KEYS.get(entity.platform) != entity.translation_key:
37 continue
38 device = device_registry.async_get(entity.device_id)
39 if device is not None:
40 found[device.id] = Appliance(device.id, device.name_by_user or device.name or entity.entity_id)
41 return list(found.values())
44@callback
45def auto_discovery(hass: HomeAssistant) -> bool:
46 """Whether appliances found are set up without asking, so not while the discovery entry is disabled or deleted"""
47 return any(
48 entry.data.get(CONF_TYPE) == TYPE_DISCOVERY and entry.options.get(CONF_AUTO_DISCOVER)
49 for entry in hass.config_entries.async_entries(DOMAIN, include_disabled=False)
50 )
53@callback
54def async_discover(hass: HomeAssistant) -> None:
55 """Set up every appliance found, or with automatic discovery off, offer each to be added"""
56 source = SOURCE_IMPORT if auto_discovery(hass) else SOURCE_INTEGRATION_DISCOVERY
57 for appliance in find_appliances(hass):
58 # appliances already configured, ignored or in progress are dropped by the flow's unique id
59 discovery_flow.async_create_flow(
60 hass,
61 DOMAIN,
62 context={"source": source},
63 data={CONF_DEVICE_ID: appliance.device_id, CONF_NAME: appliance.name},
64 )
67@callback
68def async_start_discovery(hass: HomeAssistant) -> None:
69 """Look for appliances now, and again whenever a known platform adds an entity"""
71 @callback
72 def entity_registry_updated(event: Event[EventEntityRegistryUpdatedData]) -> None:
73 if event.data["action"] != "create":
74 return
75 entity = er.async_get(hass).async_get(event.data["entity_id"])
76 if entity is not None and entity.platform in CYCLE_KEYS:
77 async_discover(hass)
79 hass.bus.async_listen(er.EVENT_ENTITY_REGISTRY_UPDATED, entity_registry_updated)
80 async_discover(hass)