Merge branch 'dev' into homee-switch

This commit is contained in:
Markus Adrario 2025-02-08 11:02:31 +01:00 committed by GitHub
commit 45708c6f9b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
194 changed files with 1701 additions and 1144 deletions

View File

@ -716,109 +716,6 @@ def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]:
return domains return domains
class _WatchPendingSetups:
"""Periodic log and dispatch of setups that are pending."""
def __init__(
self,
hass: core.HomeAssistant,
setup_started: dict[tuple[str, str | None], float],
) -> None:
"""Initialize the WatchPendingSetups class."""
self._hass = hass
self._setup_started = setup_started
self._duration_count = 0
self._handle: asyncio.TimerHandle | None = None
self._previous_was_empty = True
self._loop = hass.loop
def _async_watch(self) -> None:
"""Periodic log of setups that are pending."""
now = monotonic()
self._duration_count += SLOW_STARTUP_CHECK_INTERVAL
remaining_with_setup_started: defaultdict[str, float] = defaultdict(float)
for integration_group, start_time in self._setup_started.items():
domain, _ = integration_group
remaining_with_setup_started[domain] += now - start_time
if remaining_with_setup_started:
_LOGGER.debug("Integration remaining: %s", remaining_with_setup_started)
elif waiting_tasks := self._hass._active_tasks: # noqa: SLF001
_LOGGER.debug("Waiting on tasks: %s", waiting_tasks)
self._async_dispatch(remaining_with_setup_started)
if (
self._setup_started
and self._duration_count % LOG_SLOW_STARTUP_INTERVAL == 0
):
# We log every LOG_SLOW_STARTUP_INTERVAL until all integrations are done
# once we take over LOG_SLOW_STARTUP_INTERVAL (60s) to start up
_LOGGER.warning(
"Waiting on integrations to complete setup: %s",
self._setup_started,
)
_LOGGER.debug("Running timeout Zones: %s", self._hass.timeout.zones)
self._async_schedule_next()
def _async_dispatch(self, remaining_with_setup_started: dict[str, float]) -> None:
"""Dispatch the signal."""
if remaining_with_setup_started or not self._previous_was_empty:
async_dispatcher_send_internal(
self._hass, SIGNAL_BOOTSTRAP_INTEGRATIONS, remaining_with_setup_started
)
self._previous_was_empty = not remaining_with_setup_started
def _async_schedule_next(self) -> None:
"""Schedule the next call."""
self._handle = self._loop.call_later(
SLOW_STARTUP_CHECK_INTERVAL, self._async_watch
)
def async_start(self) -> None:
"""Start watching."""
self._async_schedule_next()
def async_stop(self) -> None:
"""Stop watching."""
self._async_dispatch({})
if self._handle:
self._handle.cancel()
self._handle = None
async def async_setup_multi_components(
hass: core.HomeAssistant,
domains: set[str],
config: dict[str, Any],
) -> None:
"""Set up multiple domains. Log on failure."""
# Avoid creating tasks for domains that were setup in a previous stage
domains_not_yet_setup = domains - hass.config.components
# Create setup tasks for base platforms first since everything will have
# to wait to be imported, and the sooner we can get the base platforms
# loaded the sooner we can start loading the rest of the integrations.
futures = {
domain: hass.async_create_task_internal(
async_setup_component(hass, domain, config),
f"setup component {domain}",
eager_start=True,
)
for domain in sorted(
domains_not_yet_setup, key=SETUP_ORDER_SORT_KEY, reverse=True
)
}
results = await asyncio.gather(*futures.values(), return_exceptions=True)
for idx, domain in enumerate(futures):
result = results[idx]
if isinstance(result, BaseException):
_LOGGER.error(
"Error setting up integration %s - received exception",
domain,
exc_info=(type(result), result, result.__traceback__),
)
async def _async_resolve_domains_to_setup( async def _async_resolve_domains_to_setup(
hass: core.HomeAssistant, config: dict[str, Any] hass: core.HomeAssistant, config: dict[str, Any]
) -> tuple[set[str], dict[str, loader.Integration]]: ) -> tuple[set[str], dict[str, loader.Integration]]:
@ -1038,7 +935,7 @@ async def _async_set_up_integrations(
for dep in integration.all_dependencies for dep in integration.all_dependencies
) )
async_set_domains_to_be_loaded(hass, to_be_loaded) async_set_domains_to_be_loaded(hass, to_be_loaded)
await async_setup_multi_components(hass, domain_group, config) await _async_setup_multi_components(hass, domain_group, config)
# Enables after dependencies when setting up stage 1 domains # Enables after dependencies when setting up stage 1 domains
async_set_domains_to_be_loaded(hass, stage_1_domains) async_set_domains_to_be_loaded(hass, stage_1_domains)
@ -1050,7 +947,7 @@ async def _async_set_up_integrations(
async with hass.timeout.async_timeout( async with hass.timeout.async_timeout(
STAGE_1_TIMEOUT, cool_down=COOLDOWN_TIME STAGE_1_TIMEOUT, cool_down=COOLDOWN_TIME
): ):
await async_setup_multi_components(hass, stage_1_domains, config) await _async_setup_multi_components(hass, stage_1_domains, config)
except TimeoutError: except TimeoutError:
_LOGGER.warning( _LOGGER.warning(
"Setup timed out for stage 1 waiting on %s - moving forward", "Setup timed out for stage 1 waiting on %s - moving forward",
@ -1066,7 +963,7 @@ async def _async_set_up_integrations(
async with hass.timeout.async_timeout( async with hass.timeout.async_timeout(
STAGE_2_TIMEOUT, cool_down=COOLDOWN_TIME STAGE_2_TIMEOUT, cool_down=COOLDOWN_TIME
): ):
await async_setup_multi_components(hass, stage_2_domains, config) await _async_setup_multi_components(hass, stage_2_domains, config)
except TimeoutError: except TimeoutError:
_LOGGER.warning( _LOGGER.warning(
"Setup timed out for stage 2 waiting on %s - moving forward", "Setup timed out for stage 2 waiting on %s - moving forward",
@ -1092,3 +989,106 @@ async def _async_set_up_integrations(
"Integration setup times: %s", "Integration setup times: %s",
dict(sorted(setup_time.items(), key=itemgetter(1), reverse=True)), dict(sorted(setup_time.items(), key=itemgetter(1), reverse=True)),
) )
class _WatchPendingSetups:
"""Periodic log and dispatch of setups that are pending."""
def __init__(
self,
hass: core.HomeAssistant,
setup_started: dict[tuple[str, str | None], float],
) -> None:
"""Initialize the WatchPendingSetups class."""
self._hass = hass
self._setup_started = setup_started
self._duration_count = 0
self._handle: asyncio.TimerHandle | None = None
self._previous_was_empty = True
self._loop = hass.loop
def _async_watch(self) -> None:
"""Periodic log of setups that are pending."""
now = monotonic()
self._duration_count += SLOW_STARTUP_CHECK_INTERVAL
remaining_with_setup_started: defaultdict[str, float] = defaultdict(float)
for integration_group, start_time in self._setup_started.items():
domain, _ = integration_group
remaining_with_setup_started[domain] += now - start_time
if remaining_with_setup_started:
_LOGGER.debug("Integration remaining: %s", remaining_with_setup_started)
elif waiting_tasks := self._hass._active_tasks: # noqa: SLF001
_LOGGER.debug("Waiting on tasks: %s", waiting_tasks)
self._async_dispatch(remaining_with_setup_started)
if (
self._setup_started
and self._duration_count % LOG_SLOW_STARTUP_INTERVAL == 0
):
# We log every LOG_SLOW_STARTUP_INTERVAL until all integrations are done
# once we take over LOG_SLOW_STARTUP_INTERVAL (60s) to start up
_LOGGER.warning(
"Waiting on integrations to complete setup: %s",
self._setup_started,
)
_LOGGER.debug("Running timeout Zones: %s", self._hass.timeout.zones)
self._async_schedule_next()
def _async_dispatch(self, remaining_with_setup_started: dict[str, float]) -> None:
"""Dispatch the signal."""
if remaining_with_setup_started or not self._previous_was_empty:
async_dispatcher_send_internal(
self._hass, SIGNAL_BOOTSTRAP_INTEGRATIONS, remaining_with_setup_started
)
self._previous_was_empty = not remaining_with_setup_started
def _async_schedule_next(self) -> None:
"""Schedule the next call."""
self._handle = self._loop.call_later(
SLOW_STARTUP_CHECK_INTERVAL, self._async_watch
)
def async_start(self) -> None:
"""Start watching."""
self._async_schedule_next()
def async_stop(self) -> None:
"""Stop watching."""
self._async_dispatch({})
if self._handle:
self._handle.cancel()
self._handle = None
async def _async_setup_multi_components(
hass: core.HomeAssistant,
domains: set[str],
config: dict[str, Any],
) -> None:
"""Set up multiple domains. Log on failure."""
# Avoid creating tasks for domains that were setup in a previous stage
domains_not_yet_setup = domains - hass.config.components
# Create setup tasks for base platforms first since everything will have
# to wait to be imported, and the sooner we can get the base platforms
# loaded the sooner we can start loading the rest of the integrations.
futures = {
domain: hass.async_create_task_internal(
async_setup_component(hass, domain, config),
f"setup component {domain}",
eager_start=True,
)
for domain in sorted(
domains_not_yet_setup, key=SETUP_ORDER_SORT_KEY, reverse=True
)
}
results = await asyncio.gather(*futures.values(), return_exceptions=True)
for idx, domain in enumerate(futures):
result = results[idx]
if isinstance(result, BaseException):
_LOGGER.error(
"Error setting up integration %s - received exception",
domain,
exc_info=(type(result), result, result.__traceback__),
)

View File

@ -16,7 +16,7 @@
"error": { "error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]", "invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
"requests_exceeded": "The allowed number of requests to Accuweather API has been exceeded. You have to wait or change API Key." "requests_exceeded": "The allowed number of requests to the AccuWeather API has been exceeded. You have to wait or change the API key."
} }
}, },
"entity": { "entity": {

View File

@ -6,21 +6,18 @@ from datetime import timedelta
import logging import logging
from homeassistant.components.air_quality import DOMAIN as AIR_QUALITY_PLATFORM from homeassistant.components.air_quality import DOMAIN as AIR_QUALITY_PLATFORM
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, Platform from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import CONF_USE_NEAREST, DOMAIN, MIN_UPDATE_INTERVAL from .const import CONF_USE_NEAREST, DOMAIN, MIN_UPDATE_INTERVAL
from .coordinator import AirlyDataUpdateCoordinator from .coordinator import AirlyConfigEntry, AirlyDataUpdateCoordinator
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
type AirlyConfigEntry = ConfigEntry[AirlyDataUpdateCoordinator]
async def async_setup_entry(hass: HomeAssistant, entry: AirlyConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: AirlyConfigEntry) -> bool:
"""Set up Airly as config entry.""" """Set up Airly as config entry."""
@ -60,7 +57,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirlyConfigEntry) -> boo
update_interval = timedelta(minutes=MIN_UPDATE_INTERVAL) update_interval = timedelta(minutes=MIN_UPDATE_INTERVAL)
coordinator = AirlyDataUpdateCoordinator( coordinator = AirlyDataUpdateCoordinator(
hass, websession, api_key, latitude, longitude, update_interval, use_nearest hass,
entry,
websession,
api_key,
latitude,
longitude,
update_interval,
use_nearest,
) )
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()

View File

@ -10,6 +10,7 @@ from aiohttp.client_exceptions import ClientConnectorError
from airly import Airly from airly import Airly
from airly.exceptions import AirlyError from airly.exceptions import AirlyError
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
@ -27,6 +28,8 @@ from .const import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
type AirlyConfigEntry = ConfigEntry[AirlyDataUpdateCoordinator]
def set_update_interval(instances_count: int, requests_remaining: int) -> timedelta: def set_update_interval(instances_count: int, requests_remaining: int) -> timedelta:
"""Return data update interval. """Return data update interval.
@ -58,9 +61,12 @@ def set_update_interval(instances_count: int, requests_remaining: int) -> timede
class AirlyDataUpdateCoordinator(DataUpdateCoordinator[dict[str, str | float | int]]): class AirlyDataUpdateCoordinator(DataUpdateCoordinator[dict[str, str | float | int]]):
"""Define an object to hold Airly data.""" """Define an object to hold Airly data."""
config_entry: AirlyConfigEntry
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
config_entry: AirlyConfigEntry,
session: ClientSession, session: ClientSession,
api_key: str, api_key: str,
latitude: float, latitude: float,
@ -76,7 +82,13 @@ class AirlyDataUpdateCoordinator(DataUpdateCoordinator[dict[str, str | float | i
self.airly = Airly(api_key, session, language=language) self.airly = Airly(api_key, session, language=language)
self.use_nearest = use_nearest self.use_nearest = use_nearest
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval) super().__init__(
hass,
_LOGGER,
config_entry=config_entry,
name=DOMAIN,
update_interval=update_interval,
)
async def _async_update_data(self) -> dict[str, str | float | int]: async def _async_update_data(self) -> dict[str, str | float | int]:
"""Update data via library.""" """Update data via library."""

View File

@ -13,7 +13,7 @@ from homeassistant.const import (
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import AirlyConfigEntry from .coordinator import AirlyConfigEntry
TO_REDACT = {CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_UNIQUE_ID} TO_REDACT = {CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_UNIQUE_ID}

View File

@ -24,7 +24,6 @@ from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AirlyConfigEntry, AirlyDataUpdateCoordinator
from .const import ( from .const import (
ATTR_ADVICE, ATTR_ADVICE,
ATTR_API_ADVICE, ATTR_API_ADVICE,
@ -52,6 +51,7 @@ from .const import (
SUFFIX_PERCENT, SUFFIX_PERCENT,
URL, URL,
) )
from .coordinator import AirlyConfigEntry, AirlyDataUpdateCoordinator
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 1

View File

@ -9,8 +9,8 @@ from airly import Airly
from homeassistant.components import system_health from homeassistant.components import system_health
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from . import AirlyConfigEntry
from .const import DOMAIN from .const import DOMAIN
from .coordinator import AirlyConfigEntry
@callback @callback

View File

@ -15,13 +15,11 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .coordinator import AirNowDataUpdateCoordinator from .coordinator import AirNowConfigEntry, AirNowDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
type AirNowConfigEntry = ConfigEntry[AirNowDataUpdateCoordinator]
async def async_setup_entry(hass: HomeAssistant, entry: AirNowConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: AirNowConfigEntry) -> bool:
"""Set up AirNow from a config entry.""" """Set up AirNow from a config entry."""
@ -38,7 +36,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirNowConfigEntry) -> bo
# Setup the Coordinator # Setup the Coordinator
session = async_get_clientsession(hass) session = async_get_clientsession(hass)
coordinator = AirNowDataUpdateCoordinator( coordinator = AirNowDataUpdateCoordinator(
hass, session, api_key, latitude, longitude, distance, update_interval hass, entry, session, api_key, latitude, longitude, distance, update_interval
) )
# Sync with Coordinator # Sync with Coordinator

View File

@ -10,6 +10,7 @@ from pyairnow import WebServiceAPI
from pyairnow.conv import aqi_to_concentration from pyairnow.conv import aqi_to_concentration
from pyairnow.errors import AirNowError from pyairnow.errors import AirNowError
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@ -34,13 +35,18 @@ from .const import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
type AirNowConfigEntry = ConfigEntry[AirNowDataUpdateCoordinator]
class AirNowDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): class AirNowDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""The AirNow update coordinator.""" """The AirNow update coordinator."""
config_entry: AirNowConfigEntry
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
config_entry: AirNowConfigEntry,
session: ClientSession, session: ClientSession,
api_key: str, api_key: str,
latitude: float, latitude: float,
@ -55,7 +61,13 @@ class AirNowDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
self.airnow = WebServiceAPI(api_key, session=session) self.airnow = WebServiceAPI(api_key, session=session)
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval) super().__init__(
hass,
_LOGGER,
config_entry=config_entry,
name=DOMAIN,
update_interval=update_interval,
)
async def _async_update_data(self) -> dict[str, Any]: async def _async_update_data(self) -> dict[str, Any]:
"""Update data via library.""" """Update data via library."""

View File

@ -13,7 +13,7 @@ from homeassistant.const import (
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import AirNowConfigEntry from .coordinator import AirNowConfigEntry
ATTR_LATITUDE_CAP = "Latitude" ATTR_LATITUDE_CAP = "Latitude"
ATTR_LONGITUDE_CAP = "Longitude" ATTR_LONGITUDE_CAP = "Longitude"

View File

@ -25,7 +25,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AirNowConfigEntry, AirNowDataUpdateCoordinator
from .const import ( from .const import (
ATTR_API_AQI, ATTR_API_AQI,
ATTR_API_AQI_DESCRIPTION, ATTR_API_AQI_DESCRIPTION,
@ -43,6 +42,7 @@ from .const import (
DOMAIN, DOMAIN,
US_TZ_OFFSETS, US_TZ_OFFSETS,
) )
from .coordinator import AirNowConfigEntry, AirNowDataUpdateCoordinator
ATTRIBUTION = "Data provided by AirNow" ATTRIBUTION = "Data provided by AirNow"

View File

@ -15,7 +15,6 @@ from aioairzone.const import (
) )
from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT, Platform from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT, Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import ( from homeassistant.helpers import (
@ -25,7 +24,7 @@ from homeassistant.helpers import (
) )
from .const import DOMAIN, MANUFACTURER from .const import DOMAIN, MANUFACTURER
from .coordinator import AirzoneUpdateCoordinator from .coordinator import AirzoneConfigEntry, AirzoneUpdateCoordinator
PLATFORMS: list[Platform] = [ PLATFORMS: list[Platform] = [
Platform.BINARY_SENSOR, Platform.BINARY_SENSOR,
@ -38,8 +37,6 @@ PLATFORMS: list[Platform] = [
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
type AirzoneConfigEntry = ConfigEntry[AirzoneUpdateCoordinator]
async def _async_migrate_unique_ids( async def _async_migrate_unique_ids(
hass: HomeAssistant, hass: HomeAssistant,
@ -90,7 +87,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
) )
airzone = AirzoneLocalApi(aiohttp_client.async_get_clientsession(hass), options) airzone = AirzoneLocalApi(aiohttp_client.async_get_clientsession(hass), options)
coordinator = AirzoneUpdateCoordinator(hass, airzone) coordinator = AirzoneUpdateCoordinator(hass, entry, airzone)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
await _async_migrate_unique_ids(hass, entry, coordinator) await _async_migrate_unique_ids(hass, entry, coordinator)

View File

@ -25,8 +25,7 @@ from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirzoneConfigEntry from .coordinator import AirzoneConfigEntry, AirzoneUpdateCoordinator
from .coordinator import AirzoneUpdateCoordinator
from .entity import AirzoneEntity, AirzoneSystemEntity, AirzoneZoneEntity from .entity import AirzoneEntity, AirzoneSystemEntity, AirzoneZoneEntity

View File

@ -50,9 +50,8 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirzoneConfigEntry
from .const import API_TEMPERATURE_STEP, TEMP_UNIT_LIB_TO_HASS from .const import API_TEMPERATURE_STEP, TEMP_UNIT_LIB_TO_HASS
from .coordinator import AirzoneUpdateCoordinator from .coordinator import AirzoneConfigEntry, AirzoneUpdateCoordinator
from .entity import AirzoneZoneEntity from .entity import AirzoneZoneEntity
BASE_FAN_SPEEDS: Final[dict[int, str]] = { BASE_FAN_SPEEDS: Final[dict[int, str]] = {

View File

@ -10,6 +10,7 @@ from typing import Any
from aioairzone.exceptions import AirzoneError from aioairzone.exceptions import AirzoneError
from aioairzone.localapi import AirzoneLocalApi from aioairzone.localapi import AirzoneLocalApi
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@ -19,17 +20,27 @@ SCAN_INTERVAL = timedelta(seconds=60)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
type AirzoneConfigEntry = ConfigEntry[AirzoneUpdateCoordinator]
class AirzoneUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): class AirzoneUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Class to manage fetching data from the Airzone device.""" """Class to manage fetching data from the Airzone device."""
def __init__(self, hass: HomeAssistant, airzone: AirzoneLocalApi) -> None: config_entry: AirzoneConfigEntry
def __init__(
self,
hass: HomeAssistant,
config_entry: AirzoneConfigEntry,
airzone: AirzoneLocalApi,
) -> None:
"""Initialize.""" """Initialize."""
self.airzone = airzone self.airzone = airzone
super().__init__( super().__init__(
hass, hass,
_LOGGER, _LOGGER,
config_entry=config_entry,
name=DOMAIN, name=DOMAIN,
update_interval=SCAN_INTERVAL, update_interval=SCAN_INTERVAL,
) )

View File

@ -10,7 +10,7 @@ from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_UNIQUE_ID from homeassistant.const import CONF_UNIQUE_ID
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import AirzoneConfigEntry from .coordinator import AirzoneConfigEntry
TO_REDACT_API = [ TO_REDACT_API = [
API_MAC, API_MAC,

View File

@ -31,9 +31,8 @@ from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AirzoneConfigEntry
from .const import DOMAIN, MANUFACTURER from .const import DOMAIN, MANUFACTURER
from .coordinator import AirzoneUpdateCoordinator from .coordinator import AirzoneConfigEntry, AirzoneUpdateCoordinator
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -27,8 +27,7 @@ from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirzoneConfigEntry from .coordinator import AirzoneConfigEntry, AirzoneUpdateCoordinator
from .coordinator import AirzoneUpdateCoordinator
from .entity import AirzoneEntity, AirzoneZoneEntity from .entity import AirzoneEntity, AirzoneZoneEntity

View File

@ -30,9 +30,8 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirzoneConfigEntry
from .const import TEMP_UNIT_LIB_TO_HASS from .const import TEMP_UNIT_LIB_TO_HASS
from .coordinator import AirzoneUpdateCoordinator from .coordinator import AirzoneConfigEntry, AirzoneUpdateCoordinator
from .entity import ( from .entity import (
AirzoneEntity, AirzoneEntity,
AirzoneHotWaterEntity, AirzoneHotWaterEntity,

View File

@ -16,8 +16,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirzoneConfigEntry from .coordinator import AirzoneConfigEntry, AirzoneUpdateCoordinator
from .coordinator import AirzoneUpdateCoordinator
from .entity import AirzoneEntity, AirzoneZoneEntity from .entity import AirzoneEntity, AirzoneZoneEntity

View File

@ -30,9 +30,8 @@ from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirzoneConfigEntry
from .const import TEMP_UNIT_LIB_TO_HASS from .const import TEMP_UNIT_LIB_TO_HASS
from .coordinator import AirzoneUpdateCoordinator from .coordinator import AirzoneConfigEntry, AirzoneUpdateCoordinator
from .entity import AirzoneHotWaterEntity from .entity import AirzoneHotWaterEntity
OPERATION_LIB_TO_HASS: Final[dict[HotWaterOperation, str]] = { OPERATION_LIB_TO_HASS: Final[dict[HotWaterOperation, str]] = {

View File

@ -2,14 +2,11 @@
import amberelectric import amberelectric
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_TOKEN from homeassistant.const import CONF_API_TOKEN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import CONF_SITE_ID, PLATFORMS from .const import CONF_SITE_ID, PLATFORMS
from .coordinator import AmberUpdateCoordinator from .coordinator import AmberConfigEntry, AmberUpdateCoordinator
type AmberConfigEntry = ConfigEntry[AmberUpdateCoordinator]
async def async_setup_entry(hass: HomeAssistant, entry: AmberConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: AmberConfigEntry) -> bool:
@ -19,7 +16,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AmberConfigEntry) -> boo
api_instance = amberelectric.AmberApi(api_client) api_instance = amberelectric.AmberApi(api_client)
site_id = entry.data[CONF_SITE_ID] site_id = entry.data[CONF_SITE_ID]
coordinator = AmberUpdateCoordinator(hass, api_instance, site_id) coordinator = AmberUpdateCoordinator(hass, entry, api_instance, site_id)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

@ -12,9 +12,8 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AmberConfigEntry
from .const import ATTRIBUTION from .const import ATTRIBUTION
from .coordinator import AmberUpdateCoordinator from .coordinator import AmberConfigEntry, AmberUpdateCoordinator
PRICE_SPIKE_ICONS = { PRICE_SPIKE_ICONS = {
"none": "mdi:power-plug", "none": "mdi:power-plug",

View File

@ -13,11 +13,14 @@ from amberelectric.models.forecast_interval import ForecastInterval
from amberelectric.models.price_descriptor import PriceDescriptor from amberelectric.models.price_descriptor import PriceDescriptor
from amberelectric.rest import ApiException from amberelectric.rest import ApiException
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import LOGGER from .const import LOGGER
type AmberConfigEntry = ConfigEntry[AmberUpdateCoordinator]
def is_current(interval: ActualInterval | CurrentInterval | ForecastInterval) -> bool: def is_current(interval: ActualInterval | CurrentInterval | ForecastInterval) -> bool:
"""Return true if the supplied interval is a CurrentInterval.""" """Return true if the supplied interval is a CurrentInterval."""
@ -70,13 +73,20 @@ def normalize_descriptor(descriptor: PriceDescriptor | None) -> str | None:
class AmberUpdateCoordinator(DataUpdateCoordinator): class AmberUpdateCoordinator(DataUpdateCoordinator):
"""AmberUpdateCoordinator - In charge of downloading the data for a site, which all the sensors read.""" """AmberUpdateCoordinator - In charge of downloading the data for a site, which all the sensors read."""
config_entry: AmberConfigEntry
def __init__( def __init__(
self, hass: HomeAssistant, api: amberelectric.AmberApi, site_id: str self,
hass: HomeAssistant,
config_entry: AmberConfigEntry,
api: amberelectric.AmberApi,
site_id: str,
) -> None: ) -> None:
"""Initialise the data service.""" """Initialise the data service."""
super().__init__( super().__init__(
hass, hass,
LOGGER, LOGGER,
config_entry=config_entry,
name="amberelectric", name="amberelectric",
update_interval=timedelta(minutes=1), update_interval=timedelta(minutes=1),
) )

View File

@ -22,9 +22,8 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AmberConfigEntry
from .const import ATTRIBUTION from .const import ATTRIBUTION
from .coordinator import AmberUpdateCoordinator, normalize_descriptor from .coordinator import AmberConfigEntry, AmberUpdateCoordinator, normalize_descriptor
UNIT = f"{CURRENCY_DOLLAR}/{UnitOfEnergy.KILO_WATT_HOUR}" UNIT = f"{CURRENCY_DOLLAR}/{UnitOfEnergy.KILO_WATT_HOUR}"

View File

@ -4,13 +4,10 @@ from __future__ import annotations
from aioambient.open_api import OpenAPI from aioambient.open_api import OpenAPI
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .coordinator import AmbientNetworkDataUpdateCoordinator from .coordinator import AmbientNetworkConfigEntry, AmbientNetworkDataUpdateCoordinator
type AmbientNetworkConfigEntry = ConfigEntry[AmbientNetworkDataUpdateCoordinator]
PLATFORMS: list[Platform] = [Platform.SENSOR] PLATFORMS: list[Platform] = [Platform.SENSOR]
@ -21,7 +18,7 @@ async def async_setup_entry(
"""Set up the Ambient Weather Network from a config entry.""" """Set up the Ambient Weather Network from a config entry."""
api = OpenAPI() api = OpenAPI()
coordinator = AmbientNetworkDataUpdateCoordinator(hass, api) coordinator = AmbientNetworkDataUpdateCoordinator(hass, entry, api)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

@ -19,17 +19,27 @@ from .helper import get_station_name
SCAN_INTERVAL = timedelta(minutes=5) SCAN_INTERVAL = timedelta(minutes=5)
type AmbientNetworkConfigEntry = ConfigEntry[AmbientNetworkDataUpdateCoordinator]
class AmbientNetworkDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): class AmbientNetworkDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""The Ambient Network Data Update Coordinator.""" """The Ambient Network Data Update Coordinator."""
config_entry: ConfigEntry config_entry: AmbientNetworkConfigEntry
station_name: str station_name: str
last_measured: datetime | None = None last_measured: datetime | None = None
def __init__(self, hass: HomeAssistant, api: OpenAPI) -> None: def __init__(
self, hass: HomeAssistant, config_entry: AmbientNetworkConfigEntry, api: OpenAPI
) -> None:
"""Initialize the coordinator.""" """Initialize the coordinator."""
super().__init__(hass, LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL) super().__init__(
hass,
LOGGER,
config_entry=config_entry,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
)
self.api = api self.api = api
async def _async_update_data(self) -> dict[str, Any]: async def _async_update_data(self) -> dict[str, Any]:

View File

@ -28,8 +28,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from . import AmbientNetworkConfigEntry from .coordinator import AmbientNetworkConfigEntry, AmbientNetworkDataUpdateCoordinator
from .coordinator import AmbientNetworkDataUpdateCoordinator
from .entity import AmbientNetworkEntity from .entity import AmbientNetworkEntity
TYPE_AQI_PM25 = "aqi_pm25" TYPE_AQI_PM25 = "aqi_pm25"

View File

@ -48,7 +48,7 @@ async def async_setup_entry(
continue continue
names[integration] = integrations[integration].title names[integration] = integrations[integration].title
coordinator = HomeassistantAnalyticsDataUpdateCoordinator(hass, client) coordinator = HomeassistantAnalyticsDataUpdateCoordinator(hass, entry, client)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()

View File

@ -46,12 +46,16 @@ class HomeassistantAnalyticsDataUpdateCoordinator(DataUpdateCoordinator[Analytic
config_entry: AnalyticsInsightsConfigEntry config_entry: AnalyticsInsightsConfigEntry
def __init__( def __init__(
self, hass: HomeAssistant, client: HomeassistantAnalyticsClient self,
hass: HomeAssistant,
config_entry: AnalyticsInsightsConfigEntry,
client: HomeassistantAnalyticsClient,
) -> None: ) -> None:
"""Initialize the Homeassistant Analytics data coordinator.""" """Initialize the Homeassistant Analytics data coordinator."""
super().__init__( super().__init__(
hass, hass,
LOGGER, LOGGER,
config_entry=config_entry,
name=DOMAIN, name=DOMAIN,
update_interval=timedelta(hours=12), update_interval=timedelta(hours=12),
) )

View File

@ -35,6 +35,7 @@ class AndroidIPCamDataUpdateCoordinator(DataUpdateCoordinator[None]):
super().__init__( super().__init__(
self.hass, self.hass,
_LOGGER, _LOGGER,
config_entry=config_entry,
name=f"{DOMAIN} {config_entry.data[CONF_HOST]}", name=f"{DOMAIN} {config_entry.data[CONF_HOST]}",
update_interval=timedelta(seconds=10), update_interval=timedelta(seconds=10),
) )

View File

@ -35,16 +35,12 @@ async def async_setup_entry(
@callback @callback
def is_available_updated(is_available: bool) -> None: def is_available_updated(is_available: bool) -> None:
if is_available: _LOGGER.info(
_LOGGER.info( "%s %s at %s",
"Reconnected to %s at %s", entry.data[CONF_NAME], entry.data[CONF_HOST] "Reconnected to" if is_available else "Disconnected from",
) entry.data[CONF_NAME],
else: entry.data[CONF_HOST],
_LOGGER.warning( )
"Disconnected from %s at %s",
entry.data[CONF_NAME],
entry.data[CONF_HOST],
)
api.add_is_available_updated_callback(is_available_updated) api.add_is_available_updated_callback(is_available_updated)

View File

@ -18,8 +18,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client from homeassistant.helpers import aiohttp_client
from .coordinator import AnovaCoordinator from .coordinator import AnovaConfigEntry, AnovaCoordinator, AnovaData
from .models import AnovaConfigEntry, AnovaData
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
@ -59,7 +58,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AnovaConfigEntry) -> boo
# websocket client # websocket client
assert api.websocket_handler is not None assert api.websocket_handler is not None
devices = list(api.websocket_handler.devices.values()) devices = list(api.websocket_handler.devices.values())
coordinators = [AnovaCoordinator(hass, device) for device in devices] coordinators = [AnovaCoordinator(hass, entry, device) for device in devices]
entry.runtime_data = AnovaData(api_jwt=api.jwt, coordinators=coordinators, api=api) entry.runtime_data = AnovaData(api_jwt=api.jwt, coordinators=coordinators, api=api)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True return True

View File

@ -1,8 +1,9 @@
"""Support for Anova Coordinators.""" """Support for Anova Coordinators."""
from dataclasses import dataclass
import logging import logging
from anova_wifi import APCUpdate, APCWifiDevice from anova_wifi import AnovaApi, APCUpdate, APCWifiDevice
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -14,15 +15,33 @@ from .const import DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@dataclass
class AnovaData:
"""Data for the Anova integration."""
api_jwt: str
coordinators: list["AnovaCoordinator"]
api: AnovaApi
type AnovaConfigEntry = ConfigEntry[AnovaData]
class AnovaCoordinator(DataUpdateCoordinator[APCUpdate]): class AnovaCoordinator(DataUpdateCoordinator[APCUpdate]):
"""Anova custom coordinator.""" """Anova custom coordinator."""
config_entry: ConfigEntry config_entry: AnovaConfigEntry
def __init__(self, hass: HomeAssistant, anova_device: APCWifiDevice) -> None: def __init__(
self,
hass: HomeAssistant,
config_entry: AnovaConfigEntry,
anova_device: APCWifiDevice,
) -> None:
"""Set up Anova Coordinator.""" """Set up Anova Coordinator."""
super().__init__( super().__init__(
hass, hass,
config_entry=config_entry,
name="Anova Precision Cooker", name="Anova Precision Cooker",
logger=_LOGGER, logger=_LOGGER,
) )

View File

@ -1,20 +0,0 @@
"""Dataclass models for the Anova integration."""
from dataclasses import dataclass
from anova_wifi import AnovaApi
from homeassistant.config_entries import ConfigEntry
from .coordinator import AnovaCoordinator
type AnovaConfigEntry = ConfigEntry[AnovaData]
@dataclass
class AnovaData:
"""Data for the Anova integration."""
api_jwt: str
coordinators: list[AnovaCoordinator]
api: AnovaApi

View File

@ -18,9 +18,8 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from .coordinator import AnovaCoordinator from .coordinator import AnovaConfigEntry, AnovaCoordinator
from .entity import AnovaDescriptionEntity from .entity import AnovaDescriptionEntity
from .models import AnovaConfigEntry
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)

View File

@ -4,13 +4,10 @@ from __future__ import annotations
from typing import Final from typing import Final
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PORT, Platform from homeassistant.const import CONF_HOST, CONF_PORT, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .coordinator import APCUPSdCoordinator from .coordinator import APCUPSdConfigEntry, APCUPSdCoordinator
type APCUPSdConfigEntry = ConfigEntry[APCUPSdCoordinator]
PLATFORMS: Final = (Platform.BINARY_SENSOR, Platform.SENSOR) PLATFORMS: Final = (Platform.BINARY_SENSOR, Platform.SENSOR)
@ -20,7 +17,7 @@ async def async_setup_entry(
) -> bool: ) -> bool:
"""Use config values to set up a function enabling status retrieval.""" """Use config values to set up a function enabling status retrieval."""
host, port = config_entry.data[CONF_HOST], config_entry.data[CONF_PORT] host, port = config_entry.data[CONF_HOST], config_entry.data[CONF_PORT]
coordinator = APCUPSdCoordinator(hass, host, port) coordinator = APCUPSdCoordinator(hass, config_entry, host, port)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()

View File

@ -12,8 +12,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import APCUPSdConfigEntry from .coordinator import APCUPSdConfigEntry, APCUPSdCoordinator
from .coordinator import APCUPSdCoordinator
PARALLEL_UPDATES = 0 PARALLEL_UPDATES = 0

View File

@ -25,6 +25,8 @@ _LOGGER = logging.getLogger(__name__)
UPDATE_INTERVAL: Final = timedelta(seconds=60) UPDATE_INTERVAL: Final = timedelta(seconds=60)
REQUEST_REFRESH_COOLDOWN: Final = 5 REQUEST_REFRESH_COOLDOWN: Final = 5
type APCUPSdConfigEntry = ConfigEntry[APCUPSdCoordinator]
class APCUPSdData(dict[str, str]): class APCUPSdData(dict[str, str]):
"""Store data about an APCUPSd and provide a few helper methods for easier accesses.""" """Store data about an APCUPSd and provide a few helper methods for easier accesses."""
@ -57,13 +59,20 @@ class APCUPSdCoordinator(DataUpdateCoordinator[APCUPSdData]):
updates from the server. updates from the server.
""" """
config_entry: ConfigEntry config_entry: APCUPSdConfigEntry
def __init__(self, hass: HomeAssistant, host: str, port: int) -> None: def __init__(
self,
hass: HomeAssistant,
config_entry: APCUPSdConfigEntry,
host: str,
port: int,
) -> None:
"""Initialize the data object.""" """Initialize the data object."""
super().__init__( super().__init__(
hass, hass,
_LOGGER, _LOGGER,
config_entry=config_entry,
name=DOMAIN, name=DOMAIN,
update_interval=UPDATE_INTERVAL, update_interval=UPDATE_INTERVAL,
request_refresh_debouncer=Debouncer( request_refresh_debouncer=Debouncer(

View File

@ -7,7 +7,7 @@ from typing import Any
from homeassistant.components.diagnostics import async_redact_data from homeassistant.components.diagnostics import async_redact_data
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import APCUPSdConfigEntry from .coordinator import APCUPSdConfigEntry
TO_REDACT = {"SERIALNO", "HOSTNAME"} TO_REDACT = {"SERIALNO", "HOSTNAME"}

View File

@ -24,9 +24,8 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import APCUPSdConfigEntry
from .const import LAST_S_TEST from .const import LAST_S_TEST
from .coordinator import APCUPSdCoordinator from .coordinator import APCUPSdConfigEntry, APCUPSdCoordinator
PARALLEL_UPDATES = 0 PARALLEL_UPDATES = 0

View File

@ -5,18 +5,15 @@ from __future__ import annotations
from aioaquacell import AquacellApi from aioaquacell import AquacellApi
from aioaquacell.const import Brand from aioaquacell.const import Brand
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import CONF_BRAND from .const import CONF_BRAND
from .coordinator import AquacellCoordinator from .coordinator import AquacellConfigEntry, AquacellCoordinator
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
type AquacellConfigEntry = ConfigEntry[AquacellCoordinator]
async def async_setup_entry(hass: HomeAssistant, entry: AquacellConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: AquacellConfigEntry) -> bool:
"""Set up Aquacell from a config entry.""" """Set up Aquacell from a config entry."""
@ -26,7 +23,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AquacellConfigEntry) ->
aquacell_api = AquacellApi(session, brand) aquacell_api = AquacellApi(session, brand)
coordinator = AquacellCoordinator(hass, aquacell_api) coordinator = AquacellCoordinator(hass, entry, aquacell_api)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator entry.runtime_data = coordinator
@ -36,6 +33,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: AquacellConfigEntry) ->
return True return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: AquacellConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@ -26,17 +26,25 @@ from .const import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
type AquacellConfigEntry = ConfigEntry[AquacellCoordinator]
class AquacellCoordinator(DataUpdateCoordinator[dict[str, Softener]]): class AquacellCoordinator(DataUpdateCoordinator[dict[str, Softener]]):
"""My aquacell coordinator.""" """My aquacell coordinator."""
config_entry: ConfigEntry config_entry: AquacellConfigEntry
def __init__(self, hass: HomeAssistant, aquacell_api: AquacellApi) -> None: def __init__(
self,
hass: HomeAssistant,
config_entry: AquacellConfigEntry,
aquacell_api: AquacellApi,
) -> None:
"""Initialize coordinator.""" """Initialize coordinator."""
super().__init__( super().__init__(
hass, hass,
_LOGGER, _LOGGER,
config_entry=config_entry,
name="Aquacell Coordinator", name="Aquacell Coordinator",
update_interval=UPDATE_INTERVAL, update_interval=UPDATE_INTERVAL,
) )

View File

@ -18,8 +18,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from . import AquacellConfigEntry from .coordinator import AquacellConfigEntry, AquacellCoordinator
from .coordinator import AquacellCoordinator
from .entity import AquacellEntity from .entity import AquacellEntity
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 1

View File

@ -13,7 +13,7 @@ PLATFORMS: list[Platform] = [Platform.SENSOR]
async def async_setup_entry(hass: HomeAssistant, entry: ArveConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ArveConfigEntry) -> bool:
"""Set up Arve from a config entry.""" """Set up Arve from a config entry."""
coordinator = ArveCoordinator(hass) coordinator = ArveCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()

View File

@ -30,11 +30,12 @@ class ArveCoordinator(DataUpdateCoordinator[ArveSensProData]):
config_entry: ArveConfigEntry config_entry: ArveConfigEntry
devices: ArveDevices devices: ArveDevices
def __init__(self, hass: HomeAssistant) -> None: def __init__(self, hass: HomeAssistant, config_entry: ArveConfigEntry) -> None:
"""Initialize Arve coordinator.""" """Initialize Arve coordinator."""
super().__init__( super().__init__(
hass, hass,
LOGGER, LOGGER,
config_entry=config_entry,
name=DOMAIN, name=DOMAIN,
update_interval=timedelta(seconds=60), update_interval=timedelta(seconds=60),
) )

View File

@ -1093,16 +1093,18 @@ class PipelineRun:
agent_id = conversation.HOME_ASSISTANT_AGENT agent_id = conversation.HOME_ASSISTANT_AGENT
processed_locally = True processed_locally = True
# It was already handled, create response and add to chat history with (
if intent_response is not None: chat_session.async_get_chat_session(
with ( self.hass, user_input.conversation_id
chat_session.async_get_chat_session( ) as session,
self.hass, user_input.conversation_id conversation.async_get_chat_log(
) as session, self.hass,
conversation.async_get_chat_log( session,
self.hass, session, user_input user_input,
) as chat_log, ) as chat_log,
): ):
# It was already handled, create response and add to chat history
if intent_response is not None:
speech: str = intent_response.speech.get("plain", {}).get( speech: str = intent_response.speech.get("plain", {}).get(
"speech", "" "speech", ""
) )
@ -1117,21 +1119,21 @@ class PipelineRun:
conversation_id=session.conversation_id, conversation_id=session.conversation_id,
) )
else: else:
# Fall back to pipeline conversation agent # Fall back to pipeline conversation agent
conversation_result = await conversation.async_converse( conversation_result = await conversation.async_converse(
hass=self.hass, hass=self.hass,
text=user_input.text, text=user_input.text,
conversation_id=user_input.conversation_id, conversation_id=user_input.conversation_id,
device_id=user_input.device_id, device_id=user_input.device_id,
context=user_input.context, context=user_input.context,
language=user_input.language, language=user_input.language,
agent_id=user_input.agent_id, agent_id=user_input.agent_id,
extra_system_prompt=user_input.extra_system_prompt, extra_system_prompt=user_input.extra_system_prompt,
) )
speech = conversation_result.response.speech.get("plain", {}).get( speech = conversation_result.response.speech.get("plain", {}).get(
"speech", "" "speech", ""
) )
except Exception as src_error: except Exception as src_error:
_LOGGER.exception("Unexpected error during intent recognition") _LOGGER.exception("Unexpected error during intent recognition")

View File

@ -23,7 +23,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AuroraAbbConfigEntry) ->
comport = entry.data[CONF_PORT] comport = entry.data[CONF_PORT]
address = entry.data[CONF_ADDRESS] address = entry.data[CONF_ADDRESS]
coordinator = AuroraAbbDataUpdateCoordinator(hass, comport, address) coordinator = AuroraAbbDataUpdateCoordinator(hass, entry, comport, address)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator entry.runtime_data = coordinator

View File

@ -21,12 +21,26 @@ type AuroraAbbConfigEntry = ConfigEntry[AuroraAbbDataUpdateCoordinator]
class AuroraAbbDataUpdateCoordinator(DataUpdateCoordinator[dict[str, float]]): class AuroraAbbDataUpdateCoordinator(DataUpdateCoordinator[dict[str, float]]):
"""Class to manage fetching AuroraAbbPowerone data.""" """Class to manage fetching AuroraAbbPowerone data."""
def __init__(self, hass: HomeAssistant, comport: str, address: int) -> None: config_entry: AuroraAbbConfigEntry
def __init__(
self,
hass: HomeAssistant,
config_entry: AuroraAbbConfigEntry,
comport: str,
address: int,
) -> None:
"""Initialize the data update coordinator.""" """Initialize the data update coordinator."""
self.available_prev = False self.available_prev = False
self.available = False self.available = False
self.client = AuroraSerialClient(address, comport, parity="N", timeout=1) self.client = AuroraSerialClient(address, comport, parity="N", timeout=1)
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL) super().__init__(
hass,
_LOGGER,
config_entry=config_entry,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
)
def _update_data(self) -> dict[str, float]: def _update_data(self) -> dict[str, float]:
"""Fetch new state data for the sensors. """Fetch new state data for the sensors.

View File

@ -45,7 +45,7 @@ async def async_setup_entry(
# Initiate a Data Update Coordinator for each service # Initiate a Data Update Coordinator for each service
for service in services: for service in services:
service["coordinator"] = AussieBroadbandDataUpdateCoordinator( service["coordinator"] = AussieBroadbandDataUpdateCoordinator(
hass, client, service["service_id"] hass, entry, client, service["service_id"]
) )
await service["coordinator"].async_config_entry_first_refresh() await service["coordinator"].async_config_entry_first_refresh()

View File

@ -34,11 +34,20 @@ type AussieBroadbandConfigEntry = ConfigEntry[list[AussieBroadbandServiceData]]
class AussieBroadbandDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): class AussieBroadbandDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Aussie Broadand data update coordinator.""" """Aussie Broadand data update coordinator."""
def __init__(self, hass: HomeAssistant, client: AussieBB, service_id: str) -> None: config_entry: AussieBroadbandConfigEntry
def __init__(
self,
hass: HomeAssistant,
config_entry: AussieBroadbandConfigEntry,
client: AussieBB,
service_id: str,
) -> None:
"""Initialize Atag coordinator.""" """Initialize Atag coordinator."""
super().__init__( super().__init__(
hass, hass,
_LOGGER, _LOGGER,
config_entry=config_entry,
name=f"Aussie Broadband {service_id}", name=f"Aussie Broadband {service_id}",
update_interval=timedelta(minutes=DEFAULT_UPDATE_INTERVAL), update_interval=timedelta(minutes=DEFAULT_UPDATE_INTERVAL),
) )

View File

@ -6,18 +6,15 @@ import asyncio
from autarco import Autarco, AutarcoConnectionError from autarco import Autarco, AutarcoConnectionError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .coordinator import AutarcoDataUpdateCoordinator from .coordinator import AutarcoConfigEntry, AutarcoDataUpdateCoordinator
PLATFORMS: list[Platform] = [Platform.SENSOR] PLATFORMS: list[Platform] = [Platform.SENSOR]
type AutarcoConfigEntry = ConfigEntry[list[AutarcoDataUpdateCoordinator]]
async def async_setup_entry(hass: HomeAssistant, entry: AutarcoConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: AutarcoConfigEntry) -> bool:
"""Set up Autarco from a config entry.""" """Set up Autarco from a config entry."""
@ -34,7 +31,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: AutarcoConfigEntry) -> b
raise ConfigEntryNotReady from err raise ConfigEntryNotReady from err
coordinators: list[AutarcoDataUpdateCoordinator] = [ coordinators: list[AutarcoDataUpdateCoordinator] = [
AutarcoDataUpdateCoordinator(hass, client, site) for site in account_sites AutarcoDataUpdateCoordinator(hass, entry, client, site)
for site in account_sites
] ]
await asyncio.gather( await asyncio.gather(

View File

@ -22,6 +22,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from .const import DOMAIN, LOGGER, SCAN_INTERVAL from .const import DOMAIN, LOGGER, SCAN_INTERVAL
type AutarcoConfigEntry = ConfigEntry[list[AutarcoDataUpdateCoordinator]]
class AutarcoData(NamedTuple): class AutarcoData(NamedTuple):
"""Class for defining data in dict.""" """Class for defining data in dict."""
@ -35,11 +37,12 @@ class AutarcoData(NamedTuple):
class AutarcoDataUpdateCoordinator(DataUpdateCoordinator[AutarcoData]): class AutarcoDataUpdateCoordinator(DataUpdateCoordinator[AutarcoData]):
"""Class to manage fetching Autarco data from the API.""" """Class to manage fetching Autarco data from the API."""
config_entry: ConfigEntry config_entry: AutarcoConfigEntry
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
config_entry: AutarcoConfigEntry,
client: Autarco, client: Autarco,
account_site: AccountSite, account_site: AccountSite,
) -> None: ) -> None:
@ -47,6 +50,7 @@ class AutarcoDataUpdateCoordinator(DataUpdateCoordinator[AutarcoData]):
super().__init__( super().__init__(
hass, hass,
LOGGER, LOGGER,
config_entry=config_entry,
name=DOMAIN, name=DOMAIN,
update_interval=SCAN_INTERVAL, update_interval=SCAN_INTERVAL,
) )

View File

@ -6,7 +6,7 @@ from typing import Any
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import AutarcoConfigEntry, AutarcoDataUpdateCoordinator from .coordinator import AutarcoConfigEntry, AutarcoDataUpdateCoordinator
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(

View File

@ -20,9 +20,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AutarcoConfigEntry
from .const import DOMAIN from .const import DOMAIN
from .coordinator import AutarcoDataUpdateCoordinator from .coordinator import AutarcoConfigEntry, AutarcoDataUpdateCoordinator
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)

View File

@ -9,9 +9,7 @@ from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import CONF_PAT, CONF_PROJECT from .const import CONF_PAT, CONF_PROJECT
from .coordinator import AzureDevOpsDataUpdateCoordinator from .coordinator import AzureDevOpsConfigEntry, AzureDevOpsDataUpdateCoordinator
type AzureDevOpsConfigEntry = ConfigEntry[AzureDevOpsDataUpdateCoordinator]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -22,11 +20,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AzureDevOpsConfigEntry)
"""Set up Azure DevOps from a config entry.""" """Set up Azure DevOps from a config entry."""
# Create the data update coordinator # Create the data update coordinator
coordinator = AzureDevOpsDataUpdateCoordinator( coordinator = AzureDevOpsDataUpdateCoordinator(hass, entry, _LOGGER)
hass,
_LOGGER,
entry=entry,
)
# Store the coordinator in runtime data # Store the coordinator in runtime data
entry.runtime_data = coordinator entry.runtime_data = coordinator

View File

@ -28,6 +28,8 @@ from .data import AzureDevOpsData
BUILDS_QUERY: Final = "?queryOrder=queueTimeDescending&maxBuildsPerDefinition=1" BUILDS_QUERY: Final = "?queryOrder=queueTimeDescending&maxBuildsPerDefinition=1"
IGNORED_CATEGORIES: Final[list[Category]] = [Category.COMPLETED, Category.REMOVED] IGNORED_CATEGORIES: Final[list[Category]] = [Category.COMPLETED, Category.REMOVED]
type AzureDevOpsConfigEntry = ConfigEntry[AzureDevOpsDataUpdateCoordinator]
def ado_exception_none_handler(func: Callable) -> Callable: def ado_exception_none_handler(func: Callable) -> Callable:
"""Handle exceptions or None to always return a value or raise.""" """Handle exceptions or None to always return a value or raise."""
@ -50,28 +52,29 @@ class AzureDevOpsDataUpdateCoordinator(DataUpdateCoordinator[AzureDevOpsData]):
"""Class to manage and fetch Azure DevOps data.""" """Class to manage and fetch Azure DevOps data."""
client: DevOpsClient client: DevOpsClient
config_entry: AzureDevOpsConfigEntry
organization: str organization: str
project: Project project: Project
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
config_entry: AzureDevOpsConfigEntry,
logger: logging.Logger, logger: logging.Logger,
*,
entry: ConfigEntry,
) -> None: ) -> None:
"""Initialize global Azure DevOps data updater.""" """Initialize global Azure DevOps data updater."""
self.title = entry.title self.title = config_entry.title
super().__init__( super().__init__(
hass=hass, hass=hass,
logger=logger, logger=logger,
config_entry=config_entry,
name=DOMAIN, name=DOMAIN,
update_interval=timedelta(seconds=300), update_interval=timedelta(seconds=300),
) )
self.client = DevOpsClient(session=async_get_clientsession(hass)) self.client = DevOpsClient(session=async_get_clientsession(hass))
self.organization = entry.data[CONF_ORG] self.organization = config_entry.data[CONF_ORG]
@ado_exception_none_handler @ado_exception_none_handler
async def authorize( async def authorize(

View File

@ -21,8 +21,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from . import AzureDevOpsConfigEntry from .coordinator import AzureDevOpsConfigEntry, AzureDevOpsDataUpdateCoordinator
from .coordinator import AzureDevOpsDataUpdateCoordinator
from .entity import AzureDevOpsEntity from .entity import AzureDevOpsEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -85,7 +85,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: BlinkConfigEntry) -> boo
auth_data = deepcopy(dict(entry.data)) auth_data = deepcopy(dict(entry.data))
blink.auth = Auth(auth_data, no_prompt=True, session=session) blink.auth = Auth(auth_data, no_prompt=True, session=session)
blink.refresh_rate = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) blink.refresh_rate = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
coordinator = BlinkUpdateCoordinator(hass, blink) coordinator = BlinkUpdateCoordinator(hass, entry, blink)
try: try:
await blink.start() await blink.start()

View File

@ -23,12 +23,17 @@ type BlinkConfigEntry = ConfigEntry[BlinkUpdateCoordinator]
class BlinkUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): class BlinkUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""BlinkUpdateCoordinator - In charge of downloading the data for a site.""" """BlinkUpdateCoordinator - In charge of downloading the data for a site."""
def __init__(self, hass: HomeAssistant, api: Blink) -> None: config_entry: BlinkConfigEntry
def __init__(
self, hass: HomeAssistant, config_entry: BlinkConfigEntry, api: Blink
) -> None:
"""Initialize the data service.""" """Initialize the data service."""
self.api = api self.api = api
super().__init__( super().__init__(
hass, hass,
_LOGGER, _LOGGER,
config_entry=config_entry,
name=DOMAIN, name=DOMAIN,
update_interval=timedelta(seconds=SCAN_INTERVAL), update_interval=timedelta(seconds=SCAN_INTERVAL),
) )

View File

@ -7,14 +7,11 @@ from typing import Final
from aiohttp import CookieJar from aiohttp import CookieJar
from pybravia import BraviaClient from pybravia import BraviaClient
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_MAC, Platform from homeassistant.const import CONF_HOST, CONF_MAC, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_create_clientsession from homeassistant.helpers.aiohttp_client import async_create_clientsession
from .coordinator import BraviaTVCoordinator from .coordinator import BraviaTVConfigEntry, BraviaTVCoordinator
BraviaTVConfigEntry = ConfigEntry[BraviaTVCoordinator]
PLATFORMS: Final[list[Platform]] = [ PLATFORMS: Final[list[Platform]] = [
Platform.BUTTON, Platform.BUTTON,
@ -36,8 +33,8 @@ async def async_setup_entry(
client = BraviaClient(host, mac, session=session) client = BraviaClient(host, mac, session=session)
coordinator = BraviaTVCoordinator( coordinator = BraviaTVCoordinator(
hass=hass, hass=hass,
config_entry=config_entry,
client=client, client=client,
config=config_entry.data,
) )
config_entry.async_on_unload(config_entry.add_update_listener(update_listener)) config_entry.async_on_unload(config_entry.add_update_listener(update_listener))

View File

@ -14,8 +14,7 @@ from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BraviaTVConfigEntry from .coordinator import BraviaTVConfigEntry, BraviaTVCoordinator
from .coordinator import BraviaTVCoordinator
from .entity import BraviaTVEntity from .entity import BraviaTVEntity

View File

@ -6,7 +6,6 @@ from collections.abc import Awaitable, Callable, Coroutine, Iterable
from datetime import datetime, timedelta from datetime import datetime, timedelta
from functools import wraps from functools import wraps
import logging import logging
from types import MappingProxyType
from typing import Any, Concatenate, Final from typing import Any, Concatenate, Final
from pybravia import ( from pybravia import (
@ -20,6 +19,7 @@ from pybravia import (
) )
from homeassistant.components.media_player import MediaType from homeassistant.components.media_player import MediaType
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_CLIENT_ID, CONF_PIN from homeassistant.const import CONF_CLIENT_ID, CONF_PIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.exceptions import ConfigEntryAuthFailed
@ -39,6 +39,8 @@ _LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL: Final = timedelta(seconds=10) SCAN_INTERVAL: Final = timedelta(seconds=10)
type BraviaTVConfigEntry = ConfigEntry["BraviaTVCoordinator"]
def catch_braviatv_errors[_BraviaTVCoordinatorT: BraviaTVCoordinator, **_P]( def catch_braviatv_errors[_BraviaTVCoordinatorT: BraviaTVCoordinator, **_P](
func: Callable[Concatenate[_BraviaTVCoordinatorT, _P], Awaitable[None]], func: Callable[Concatenate[_BraviaTVCoordinatorT, _P], Awaitable[None]],
@ -64,19 +66,21 @@ def catch_braviatv_errors[_BraviaTVCoordinatorT: BraviaTVCoordinator, **_P](
class BraviaTVCoordinator(DataUpdateCoordinator[None]): class BraviaTVCoordinator(DataUpdateCoordinator[None]):
"""Representation of a Bravia TV Coordinator.""" """Representation of a Bravia TV Coordinator."""
config_entry: BraviaTVConfigEntry
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
config_entry: BraviaTVConfigEntry,
client: BraviaClient, client: BraviaClient,
config: MappingProxyType[str, Any],
) -> None: ) -> None:
"""Initialize Bravia TV Client.""" """Initialize Bravia TV Client."""
self.client = client self.client = client
self.pin = config[CONF_PIN] self.pin = config_entry.data[CONF_PIN]
self.use_psk = config.get(CONF_USE_PSK, False) self.use_psk = config_entry.data.get(CONF_USE_PSK, False)
self.client_id = config.get(CONF_CLIENT_ID, LEGACY_CLIENT_ID) self.client_id = config_entry.data.get(CONF_CLIENT_ID, LEGACY_CLIENT_ID)
self.nickname = config.get(CONF_NICKNAME, NICKNAME_PREFIX) self.nickname = config_entry.data.get(CONF_NICKNAME, NICKNAME_PREFIX)
self.source: str | None = None self.source: str | None = None
self.source_list: list[str] = [] self.source_list: list[str] = []
self.source_map: dict[str, dict] = {} self.source_map: dict[str, dict] = {}
@ -98,6 +102,7 @@ class BraviaTVCoordinator(DataUpdateCoordinator[None]):
super().__init__( super().__init__(
hass, hass,
_LOGGER, _LOGGER,
config_entry=config_entry,
name=DOMAIN, name=DOMAIN,
update_interval=SCAN_INTERVAL, update_interval=SCAN_INTERVAL,
request_refresh_debouncer=Debouncer( request_refresh_debouncer=Debouncer(

View File

@ -6,7 +6,7 @@ from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_MAC, CONF_PIN from homeassistant.const import CONF_MAC, CONF_PIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import BraviaTVConfigEntry from .coordinator import BraviaTVConfigEntry
TO_REDACT = {CONF_MAC, CONF_PIN, "macAddr"} TO_REDACT = {CONF_MAC, CONF_PIN, "macAddr"}

View File

@ -3,8 +3,8 @@
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import BraviaTVCoordinator
from .const import ATTR_MANUFACTURER, DOMAIN from .const import ATTR_MANUFACTURER, DOMAIN
from .coordinator import BraviaTVCoordinator
class BraviaTVEntity(CoordinatorEntity[BraviaTVCoordinator]): class BraviaTVEntity(CoordinatorEntity[BraviaTVCoordinator]):

View File

@ -18,8 +18,8 @@ from homeassistant.components.media_player import (
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BraviaTVConfigEntry
from .const import SourceType from .const import SourceType
from .coordinator import BraviaTVConfigEntry
from .entity import BraviaTVEntity from .entity import BraviaTVEntity

View File

@ -9,7 +9,7 @@ from homeassistant.components.remote import ATTR_NUM_REPEATS, RemoteEntity
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BraviaTVConfigEntry from .coordinator import BraviaTVConfigEntry
from .entity import BraviaTVEntity from .entity import BraviaTVEntity

View File

@ -25,7 +25,7 @@
}, },
"data_description": { "data_description": {
"email": "[%key:component::bring::config::step::user::data_description::email%]", "email": "[%key:component::bring::config::step::user::data_description::email%]",
"password": "[%key:component::bring::config::step::user::data_description::email%]" "password": "[%key:component::bring::config::step::user::data_description::password%]"
} }
}, },
"reconfigure": { "reconfigure": {
@ -37,7 +37,7 @@
}, },
"data_description": { "data_description": {
"email": "[%key:component::bring::config::step::user::data_description::email%]", "email": "[%key:component::bring::config::step::user::data_description::email%]",
"password": "[%key:component::bring::config::step::user::data_description::email%]" "password": "[%key:component::bring::config::step::user::data_description::password%]"
} }
} }
}, },

View File

@ -5,17 +5,14 @@ from __future__ import annotations
from brother import Brother, SnmpError from brother import Brother, SnmpError
from homeassistant.components.snmp import async_get_snmp_engine from homeassistant.components.snmp import async_get_snmp_engine
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_TYPE, Platform from homeassistant.const import CONF_HOST, CONF_TYPE, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from .coordinator import BrotherDataUpdateCoordinator from .coordinator import BrotherConfigEntry, BrotherDataUpdateCoordinator
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
type BrotherConfigEntry = ConfigEntry[BrotherDataUpdateCoordinator]
async def async_setup_entry(hass: HomeAssistant, entry: BrotherConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: BrotherConfigEntry) -> bool:
"""Set up Brother from a config entry.""" """Set up Brother from a config entry."""
@ -30,7 +27,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: BrotherConfigEntry) -> b
except (ConnectionError, SnmpError, TimeoutError) as error: except (ConnectionError, SnmpError, TimeoutError) as error:
raise ConfigEntryNotReady from error raise ConfigEntryNotReady from error
coordinator = BrotherDataUpdateCoordinator(hass, brother) coordinator = BrotherDataUpdateCoordinator(hass, entry, brother)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator entry.runtime_data = coordinator

View File

@ -5,6 +5,7 @@ import logging
from brother import Brother, BrotherSensors, SnmpError, UnsupportedModelError from brother import Brother, BrotherSensors, SnmpError, UnsupportedModelError
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@ -12,17 +13,24 @@ from .const import DOMAIN, UPDATE_INTERVAL
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
type BrotherConfigEntry = ConfigEntry[BrotherDataUpdateCoordinator]
class BrotherDataUpdateCoordinator(DataUpdateCoordinator[BrotherSensors]): class BrotherDataUpdateCoordinator(DataUpdateCoordinator[BrotherSensors]):
"""Class to manage fetching Brother data from the printer.""" """Class to manage fetching Brother data from the printer."""
def __init__(self, hass: HomeAssistant, brother: Brother) -> None: config_entry: BrotherConfigEntry
def __init__(
self, hass: HomeAssistant, config_entry: BrotherConfigEntry, brother: Brother
) -> None:
"""Initialize.""" """Initialize."""
self.brother = brother self.brother = brother
super().__init__( super().__init__(
hass, hass,
_LOGGER, _LOGGER,
config_entry=config_entry,
name=DOMAIN, name=DOMAIN,
update_interval=UPDATE_INTERVAL, update_interval=UPDATE_INTERVAL,
) )

View File

@ -7,7 +7,7 @@ from typing import Any
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import BrotherConfigEntry from .coordinator import BrotherConfigEntry
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(

View File

@ -24,8 +24,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import BrotherConfigEntry, BrotherDataUpdateCoordinator
from .const import DOMAIN from .const import DOMAIN
from .coordinator import BrotherConfigEntry, BrotherDataUpdateCoordinator
ATTR_COUNTER = "counter" ATTR_COUNTER = "counter"
ATTR_REMAINING_PAGES = "remaining_pages" ATTR_REMAINING_PAGES = "remaining_pages"

View File

@ -38,6 +38,7 @@ class BSBLanUpdateCoordinator(DataUpdateCoordinator[BSBLanCoordinatorData]):
super().__init__( super().__init__(
hass, hass,
logger=LOGGER, logger=LOGGER,
config_entry=config_entry,
name=f"{DOMAIN}_{config_entry.data[CONF_HOST]}", name=f"{DOMAIN}_{config_entry.data[CONF_HOST]}",
update_interval=self._get_update_interval(), update_interval=self._get_update_interval(),
) )

View File

@ -4,23 +4,20 @@ from __future__ import annotations
from aioelectricitymaps import ElectricityMaps from aioelectricitymaps import ElectricityMaps
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, Platform from homeassistant.const import CONF_API_KEY, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .coordinator import CO2SignalCoordinator from .coordinator import CO2SignalConfigEntry, CO2SignalCoordinator
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
type CO2SignalConfigEntry = ConfigEntry[CO2SignalCoordinator]
async def async_setup_entry(hass: HomeAssistant, entry: CO2SignalConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: CO2SignalConfigEntry) -> bool:
"""Set up CO2 Signal from a config entry.""" """Set up CO2 Signal from a config entry."""
session = async_get_clientsession(hass) session = async_get_clientsession(hass)
coordinator = CO2SignalCoordinator( coordinator = CO2SignalCoordinator(
hass, ElectricityMaps(token=entry.data[CONF_API_KEY], session=session) hass, entry, ElectricityMaps(token=entry.data[CONF_API_KEY], session=session)
) )
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()

View File

@ -22,16 +22,27 @@ from .helpers import fetch_latest_carbon_intensity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
type CO2SignalConfigEntry = ConfigEntry[CO2SignalCoordinator]
class CO2SignalCoordinator(DataUpdateCoordinator[CarbonIntensityResponse]): class CO2SignalCoordinator(DataUpdateCoordinator[CarbonIntensityResponse]):
"""Data update coordinator.""" """Data update coordinator."""
config_entry: ConfigEntry config_entry: CO2SignalConfigEntry
def __init__(self, hass: HomeAssistant, client: ElectricityMaps) -> None: def __init__(
self,
hass: HomeAssistant,
config_entry: CO2SignalConfigEntry,
client: ElectricityMaps,
) -> None:
"""Initialize the coordinator.""" """Initialize the coordinator."""
super().__init__( super().__init__(
hass, _LOGGER, name=DOMAIN, update_interval=timedelta(minutes=15) hass,
_LOGGER,
config_entry=config_entry,
name=DOMAIN,
update_interval=timedelta(minutes=15),
) )
self.client = client self.client = client

View File

@ -9,7 +9,7 @@ from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_API_KEY from homeassistant.const import CONF_API_KEY
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import CO2SignalConfigEntry from .coordinator import CO2SignalConfigEntry
TO_REDACT = {CONF_API_KEY} TO_REDACT = {CONF_API_KEY}

View File

@ -18,9 +18,8 @@ from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import CO2SignalConfigEntry
from .const import ATTRIBUTION, DOMAIN from .const import ATTRIBUTION, DOMAIN
from .coordinator import CO2SignalCoordinator from .coordinator import CO2SignalConfigEntry, CO2SignalCoordinator
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)

View File

@ -4,7 +4,6 @@ from __future__ import annotations
import asyncio import asyncio
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import cast
from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.const import ( from homeassistant.const import (
@ -43,9 +42,7 @@ async def async_setup_platform(
if not discovery_info: if not discovery_info:
return return
discovery_info = cast(DiscoveryInfoType, discovery_info)
binary_sensor_config = discovery_info binary_sensor_config = discovery_info
command: str = binary_sensor_config[CONF_COMMAND] command: str = binary_sensor_config[CONF_COMMAND]
payload_off: str = binary_sensor_config[CONF_PAYLOAD_OFF] payload_off: str = binary_sensor_config[CONF_PAYLOAD_OFF]
payload_on: str = binary_sensor_config[CONF_PAYLOAD_ON] payload_on: str = binary_sensor_config[CONF_PAYLOAD_ON]

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import asyncio import asyncio
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import TYPE_CHECKING, Any, cast from typing import TYPE_CHECKING, Any
from homeassistant.components.cover import CoverEntity from homeassistant.components.cover import CoverEntity
from homeassistant.const import ( from homeassistant.const import (
@ -41,7 +41,6 @@ async def async_setup_platform(
return return
covers = [] covers = []
discovery_info = cast(DiscoveryInfoType, discovery_info)
entities: dict[str, dict[str, Any]] = { entities: dict[str, dict[str, Any]] = {
slugify(discovery_info[CONF_NAME]): discovery_info slugify(discovery_info[CONF_NAME]): discovery_info
} }

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import logging import logging
import subprocess import subprocess
from typing import Any, cast from typing import Any
from homeassistant.components.notify import BaseNotificationService from homeassistant.components.notify import BaseNotificationService
from homeassistant.const import CONF_COMMAND from homeassistant.const import CONF_COMMAND
@ -26,7 +26,6 @@ def get_service(
if not discovery_info: if not discovery_info:
return None return None
discovery_info = cast(DiscoveryInfoType, discovery_info)
notify_config = discovery_info notify_config = discovery_info
command: str = notify_config[CONF_COMMAND] command: str = notify_config[CONF_COMMAND]
timeout: int = notify_config[CONF_COMMAND_TIMEOUT] timeout: int = notify_config[CONF_COMMAND_TIMEOUT]

View File

@ -6,7 +6,7 @@ import asyncio
from collections.abc import Mapping from collections.abc import Mapping
from datetime import datetime, timedelta from datetime import datetime, timedelta
import json import json
from typing import Any, cast from typing import Any
from jsonpath import jsonpath from jsonpath import jsonpath
@ -51,9 +51,7 @@ async def async_setup_platform(
if not discovery_info: if not discovery_info:
return return
discovery_info = cast(DiscoveryInfoType, discovery_info)
sensor_config = discovery_info sensor_config = discovery_info
command: str = sensor_config[CONF_COMMAND] command: str = sensor_config[CONF_COMMAND]
command_timeout: int = sensor_config[CONF_COMMAND_TIMEOUT] command_timeout: int = sensor_config[CONF_COMMAND_TIMEOUT]
json_attributes: list[str] | None = sensor_config.get(CONF_JSON_ATTRIBUTES) json_attributes: list[str] | None = sensor_config.get(CONF_JSON_ATTRIBUTES)

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import asyncio import asyncio
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import TYPE_CHECKING, Any, cast from typing import TYPE_CHECKING, Any
from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchEntity from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchEntity
from homeassistant.const import ( from homeassistant.const import (
@ -40,7 +40,6 @@ async def async_setup_platform(
return return
switches = [] switches = []
discovery_info = cast(DiscoveryInfoType, discovery_info)
entities: dict[str, dict[str, Any]] = { entities: dict[str, dict[str, Any]] = {
slugify(discovery_info[CONF_NAME]): discovery_info slugify(discovery_info[CONF_NAME]): discovery_info
} }

View File

@ -79,6 +79,9 @@ async def async_converse(
extra_system_prompt: str | None = None, extra_system_prompt: str | None = None,
) -> ConversationResult: ) -> ConversationResult:
"""Process text and get intent.""" """Process text and get intent."""
if agent_id is None:
agent_id = HOME_ASSISTANT_AGENT
agent = async_get_agent(hass, agent_id) agent = async_get_agent(hass, agent_id)
if agent is None: if agent is None:

View File

@ -1,9 +1,11 @@
"""Conversation history.""" """Conversation chat log."""
from __future__ import annotations from __future__ import annotations
import asyncio
from collections.abc import AsyncGenerator, Generator from collections.abc import AsyncGenerator, Generator
from contextlib import contextmanager from contextlib import contextmanager
from contextvars import ContextVar
from dataclasses import dataclass, field, replace from dataclasses import dataclass, field, replace
import logging import logging
@ -19,10 +21,14 @@ from . import trace
from .const import DOMAIN from .const import DOMAIN
from .models import ConversationInput, ConversationResult from .models import ConversationInput, ConversationResult
DATA_CHAT_HISTORY: HassKey[dict[str, ChatLog]] = HassKey("conversation_chat_log") DATA_CHAT_LOGS: HassKey[dict[str, ChatLog]] = HassKey("conversation_chat_logs")
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
current_chat_log: ContextVar[ChatLog | None] = ContextVar(
"current_chat_log", default=None
)
@contextmanager @contextmanager
def async_get_chat_log( def async_get_chat_log(
@ -31,41 +37,50 @@ def async_get_chat_log(
user_input: ConversationInput | None = None, user_input: ConversationInput | None = None,
) -> Generator[ChatLog]: ) -> Generator[ChatLog]:
"""Return chat log for a specific chat session.""" """Return chat log for a specific chat session."""
all_history = hass.data.get(DATA_CHAT_HISTORY) if chat_log := current_chat_log.get():
if all_history is None: # If a chat log is already active and it's the requested conversation ID,
all_history = {} # return that. We won't update the last updated time in this case.
hass.data[DATA_CHAT_HISTORY] = all_history if chat_log.conversation_id == session.conversation_id:
yield chat_log
return
history = all_history.get(session.conversation_id) all_chat_logs = hass.data.get(DATA_CHAT_LOGS)
if all_chat_logs is None:
all_chat_logs = {}
hass.data[DATA_CHAT_LOGS] = all_chat_logs
if history: chat_log = all_chat_logs.get(session.conversation_id)
history = replace(history, content=history.content.copy())
if chat_log:
chat_log = replace(chat_log, content=chat_log.content.copy())
else: else:
history = ChatLog(hass, session.conversation_id) chat_log = ChatLog(hass, session.conversation_id)
if user_input is not None: if user_input is not None:
history.async_add_user_content(UserContent(content=user_input.text)) chat_log.async_add_user_content(UserContent(content=user_input.text))
last_message = history.content[-1] last_message = chat_log.content[-1]
yield history token = current_chat_log.set(chat_log)
yield chat_log
current_chat_log.reset(token)
if history.content[-1] is last_message: if chat_log.content[-1] is last_message:
LOGGER.debug( LOGGER.debug(
"History opened but no assistant message was added, ignoring update" "Chat Log opened but no assistant message was added, ignoring update"
) )
return return
if session.conversation_id not in all_history: if session.conversation_id not in all_chat_logs:
@callback @callback
def do_cleanup() -> None: def do_cleanup() -> None:
"""Handle cleanup.""" """Handle cleanup."""
all_history.pop(session.conversation_id) all_chat_logs.pop(session.conversation_id)
session.async_on_cleanup(do_cleanup) session.async_on_cleanup(do_cleanup)
all_history[session.conversation_id] = history all_chat_logs[session.conversation_id] = chat_log
class ConverseError(HomeAssistantError): class ConverseError(HomeAssistantError):
@ -112,7 +127,7 @@ class AssistantContent:
role: str = field(init=False, default="assistant") role: str = field(init=False, default="assistant")
agent_id: str agent_id: str
content: str content: str | None = None
tool_calls: list[llm.ToolInput] | None = None tool_calls: list[llm.ToolInput] | None = None
@ -143,6 +158,7 @@ class ChatLog:
@callback @callback
def async_add_user_content(self, content: UserContent) -> None: def async_add_user_content(self, content: UserContent) -> None:
"""Add user content to the log.""" """Add user content to the log."""
LOGGER.debug("Adding user content: %s", content)
self.content.append(content) self.content.append(content)
@callback @callback
@ -150,14 +166,24 @@ class ChatLog:
self, content: AssistantContent self, content: AssistantContent
) -> None: ) -> None:
"""Add assistant content to the log.""" """Add assistant content to the log."""
LOGGER.debug("Adding assistant content: %s", content)
if content.tool_calls is not None: if content.tool_calls is not None:
raise ValueError("Tool calls not allowed") raise ValueError("Tool calls not allowed")
self.content.append(content) self.content.append(content)
async def async_add_assistant_content( async def async_add_assistant_content(
self, content: AssistantContent self,
content: AssistantContent,
/,
tool_call_tasks: dict[str, asyncio.Task] | None = None,
) -> AsyncGenerator[ToolResultContent]: ) -> AsyncGenerator[ToolResultContent]:
"""Add assistant content.""" """Add assistant content and execute tool calls.
tool_call_tasks can contains tasks for tool calls that are already in progress.
This method is an async generator and will yield the tool results as they come in.
"""
LOGGER.debug("Adding assistant content: %s", content)
self.content.append(content) self.content.append(content)
if content.tool_calls is None: if content.tool_calls is None:
@ -166,13 +192,22 @@ class ChatLog:
if self.llm_api is None: if self.llm_api is None:
raise ValueError("No LLM API configured") raise ValueError("No LLM API configured")
if tool_call_tasks is None:
tool_call_tasks = {}
for tool_input in content.tool_calls:
if tool_input.id not in tool_call_tasks:
tool_call_tasks[tool_input.id] = self.hass.async_create_task(
self.llm_api.async_call_tool(tool_input),
name=f"llm_tool_{tool_input.id}",
)
for tool_input in content.tool_calls: for tool_input in content.tool_calls:
LOGGER.debug( LOGGER.debug(
"Tool call: %s(%s)", tool_input.tool_name, tool_input.tool_args "Tool call: %s(%s)", tool_input.tool_name, tool_input.tool_args
) )
try: try:
tool_result = await self.llm_api.async_call_tool(tool_input) tool_result = await tool_call_tasks[tool_input.id]
except (HomeAssistantError, vol.Invalid) as e: except (HomeAssistantError, vol.Invalid) as e:
tool_result = {"error": type(e).__name__} tool_result = {"error": type(e).__name__}
if str(e): if str(e):

View File

@ -381,7 +381,7 @@ class DefaultAgent(ConversationEntity):
speech: str = response.speech.get("plain", {}).get("speech", "") speech: str = response.speech.get("plain", {}).get("speech", "")
chat_log.async_add_assistant_content_without_tools( chat_log.async_add_assistant_content_without_tools(
AssistantContent( AssistantContent(
agent_id=user_input.agent_id, # type: ignore[arg-type] agent_id=user_input.agent_id,
content=speech, content=speech,
) )
) )

View File

@ -195,7 +195,7 @@ async def websocket_hass_agent_debug(
conversation_id=None, conversation_id=None,
device_id=msg.get("device_id"), device_id=msg.get("device_id"),
language=msg.get("language", hass.config.language), language=msg.get("language", hass.config.language),
agent_id=None, agent_id=agent.entity_id,
) )
result_dict: dict[str, Any] | None = None result_dict: dict[str, Any] | None = None

View File

@ -37,7 +37,7 @@ class ConversationInput:
language: str language: str
"""Language of the request.""" """Language of the request."""
agent_id: str | None = None agent_id: str
"""Agent to use for processing.""" """Agent to use for processing."""
extra_system_prompt: str | None = None extra_system_prompt: str | None = None

View File

@ -6,18 +6,15 @@ from pydiscovergy import Discovergy
from pydiscovergy.authentication import BasicAuth from pydiscovergy.authentication import BasicAuth
import pydiscovergy.error as discovergyError import pydiscovergy.error as discovergyError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.httpx_client import get_async_client from homeassistant.helpers.httpx_client import get_async_client
from .coordinator import DiscovergyUpdateCoordinator from .coordinator import DiscovergyConfigEntry, DiscovergyUpdateCoordinator
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
type DiscovergyConfigEntry = ConfigEntry[list[DiscovergyUpdateCoordinator]]
async def async_setup_entry(hass: HomeAssistant, entry: DiscovergyConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: DiscovergyConfigEntry) -> bool:
"""Set up Discovergy from a config entry.""" """Set up Discovergy from a config entry."""
@ -46,6 +43,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: DiscovergyConfigEntry) -
# so we have data when entities are added # so we have data when entities are added
coordinator = DiscovergyUpdateCoordinator( coordinator = DiscovergyUpdateCoordinator(
hass=hass, hass=hass,
config_entry=entry,
meter=meter, meter=meter,
discovergy_client=client, discovergy_client=client,
) )

View File

@ -9,19 +9,25 @@ from pydiscovergy import Discovergy
from pydiscovergy.error import DiscovergyClientError, HTTPError, InvalidLogin from pydiscovergy.error import DiscovergyClientError, HTTPError, InvalidLogin
from pydiscovergy.models import Meter, Reading from pydiscovergy.models import Meter, Reading
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
type DiscovergyConfigEntry = ConfigEntry[list[DiscovergyUpdateCoordinator]]
class DiscovergyUpdateCoordinator(DataUpdateCoordinator[Reading]): class DiscovergyUpdateCoordinator(DataUpdateCoordinator[Reading]):
"""The Discovergy update coordinator.""" """The Discovergy update coordinator."""
config_entry: DiscovergyConfigEntry
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
config_entry: DiscovergyConfigEntry,
meter: Meter, meter: Meter,
discovergy_client: Discovergy, discovergy_client: Discovergy,
) -> None: ) -> None:
@ -32,6 +38,7 @@ class DiscovergyUpdateCoordinator(DataUpdateCoordinator[Reading]):
super().__init__( super().__init__(
hass, hass,
_LOGGER, _LOGGER,
config_entry=config_entry,
name=f"Discovergy meter {meter.meter_id}", name=f"Discovergy meter {meter.meter_id}",
update_interval=timedelta(seconds=30), update_interval=timedelta(seconds=30),
) )

View File

@ -8,7 +8,7 @@ from typing import Any
from homeassistant.components.diagnostics import async_redact_data from homeassistant.components.diagnostics import async_redact_data
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import DiscovergyConfigEntry from .coordinator import DiscovergyConfigEntry
TO_REDACT_METER = { TO_REDACT_METER = {
"serial_number", "serial_number",

View File

@ -24,9 +24,8 @@ from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import DiscovergyConfigEntry
from .const import DOMAIN, MANUFACTURER from .const import DOMAIN, MANUFACTURER
from .coordinator import DiscovergyUpdateCoordinator from .coordinator import DiscovergyConfigEntry, DiscovergyUpdateCoordinator
PARALLEL_UPDATES = 0 PARALLEL_UPDATES = 0

View File

@ -29,7 +29,7 @@ async def async_setup_entry(
f"Unable to connect to Dremel 3D Printer: {ex}" f"Unable to connect to Dremel 3D Printer: {ex}"
) from ex ) from ex
coordinator = Dremel3DPrinterDataUpdateCoordinator(hass, api) coordinator = Dremel3DPrinterDataUpdateCoordinator(hass, config_entry, api)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
config_entry.runtime_data = coordinator config_entry.runtime_data = coordinator
platforms = list(PLATFORMS) platforms = list(PLATFORMS)

View File

@ -18,11 +18,14 @@ class Dremel3DPrinterDataUpdateCoordinator(DataUpdateCoordinator[None]):
config_entry: DremelConfigEntry config_entry: DremelConfigEntry
def __init__(self, hass: HomeAssistant, api: Dremel3DPrinter) -> None: def __init__(
self, hass: HomeAssistant, config_entry: DremelConfigEntry, api: Dremel3DPrinter
) -> None:
"""Initialize Dremel 3D Printer data update coordinator.""" """Initialize Dremel 3D Printer data update coordinator."""
super().__init__( super().__init__(
hass=hass, hass=hass,
logger=LOGGER, logger=LOGGER,
config_entry=config_entry,
name=DOMAIN, name=DOMAIN,
update_interval=timedelta(seconds=10), update_interval=timedelta(seconds=10),
) )

View File

@ -16,7 +16,7 @@ async def async_setup_entry(
device_registry = dr.async_get(hass) device_registry = dr.async_get(hass)
if device_registry.async_get_device(identifiers={(DOMAIN, entry.entry_id)}): if device_registry.async_get_device(identifiers={(DOMAIN, entry.entry_id)}):
device_registry.async_clear_config_entry(entry.entry_id) device_registry.async_clear_config_entry(entry.entry_id)
coordinator = DwdWeatherWarningsCoordinator(hass) coordinator = DwdWeatherWarningsCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator entry.runtime_data = coordinator

View File

@ -28,10 +28,16 @@ class DwdWeatherWarningsCoordinator(DataUpdateCoordinator[None]):
config_entry: DwdWeatherWarningsConfigEntry config_entry: DwdWeatherWarningsConfigEntry
api: DwdWeatherWarningsAPI api: DwdWeatherWarningsAPI
def __init__(self, hass: HomeAssistant) -> None: def __init__(
self, hass: HomeAssistant, config_entry: DwdWeatherWarningsConfigEntry
) -> None:
"""Initialize the dwd_weather_warnings coordinator.""" """Initialize the dwd_weather_warnings coordinator."""
super().__init__( super().__init__(
hass, LOGGER, name=DOMAIN, update_interval=DEFAULT_SCAN_INTERVAL hass,
LOGGER,
config_entry=config_entry,
name=DOMAIN,
update_interval=DEFAULT_SCAN_INTERVAL,
) )
self._device_tracker = None self._device_tracker = None

View File

@ -2,25 +2,22 @@
from __future__ import annotations from __future__ import annotations
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.device_registry import DeviceEntry
from .const import DOMAIN from .const import DOMAIN
from .coordinator import EheimDigitalUpdateCoordinator from .coordinator import EheimDigitalConfigEntry, EheimDigitalUpdateCoordinator
PLATFORMS = [Platform.CLIMATE, Platform.LIGHT] PLATFORMS = [Platform.CLIMATE, Platform.LIGHT]
type EheimDigitalConfigEntry = ConfigEntry[EheimDigitalUpdateCoordinator]
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: EheimDigitalConfigEntry hass: HomeAssistant, entry: EheimDigitalConfigEntry
) -> bool: ) -> bool:
"""Set up EHEIM Digital from a config entry.""" """Set up EHEIM Digital from a config entry."""
coordinator = EheimDigitalUpdateCoordinator(hass) coordinator = EheimDigitalUpdateCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator entry.runtime_data = coordinator

View File

@ -23,9 +23,8 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import EheimDigitalConfigEntry
from .const import HEATER_BIO_MODE, HEATER_PRESET_TO_HEATER_MODE, HEATER_SMART_MODE from .const import HEATER_BIO_MODE, HEATER_PRESET_TO_HEATER_MODE, HEATER_SMART_MODE
from .coordinator import EheimDigitalUpdateCoordinator from .coordinator import EheimDigitalConfigEntry, EheimDigitalUpdateCoordinator
from .entity import EheimDigitalEntity from .entity import EheimDigitalEntity
# Coordinator is used to centralize the data updates # Coordinator is used to centralize the data updates

View File

@ -22,18 +22,26 @@ type AsyncSetupDeviceEntitiesCallback = Callable[
[str | dict[str, EheimDigitalDevice]], None [str | dict[str, EheimDigitalDevice]], None
] ]
type EheimDigitalConfigEntry = ConfigEntry[EheimDigitalUpdateCoordinator]
class EheimDigitalUpdateCoordinator( class EheimDigitalUpdateCoordinator(
DataUpdateCoordinator[dict[str, EheimDigitalDevice]] DataUpdateCoordinator[dict[str, EheimDigitalDevice]]
): ):
"""The EHEIM Digital data update coordinator.""" """The EHEIM Digital data update coordinator."""
config_entry: ConfigEntry config_entry: EheimDigitalConfigEntry
def __init__(self, hass: HomeAssistant) -> None: def __init__(
self, hass: HomeAssistant, config_entry: EheimDigitalConfigEntry
) -> None:
"""Initialize the EHEIM Digital data update coordinator.""" """Initialize the EHEIM Digital data update coordinator."""
super().__init__( super().__init__(
hass, LOGGER, name=DOMAIN, update_interval=DEFAULT_SCAN_INTERVAL hass,
LOGGER,
config_entry=config_entry,
name=DOMAIN,
update_interval=DEFAULT_SCAN_INTERVAL,
) )
self.hub = EheimDigitalHub( self.hub = EheimDigitalHub(
host=self.config_entry.data[CONF_HOST], host=self.config_entry.data[CONF_HOST],

View File

@ -19,9 +19,8 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util.color import brightness_to_value, value_to_brightness from homeassistant.util.color import brightness_to_value, value_to_brightness
from . import EheimDigitalConfigEntry
from .const import EFFECT_DAYCL_MODE, EFFECT_TO_LIGHT_MODE from .const import EFFECT_DAYCL_MODE, EFFECT_TO_LIGHT_MODE
from .coordinator import EheimDigitalUpdateCoordinator from .coordinator import EheimDigitalConfigEntry, EheimDigitalUpdateCoordinator
from .entity import EheimDigitalEntity from .entity import EheimDigitalEntity
BRIGHTNESS_SCALE = (1, 100) BRIGHTNESS_SCALE = (1, 100)

View File

@ -2,7 +2,6 @@
from pyemoncms import EmoncmsClient from pyemoncms import EmoncmsClient
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_URL, Platform from homeassistant.const import CONF_API_KEY, CONF_URL, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
@ -10,12 +9,10 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from .const import DOMAIN, EMONCMS_UUID_DOC_URL, LOGGER from .const import DOMAIN, EMONCMS_UUID_DOC_URL, LOGGER
from .coordinator import EmoncmsCoordinator from .coordinator import EmonCMSConfigEntry, EmoncmsCoordinator
PLATFORMS: list[Platform] = [Platform.SENSOR] PLATFORMS: list[Platform] = [Platform.SENSOR]
type EmonCMSConfigEntry = ConfigEntry[EmoncmsCoordinator]
def _migrate_unique_id( def _migrate_unique_id(
hass: HomeAssistant, entry: EmonCMSConfigEntry, emoncms_unique_id: str hass: HomeAssistant, entry: EmonCMSConfigEntry, emoncms_unique_id: str
@ -68,7 +65,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: EmonCMSConfigEntry) -> b
session=async_get_clientsession(hass), session=async_get_clientsession(hass),
) )
await _check_unique_id_migration(hass, entry, emoncms_client) await _check_unique_id_migration(hass, entry, emoncms_client)
coordinator = EmoncmsCoordinator(hass, emoncms_client) coordinator = EmoncmsCoordinator(hass, entry, emoncms_client)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator entry.runtime_data = coordinator
@ -77,11 +74,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: EmonCMSConfigEntry) -> b
return True return True
async def update_listener(hass: HomeAssistant, entry: ConfigEntry): async def update_listener(hass: HomeAssistant, entry: EmonCMSConfigEntry):
"""Handle options update.""" """Handle options update."""
await hass.config_entries.async_reload(entry.entry_id) await hass.config_entries.async_reload(entry.entry_id)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: EmonCMSConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

Some files were not shown because too many files have changed in this diff Show More