Initil after Upgrade

This commit is contained in:
2026-06-15 10:53:52 -04:00
parent 2fe9bf0dd6
commit 887feaa50a
143 changed files with 2288 additions and 881 deletions
+62 -24
View File
@@ -1,17 +1,17 @@
from __future__ import annotations
from collections.abc import Awaitable, Callable
from datetime import timedelta
from datetime import datetime, timedelta
from enum import StrEnum
import logging
import re
from typing import Any
from typing import Any, TypeVar
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.config_entries import SOURCE_INTEGRATION_DISCOVERY, SOURCE_USER, ConfigEntry
from homeassistant.const import CONF_ENTITY_ID, CONF_PLATFORM, CONF_UNIQUE_ID
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers import discovery_flow
import homeassistant.helpers.device_registry as dr
from homeassistant.helpers.entity import EntityCategory
@@ -33,13 +33,22 @@ from .const import (
MANUFACTURER_WLED,
CalculationStrategy,
)
from .group_include.filter import CategoryFilter, CompositeFilter, DomainFilter, FilterOperator, LambdaFilter, NotFilter, get_filtered_entity_list
from .group_include.filter import (
CategoryFilter,
CompositeFilter,
DomainFilter,
FilterOperator,
LambdaFilter,
NotFilter,
get_filtered_entity_list,
)
from .helpers import get_or_create_unique_id
from .power_profile.factory import get_power_profile
from .power_profile.library import ModelInfo, ProfileLibrary
from .power_profile.power_profile import SUPPORTED_DOMAINS, DeviceType, DiscoveryBy, PowerProfile
_LOGGER = logging.getLogger(__name__)
_DiscoverySourceT = TypeVar("_DiscoverySourceT", er.RegistryEntry, dr.DeviceEntry)
async def get_power_profile_by_source_entity(hass: HomeAssistant, source_entity: SourceEntity) -> PowerProfile | None:
@@ -102,6 +111,7 @@ class DiscoveryManager:
self.library: ProfileLibrary | None = None
self._exclude_device_types = exclude_device_types or []
self._exclude_self_usage_profiles = exclude_self_usage_profiles or False
self._cancel_rediscover_interval: CALLBACK_TYPE | None = None
self._status = DiscoveryStatus.NOT_STARTED if enabled else DiscoveryStatus.DISABLED
async def setup(self) -> None:
@@ -112,14 +122,18 @@ class DiscoveryManager:
await self.start_discovery()
async def _rediscover(_: Any) -> None: # noqa: ANN401
async def _rediscover(_: datetime) -> None:
"""Rediscover entities."""
await self.update_library_and_rediscover()
async_track_time_interval(
if self._cancel_rediscover_interval: # pragma: no cover
self._cancel_rediscover_interval()
self._cancel_rediscover_interval = async_track_time_interval(
self.hass,
_rediscover,
timedelta(hours=2),
cancel_on_shutdown=True,
)
async def update_library_and_rediscover(self) -> None:
@@ -130,6 +144,9 @@ class DiscoveryManager:
async def start_discovery(self) -> None:
"""Start the discovery procedure."""
if self._status == DiscoveryStatus.DISABLED:
_LOGGER.debug("Discovery manager is disabled, skipping discovery run")
return
if self._status == DiscoveryStatus.IN_PROGRESS:
_LOGGER.debug("Discovery already in progress, skipping new discovery run")
return
@@ -139,10 +156,10 @@ class DiscoveryManager:
_LOGGER.debug("Start auto discovery")
_LOGGER.debug("Start entity discovery")
await self.perform_discovery(self.get_entities, self.create_entity_source, DiscoveryBy.ENTITY) # type: ignore[arg-type]
await self.perform_discovery(self.get_entities, self.create_entity_source, DiscoveryBy.ENTITY)
_LOGGER.debug("Start device discovery")
await self.perform_discovery(self.get_devices, self.create_device_source, DiscoveryBy.DEVICE) # type: ignore[arg-type]
await self.perform_discovery(self.get_devices, self.create_device_source, DiscoveryBy.DEVICE)
_LOGGER.debug("Done auto discovery")
self._status = DiscoveryStatus.FINISHED
@@ -158,7 +175,7 @@ class DiscoveryManager:
if not entity_id or entity_id == DUMMY_ENTITY_ID:
continue
entity = await create_source_entity(str(entity_id), self.hass)
entity = create_source_entity(str(entity_id), self.hass)
if entity and entity.device_entry:
self.initialized_flows.add(f"pc_{entity.device_entry.id}")
self.initialized_flows.add(entity_id)
@@ -173,13 +190,13 @@ class DiscoveryManager:
async def perform_discovery(
self,
source_provider: Callable[[], Awaitable[list]],
source_creator: Callable[[er.RegistryEntry | dr.DeviceEntry], Awaitable[SourceEntity]],
source_provider: Callable[[], Awaitable[list[_DiscoverySourceT]]],
source_creator: Callable[[_DiscoverySourceT], Awaitable[SourceEntity]],
discovery_type: DiscoveryBy,
) -> None:
"""Generalized discovery procedure for entities and devices."""
for source in await source_provider():
log_identifier = source.entity_id if discovery_type == DiscoveryBy.ENTITY else source.id
log_identifier = str(getattr(source, "entity_id", getattr(source, "id", "unknown")))
try:
model_info = await self.extract_model_info_from_device_info(source)
if not model_info:
@@ -229,7 +246,7 @@ class DiscoveryManager:
async def create_entity_source(self, entity_entry: er.RegistryEntry) -> SourceEntity:
"""Create SourceEntity for an entity."""
return await create_source_entity(entity_entry.entity_id, self.hass)
return create_source_entity(entity_entry.entity_id, self.hass)
@staticmethod
async def create_device_source(device_entry: dr.DeviceEntry) -> SourceEntity:
@@ -267,13 +284,19 @@ class DiscoveryManager:
power_profiles = []
for model_info in models:
profile = await get_power_profile(self.hass, {}, source_entity, model_info=model_info, process_variables=False)
profile = await get_power_profile(
self.hass,
{},
source_entity,
model_info=model_info,
process_variables=False,
)
if not profile or profile.discovery_by != discovery_type: # pragma: no cover
continue
if discovery_type == DiscoveryBy.ENTITY and not profile.is_entity_domain_supported(
source_entity.entity_entry, # type: ignore[arg-type]
):
continue
if discovery_type == DiscoveryBy.ENTITY:
entity_entry = source_entity.entity_entry
if entity_entry is not None and not profile.is_entity_domain_supported(entity_entry):
continue
if profile.device_type in self._exclude_device_types:
continue
if self._exclude_self_usage_profiles and profile.only_self_usage:
@@ -294,7 +317,11 @@ class DiscoveryManager:
"""Initialize the discovery flow for a WLED light."""
if DeviceType.LIGHT in self._exclude_device_types:
return
unique_id = f"pc_{source_entity.device_entry.id}" if source_entity.device_entry else get_or_create_unique_id({}, source_entity, None)
unique_id = (
f"pc_{source_entity.device_entry.id}"
if source_entity.device_entry
else get_or_create_unique_id({}, source_entity, None)
)
if self._is_already_discovered(source_entity, unique_id):
_LOGGER.debug(
"%s: Already setup with discovery, skipping new discovery (unique_id=%s)",
@@ -352,9 +379,9 @@ class DiscoveryManager:
],
FilterOperator.OR,
)
return await get_filtered_entity_list(self.hass, NotFilter(entity_filter))
return get_filtered_entity_list(self.hass, NotFilter(entity_filter))
async def get_devices(self) -> list:
async def get_devices(self) -> list[dr.DeviceEntry]:
"""Fetch device entries."""
return list(dr.async_get(self.hass).devices.values())
@@ -364,6 +391,9 @@ class DiscoveryManager:
async def disable(self) -> None:
"""Disable the discovery."""
if self._cancel_rediscover_interval:
self._cancel_rediscover_interval()
self._cancel_rediscover_interval = None
self._status = DiscoveryStatus.DISABLED
self.initialized_flows = set()
flows = self.hass.config_entries.flow.async_progress_by_handler(DOMAIN)
@@ -423,7 +453,9 @@ class DiscoveryManager:
# see https://github.com/home-assistant/core/pull/166187
manufacturer = str(device_entry.manufacturer).strip()
model = str(device_entry.model).strip()
model_id = str(device_entry.model_id).strip() if hasattr(device_entry, "model_id") and device_entry.model_id else None
model_id = (
str(device_entry.model_id).strip() if hasattr(device_entry, "model_id") and device_entry.model_id else None
)
if len(manufacturer) == 0 or len(model) == 0:
return None
@@ -508,7 +540,9 @@ class DiscoveryManager:
# Find entity ids in yaml config (Legacy)
if SENSOR_DOMAIN in self.ha_config: # pragma: no cover
sensor_config = self.ha_config.get(SENSOR_DOMAIN)
platform_entries = [item for item in sensor_config or {} if isinstance(item, dict) and item.get(CONF_PLATFORM) == DOMAIN]
platform_entries = [
item for item in sensor_config or {} if isinstance(item, dict) and item.get(CONF_PLATFORM) == DOMAIN
]
for entry in platform_entries:
entities.extend(self._find_entity_ids_in_yaml_config(entry))
@@ -521,7 +555,11 @@ class DiscoveryManager:
# Add entities from existing config entries
entities.extend(
[str(entry.data.get(CONF_ENTITY_ID)) for entry in self.hass.config_entries.async_entries(DOMAIN) if entry.source == SOURCE_USER],
[
str(entry.data.get(CONF_ENTITY_ID))
for entry in self.hass.config_entries.async_entries(DOMAIN)
if entry.source == SOURCE_USER
],
)
return entities