Add sensors for supervisor host (#89461)

Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
Joakim Sørensen 2023-03-13 15:39:49 +01:00 committed by GitHub
parent 07b25939a2
commit 11e21378b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 122 additions and 13 deletions

View file

@ -69,6 +69,7 @@ from .const import (
ATTR_VERSION, ATTR_VERSION,
DATA_KEY_ADDONS, DATA_KEY_ADDONS,
DATA_KEY_CORE, DATA_KEY_CORE,
DATA_KEY_HOST,
DATA_KEY_OS, DATA_KEY_OS,
DATA_KEY_SUPERVISOR, DATA_KEY_SUPERVISOR,
DOMAIN, DOMAIN,
@ -668,6 +669,22 @@ def async_register_os_in_dev_reg(
dev_reg.async_get_or_create(config_entry_id=entry_id, **params) dev_reg.async_get_or_create(config_entry_id=entry_id, **params)
@callback
def async_register_host_in_dev_reg(
entry_id: str,
dev_reg: dr.DeviceRegistry,
) -> None:
"""Register host in the device registry."""
params = DeviceInfo(
identifiers={(DOMAIN, "host")},
manufacturer="Home Assistant",
model=SupervisorEntityModel.HOST,
name="Home Assistant Host",
entry_type=dr.DeviceEntryType.SERVICE,
)
dev_reg.async_get_or_create(config_entry_id=entry_id, **params)
@callback @callback
def async_register_core_in_dev_reg( def async_register_core_in_dev_reg(
entry_id: str, entry_id: str,
@ -777,6 +794,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
**supervisor_info, **supervisor_info,
**get_supervisor_stats(self.hass), **get_supervisor_stats(self.hass),
} }
new_data[DATA_KEY_HOST] = get_host_info(self.hass) or {}
# If this is the initial refresh, register all addons and return the dict # If this is the initial refresh, register all addons and return the dict
if not self.data: if not self.data:
@ -789,6 +807,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
async_register_supervisor_in_dev_reg( async_register_supervisor_in_dev_reg(
self.entry_id, self.dev_reg, new_data[DATA_KEY_SUPERVISOR] self.entry_id, self.dev_reg, new_data[DATA_KEY_SUPERVISOR]
) )
async_register_host_in_dev_reg(self.entry_id, self.dev_reg)
if self.is_hass_os: if self.is_hass_os:
async_register_os_in_dev_reg( async_register_os_in_dev_reg(
self.entry_id, self.dev_reg, new_data[DATA_KEY_OS] self.entry_id, self.dev_reg, new_data[DATA_KEY_OS]

View file

@ -68,6 +68,7 @@ DATA_KEY_ADDONS = "addons"
DATA_KEY_OS = "os" DATA_KEY_OS = "os"
DATA_KEY_SUPERVISOR = "supervisor" DATA_KEY_SUPERVISOR = "supervisor"
DATA_KEY_CORE = "core" DATA_KEY_CORE = "core"
DATA_KEY_HOST = "host"
class SupervisorEntityModel(str, Enum): class SupervisorEntityModel(str, Enum):
@ -77,3 +78,4 @@ class SupervisorEntityModel(str, Enum):
OS = "Home Assistant Operating System" OS = "Home Assistant Operating System"
CORE = "Home Assistant Core" CORE = "Home Assistant Core"
SUPERVIOSR = "Home Assistant Supervisor" SUPERVIOSR = "Home Assistant Supervisor"
HOST = "Home Assistant Host"

View file

@ -11,6 +11,7 @@ from .const import (
ATTR_SLUG, ATTR_SLUG,
DATA_KEY_ADDONS, DATA_KEY_ADDONS,
DATA_KEY_CORE, DATA_KEY_CORE,
DATA_KEY_HOST,
DATA_KEY_OS, DATA_KEY_OS,
DATA_KEY_SUPERVISOR, DATA_KEY_SUPERVISOR,
) )
@ -71,6 +72,32 @@ class HassioOSEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
) )
class HassioHostEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
"""Base Entity for Hass.io host."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: HassioDataUpdateCoordinator,
entity_description: EntityDescription,
) -> None:
"""Initialize base entity."""
super().__init__(coordinator)
self.entity_description = entity_description
self._attr_unique_id = f"home_assistant_host_{entity_description.key}"
self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, "host")})
@property
def available(self) -> bool:
"""Return True if entity is available."""
return (
super().available
and DATA_KEY_HOST in self.coordinator.data
and self.entity_description.key in self.coordinator.data[DATA_KEY_HOST]
)
class HassioSupervisorEntity(CoordinatorEntity[HassioDataUpdateCoordinator]): class HassioSupervisorEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
"""Base Entity for Supervisor.""" """Base Entity for Supervisor."""

View file

@ -2,12 +2,13 @@
from __future__ import annotations from __future__ import annotations
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity, SensorEntity,
SensorEntityDescription, SensorEntityDescription,
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfInformation
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -19,12 +20,14 @@ from .const import (
ATTR_VERSION_LATEST, ATTR_VERSION_LATEST,
DATA_KEY_ADDONS, DATA_KEY_ADDONS,
DATA_KEY_CORE, DATA_KEY_CORE,
DATA_KEY_HOST,
DATA_KEY_OS, DATA_KEY_OS,
DATA_KEY_SUPERVISOR, DATA_KEY_SUPERVISOR,
) )
from .entity import ( from .entity import (
HassioAddonEntity, HassioAddonEntity,
HassioCoreEntity, HassioCoreEntity,
HassioHostEntity,
HassioOSEntity, HassioOSEntity,
HassioSupervisorEntity, HassioSupervisorEntity,
) )
@ -66,6 +69,45 @@ CORE_ENTITY_DESCRIPTIONS = STATS_ENTITY_DESCRIPTIONS
OS_ENTITY_DESCRIPTIONS = COMMON_ENTITY_DESCRIPTIONS OS_ENTITY_DESCRIPTIONS = COMMON_ENTITY_DESCRIPTIONS
SUPERVISOR_ENTITY_DESCRIPTIONS = STATS_ENTITY_DESCRIPTIONS SUPERVISOR_ENTITY_DESCRIPTIONS = STATS_ENTITY_DESCRIPTIONS
HOST_ENTITY_DESCRIPTIONS = (
SensorEntityDescription(
entity_registry_enabled_default=False,
key="agent_version",
name="OS Agent version",
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
entity_registry_enabled_default=False,
key="apparmor_version",
name="Apparmor version",
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
entity_registry_enabled_default=False,
key="disk_total",
name="Disk total",
native_unit_of_measurement=UnitOfInformation.GIGABYTES,
device_class=SensorDeviceClass.DATA_SIZE,
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
entity_registry_enabled_default=False,
key="disk_used",
name="Disk used",
native_unit_of_measurement=UnitOfInformation.GIGABYTES,
device_class=SensorDeviceClass.DATA_SIZE,
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
entity_registry_enabled_default=False,
key="disk_free",
name="Disk free",
native_unit_of_measurement=UnitOfInformation.GIGABYTES,
device_class=SensorDeviceClass.DATA_SIZE,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
@ -76,7 +118,7 @@ async def async_setup_entry(
coordinator = hass.data[ADDONS_COORDINATOR] coordinator = hass.data[ADDONS_COORDINATOR]
entities: list[ entities: list[
HassioOSSensor | HassioAddonSensor | CoreSensor | SupervisorSensor HassioOSSensor | HassioAddonSensor | CoreSensor | SupervisorSensor | HostSensor
] = [] ] = []
for addon in coordinator.data[DATA_KEY_ADDONS].values(): for addon in coordinator.data[DATA_KEY_ADDONS].values():
@ -105,6 +147,14 @@ async def async_setup_entry(
) )
) )
for entity_description in HOST_ENTITY_DESCRIPTIONS:
entities.append(
HostSensor(
coordinator=coordinator,
entity_description=entity_description,
)
)
if coordinator.is_hass_os: if coordinator.is_hass_os:
for entity_description in OS_ENTITY_DESCRIPTIONS: for entity_description in OS_ENTITY_DESCRIPTIONS:
entities.append( entities.append(
@ -153,3 +203,12 @@ class SupervisorSensor(HassioSupervisorEntity, SensorEntity):
def native_value(self) -> str: def native_value(self) -> str:
"""Return native value of entity.""" """Return native value of entity."""
return self.coordinator.data[DATA_KEY_SUPERVISOR][self.entity_description.key] return self.coordinator.data[DATA_KEY_SUPERVISOR][self.entity_description.key]
class HostSensor(HassioHostEntity, SensorEntity):
"""Sensor to track a host attribute."""
@property
def native_value(self) -> str:
"""Return native value of entity."""
return self.coordinator.data[DATA_KEY_HOST][self.entity_description.key]

View file

@ -211,5 +211,6 @@ async def test_diagnostics(
assert "core" in diagnostics["coordinator_data"] assert "core" in diagnostics["coordinator_data"]
assert "supervisor" in diagnostics["coordinator_data"] assert "supervisor" in diagnostics["coordinator_data"]
assert "os" in diagnostics["coordinator_data"] assert "os" in diagnostics["coordinator_data"]
assert "host" in diagnostics["coordinator_data"]
assert len(diagnostics["devices"]) == 5 assert len(diagnostics["devices"]) == 6

View file

@ -678,7 +678,7 @@ async def test_device_registry_calls(hass: HomeAssistant) -> None:
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(dev_reg.devices) == 5 assert len(dev_reg.devices) == 6
supervisor_mock_data = { supervisor_mock_data = {
"version": "1.0.0", "version": "1.0.0",
@ -709,11 +709,11 @@ async def test_device_registry_calls(hass: HomeAssistant) -> None:
): ):
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=1)) async_fire_time_changed(hass, dt_util.now() + timedelta(hours=1))
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(dev_reg.devices) == 4 assert len(dev_reg.devices) == 5
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=2)) async_fire_time_changed(hass, dt_util.now() + timedelta(hours=2))
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(dev_reg.devices) == 4 assert len(dev_reg.devices) == 5
supervisor_mock_data = { supervisor_mock_data = {
"version": "1.0.0", "version": "1.0.0",
@ -763,7 +763,7 @@ async def test_device_registry_calls(hass: HomeAssistant) -> None:
): ):
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=3)) async_fire_time_changed(hass, dt_util.now() + timedelta(hours=3))
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(dev_reg.devices) == 4 assert len(dev_reg.devices) == 5
async def test_coordinator_updates( async def test_coordinator_updates(

View file

@ -44,14 +44,12 @@ def mock_all(aioclient_mock, request):
json={ json={
"result": "ok", "result": "ok",
"data": { "data": {
"result": "ok", "agent_version": "1.0.0",
"data": {
"chassis": "vm", "chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)", "operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64", "kernel": "4.19.0-6-amd64",
}, },
}, },
},
) )
aioclient_mock.get( aioclient_mock.get(
"http://127.0.0.1/core/info", "http://127.0.0.1/core/info",
@ -179,6 +177,9 @@ def mock_all(aioclient_mock, request):
[ [
("sensor.home_assistant_operating_system_version", "1.0.0"), ("sensor.home_assistant_operating_system_version", "1.0.0"),
("sensor.home_assistant_operating_system_newest_version", "1.0.0"), ("sensor.home_assistant_operating_system_newest_version", "1.0.0"),
("sensor.home_assistant_host_os_agent_version", "1.0.0"),
("sensor.home_assistant_core_cpu_percent", "0.99"),
("sensor.home_assistant_supervisor_cpu_percent", "0.99"),
("sensor.test_version", "2.0.0"), ("sensor.test_version", "2.0.0"),
("sensor.test_newest_version", "2.0.1"), ("sensor.test_newest_version", "2.0.1"),
("sensor.test2_version", "3.1.0"), ("sensor.test2_version", "3.1.0"),