Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
ludeeus
7360ea0677
Merge branch 'dev' of github.com:home-assistant/core into hassio_stats_coordinator 2023-04-09 15:40:18 +00:00
ludeeus
6a23d452bc
init 2023-03-30 20:22:52 +00:00
5 changed files with 157 additions and 52 deletions

View file

@ -63,8 +63,6 @@ from .const import (
ATTR_PASSWORD,
ATTR_REPOSITORY,
ATTR_SLUG,
ATTR_STARTED,
ATTR_STATE,
ATTR_URL,
ATTR_VERSION,
DATA_KEY_ADDONS,
@ -718,7 +716,7 @@ def async_register_supervisor_in_dev_reg(
params = DeviceInfo(
identifiers={(DOMAIN, "supervisor")},
manufacturer="Home Assistant",
model=SupervisorEntityModel.SUPERVIOSR,
model=SupervisorEntityModel.SUPERVISOR,
sw_version=supervisor_dict[ATTR_VERSION],
name="Home Assistant Supervisor",
entry_type=dr.DeviceEntryType.SERVICE,
@ -765,7 +763,6 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
new_data: dict[str, Any] = {}
supervisor_info = get_supervisor_info(self.hass) or {}
addons_info = get_addons_info(self.hass)
addons_stats = get_addons_stats(self.hass)
addons_changelogs = get_addons_changelogs(self.hass)
store_data = get_store(self.hass) or {}
@ -777,7 +774,6 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
new_data[DATA_KEY_ADDONS] = {
addon[ATTR_SLUG]: {
**addon,
**((addons_stats or {}).get(addon[ATTR_SLUG]) or {}),
ATTR_AUTO_UPDATE: (addons_info.get(addon[ATTR_SLUG]) or {}).get(
ATTR_AUTO_UPDATE, False
),
@ -793,11 +789,9 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
new_data[DATA_KEY_CORE] = {
**(get_core_info(self.hass) or {}),
**get_core_stats(self.hass),
}
new_data[DATA_KEY_SUPERVISOR] = {
**supervisor_info,
**get_supervisor_stats(self.hass),
}
new_data[DATA_KEY_HOST] = get_host_info(self.hass) or {}
@ -857,27 +851,16 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
(
self.hass.data[DATA_INFO],
self.hass.data[DATA_CORE_INFO],
self.hass.data[DATA_CORE_STATS],
self.hass.data[DATA_SUPERVISOR_INFO],
self.hass.data[DATA_SUPERVISOR_STATS],
self.hass.data[DATA_OS_INFO],
) = await asyncio.gather(
self.hassio.get_info(),
self.hassio.get_core_info(),
self.hassio.get_core_stats(),
self.hassio.get_supervisor_info(),
self.hassio.get_supervisor_stats(),
self.hassio.get_os_info(),
)
all_addons = self.hass.data[DATA_SUPERVISOR_INFO].get("addons", [])
started_addons = [
addon for addon in all_addons if addon[ATTR_STATE] == ATTR_STARTED
]
stats_data = await asyncio.gather(
*[self._update_addon_stats(addon[ATTR_SLUG]) for addon in started_addons]
)
self.hass.data[DATA_ADDONS_STATS] = dict(stats_data)
self.hass.data[DATA_ADDONS_CHANGELOGS] = dict(
await asyncio.gather(
*[
@ -892,15 +875,6 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
)
)
async def _update_addon_stats(self, slug):
"""Update single addon stats."""
try:
stats = await self.hassio.get_addon_stats(slug)
return (slug, stats)
except HassioAPIError as err:
_LOGGER.warning("Could not fetch stats for %s: %s", slug, err)
return (slug, None)
async def _update_addon_changelog(self, slug):
"""Return the changelog for an add-on."""
try:
@ -936,3 +910,41 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
await super()._async_refresh(
log_failures, raise_on_auth_failed, scheduled, raise_on_entry_error
)
class HassioStatsDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Custom coordinator for Hass.io stats."""
config_entry: ConfigEntry
def __init__(
self,
hass: HomeAssistant,
model: SupervisorEntityModel,
*,
addon_slug: str | None = None,
) -> None:
"""Initialize coordinator."""
super().__init__(
hass,
_LOGGER,
name="_".join([x for x in (DOMAIN, model, addon_slug) if x]),
update_interval=HASSIO_UPDATE_INTERVAL,
)
self.hassio: HassIO = hass.data[DOMAIN]
self.model = model
self.addon_slug = addon_slug
async def _async_update_data(self) -> dict[str, Any]:
"""Fetch the latest data from the source."""
try:
if self.model == SupervisorEntityModel.ADDON:
return await self.hassio.get_addon_stats(self.addon_slug)
if self.model == SupervisorEntityModel.SUPERVISOR:
return await self.hassio.get_supervisor_stats()
# SupervisorEntityModel.CORE
return await self.hassio.get_core_stats()
except HassioAPIError as err:
raise UpdateFailed(f"Error communicating with Supervisor: {err}") from err

View file

@ -77,5 +77,5 @@ class SupervisorEntityModel(str, Enum):
ADDON = "Home Assistant Add-on"
OS = "Home Assistant Operating System"
CORE = "Home Assistant Core"
SUPERVIOSR = "Home Assistant Supervisor"
SUPERVISOR = "Home Assistant Supervisor"
HOST = "Home Assistant Host"

View file

@ -6,7 +6,7 @@ from typing import Any
from homeassistant.helpers.entity import DeviceInfo, EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import DOMAIN, HassioDataUpdateCoordinator
from . import DOMAIN, HassioDataUpdateCoordinator, HassioStatsDataUpdateCoordinator
from .const import (
ATTR_SLUG,
DATA_KEY_ADDONS,
@ -17,14 +17,16 @@ from .const import (
)
class HassioAddonEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
class HassioAddonEntity(
CoordinatorEntity[HassioDataUpdateCoordinator | HassioStatsDataUpdateCoordinator]
):
"""Base entity for a Hass.io add-on."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: HassioDataUpdateCoordinator,
coordinator: HassioDataUpdateCoordinator | HassioStatsDataUpdateCoordinator,
entity_description: EntityDescription,
addon: dict[str, Any],
) -> None:
@ -46,14 +48,16 @@ class HassioAddonEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
)
class HassioOSEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
class HassioOSEntity(
CoordinatorEntity[HassioDataUpdateCoordinator | HassioStatsDataUpdateCoordinator]
):
"""Base Entity for Hass.io OS."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: HassioDataUpdateCoordinator,
coordinator: HassioDataUpdateCoordinator | HassioStatsDataUpdateCoordinator,
entity_description: EntityDescription,
) -> None:
"""Initialize base entity."""
@ -72,14 +76,16 @@ class HassioOSEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
)
class HassioHostEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
class HassioHostEntity(
CoordinatorEntity[HassioDataUpdateCoordinator | HassioStatsDataUpdateCoordinator]
):
"""Base Entity for Hass.io host."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: HassioDataUpdateCoordinator,
coordinator: HassioDataUpdateCoordinator | HassioStatsDataUpdateCoordinator,
entity_description: EntityDescription,
) -> None:
"""Initialize base entity."""
@ -98,14 +104,16 @@ class HassioHostEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
)
class HassioSupervisorEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
class HassioSupervisorEntity(
CoordinatorEntity[HassioDataUpdateCoordinator | HassioStatsDataUpdateCoordinator]
):
"""Base Entity for Supervisor."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: HassioDataUpdateCoordinator,
coordinator: HassioDataUpdateCoordinator | HassioStatsDataUpdateCoordinator,
entity_description: EntityDescription,
) -> None:
"""Initialize base entity."""
@ -125,14 +133,16 @@ class HassioSupervisorEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
)
class HassioCoreEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
class HassioCoreEntity(
CoordinatorEntity[HassioDataUpdateCoordinator | HassioStatsDataUpdateCoordinator]
):
"""Base Entity for Core."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: HassioDataUpdateCoordinator,
coordinator: HassioDataUpdateCoordinator | HassioStatsDataUpdateCoordinator,
entity_description: EntityDescription,
) -> None:
"""Initialize base entity."""

View file

@ -12,10 +12,11 @@ from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfInformation
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ADDONS_COORDINATOR
from . import ADDONS_COORDINATOR, HassioStatsDataUpdateCoordinator
from .const import (
ATTR_CPU_PERCENT,
ATTR_MEMORY_PERCENT,
ATTR_SLUG,
ATTR_VERSION,
ATTR_VERSION_LATEST,
DATA_KEY_ADDONS,
@ -23,6 +24,7 @@ from .const import (
DATA_KEY_HOST,
DATA_KEY_OS,
DATA_KEY_SUPERVISOR,
SupervisorEntityModel,
)
from .entity import (
HassioAddonEntity,
@ -64,10 +66,8 @@ STATS_ENTITY_DESCRIPTIONS = (
),
)
ADDON_ENTITY_DESCRIPTIONS = COMMON_ENTITY_DESCRIPTIONS + STATS_ENTITY_DESCRIPTIONS
CORE_ENTITY_DESCRIPTIONS = STATS_ENTITY_DESCRIPTIONS
ADDON_ENTITY_DESCRIPTIONS = COMMON_ENTITY_DESCRIPTIONS
OS_ENTITY_DESCRIPTIONS = COMMON_ENTITY_DESCRIPTIONS
SUPERVISOR_ENTITY_DESCRIPTIONS = STATS_ENTITY_DESCRIPTIONS
HOST_ENTITY_DESCRIPTIONS = (
SensorEntityDescription(
@ -120,6 +120,9 @@ async def async_setup_entry(
entities: list[
HassioOSSensor | HassioAddonSensor | CoreSensor | SupervisorSensor | HostSensor
] = []
stats_entities: list[
AddonStatsSensor | CoreStatsSensor | SupervisorStatsSensor
] = []
for addon in coordinator.data[DATA_KEY_ADDONS].values():
for entity_description in ADDON_ENTITY_DESCRIPTIONS:
@ -130,19 +133,35 @@ async def async_setup_entry(
entity_description=entity_description,
)
)
for entity_description in STATS_ENTITY_DESCRIPTIONS:
stats_entities.append(
AddonStatsSensor(
addon=addon,
coordinator=HassioStatsDataUpdateCoordinator(
hass=hass,
model=SupervisorEntityModel.ADDON,
addon_slug=addon[ATTR_SLUG],
),
entity_description=entity_description,
)
)
for entity_description in CORE_ENTITY_DESCRIPTIONS:
entities.append(
CoreSensor(
coordinator=coordinator,
for entity_description in STATS_ENTITY_DESCRIPTIONS:
stats_entities.append(
CoreStatsSensor(
coordinator=HassioStatsDataUpdateCoordinator(
hass=hass,
model=SupervisorEntityModel.CORE,
),
entity_description=entity_description,
)
)
for entity_description in SUPERVISOR_ENTITY_DESCRIPTIONS:
entities.append(
SupervisorSensor(
coordinator=coordinator,
stats_entities.append(
SupervisorStatsSensor(
coordinator=HassioStatsDataUpdateCoordinator(
hass=hass,
model=SupervisorEntityModel.SUPERVISOR,
),
entity_description=entity_description,
)
)
@ -165,6 +184,7 @@ async def async_setup_entry(
)
async_add_entities(entities)
async_add_entities(stats_entities, True)
class HassioAddonSensor(HassioAddonEntity, SensorEntity):
@ -212,3 +232,63 @@ class HostSensor(HassioHostEntity, SensorEntity):
def native_value(self) -> str:
"""Return native value of entity."""
return self.coordinator.data[DATA_KEY_HOST][self.entity_description.key]
class AddonStatsSensor(HassioAddonEntity, SensorEntity):
"""Sensor to track a Hass.io add-on stats attribute."""
coordinator: HassioStatsDataUpdateCoordinator
@property
def native_value(self) -> str:
"""Return native value of entity."""
return self.coordinator.data[self.entity_description.key]
@property
def available(self) -> bool:
"""Return True if entity is available."""
return (
self.coordinator.last_update_success
and self.coordinator.data is not None
and self.entity_description.key in self.coordinator.data
)
class CoreStatsSensor(HassioCoreEntity, SensorEntity):
"""Sensor to track a Hass.io core stats attribute."""
coordinator: HassioStatsDataUpdateCoordinator
@property
def native_value(self) -> str:
"""Return native value of entity."""
return self.coordinator.data[self.entity_description.key]
@property
def available(self) -> bool:
"""Return True if entity is available."""
return (
self.coordinator.last_update_success
and self.coordinator.data is not None
and self.entity_description.key in self.coordinator.data
)
class SupervisorStatsSensor(HassioSupervisorEntity, SensorEntity):
"""Sensor to track a Hass.io supervisor stats attribute."""
coordinator: HassioStatsDataUpdateCoordinator
@property
def native_value(self) -> str:
"""Return native value of entity."""
return self.coordinator.data[self.entity_description.key]
@property
def available(self) -> bool:
"""Return True if entity is available."""
return (
self.coordinator.last_update_success
and self.coordinator.data is not None
and self.entity_description.key in self.coordinator.data
)

View file

@ -18,6 +18,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import (
ADDONS_COORDINATOR,
HassioDataUpdateCoordinator,
async_update_addon,
async_update_core,
async_update_os,
@ -89,6 +90,8 @@ async def async_setup_entry(
class SupervisorAddonUpdateEntity(HassioAddonEntity, UpdateEntity):
"""Update entity to handle updates for the Supervisor add-ons."""
coordinator: HassioDataUpdateCoordinator
_attr_supported_features = (
UpdateEntityFeature.INSTALL
| UpdateEntityFeature.BACKUP