From 42bd66430593e202c95823b4c47e11c6715e5f7f Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Sun, 25 Sep 2022 10:11:53 -0400 Subject: [PATCH] Add diagnostic sensor to Radarr (#79044) * Add diagnostic sensor to Radarr * coverage --- .coveragerc | 1 - homeassistant/components/radarr/__init__.py | 12 ++++----- .../components/radarr/coordinator.py | 9 +++---- homeassistant/components/radarr/sensor.py | 25 +++++++++++++------ tests/components/radarr/test_sensor.py | 23 ++++++++--------- 5 files changed, 38 insertions(+), 32 deletions(-) diff --git a/.coveragerc b/.coveragerc index 8bb6f3e5873..0a4b3803cd2 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1000,7 +1000,6 @@ omit = homeassistant/components/rachio/entity.py homeassistant/components/rachio/switch.py homeassistant/components/rachio/webhooks.py - homeassistant/components/radarr/sensor.py homeassistant/components/radio_browser/__init__.py homeassistant/components/radio_browser/media_source.py homeassistant/components/radiotherm/__init__.py diff --git a/homeassistant/components/radarr/__init__.py b/homeassistant/components/radarr/__init__.py index 467f95780e8..c4845f1ba30 100644 --- a/homeassistant/components/radarr/__init__.py +++ b/homeassistant/components/radarr/__init__.py @@ -7,6 +7,7 @@ from aiopyarr.radarr_client import RadarrClient from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( + ATTR_SW_VERSION, CONF_API_KEY, CONF_PLATFORM, CONF_URL, @@ -77,13 +78,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: "disk_space": DiskSpaceDataUpdateCoordinator(hass, host_configuration, radarr), "movie": MoviesDataUpdateCoordinator(hass, host_configuration, radarr), } - # Temporary, until we add diagnostic entities - _version = None for coordinator in coordinators.values(): await coordinator.async_config_entry_first_refresh() - if isinstance(coordinator, StatusDataUpdateCoordinator): - _version = coordinator.data - coordinator.system_version = _version hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinators await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) @@ -105,11 +101,13 @@ class RadarrEntity(CoordinatorEntity[RadarrDataUpdateCoordinator]): @property def device_info(self) -> DeviceInfo: """Return device information about the Radarr instance.""" - return DeviceInfo( + device_info = DeviceInfo( configuration_url=self.coordinator.host_configuration.url, entry_type=DeviceEntryType.SERVICE, identifiers={(DOMAIN, self.coordinator.config_entry.entry_id)}, manufacturer=DEFAULT_NAME, name=self.coordinator.config_entry.title, - sw_version=self.coordinator.system_version, ) + if isinstance(self.coordinator, StatusDataUpdateCoordinator): + device_info[ATTR_SW_VERSION] = self.coordinator.data.version + return device_info diff --git a/homeassistant/components/radarr/coordinator.py b/homeassistant/components/radarr/coordinator.py index a66455c8cc3..a13637f84b1 100644 --- a/homeassistant/components/radarr/coordinator.py +++ b/homeassistant/components/radarr/coordinator.py @@ -5,7 +5,7 @@ from abc import abstractmethod from datetime import timedelta from typing import Generic, TypeVar, cast -from aiopyarr import RootFolder, exceptions +from aiopyarr import RootFolder, SystemStatus, exceptions from aiopyarr.models.host_configuration import PyArrHostConfiguration from aiopyarr.radarr_client import RadarrClient @@ -16,7 +16,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .const import DOMAIN, LOGGER -T = TypeVar("T", str, list[RootFolder], int) +T = TypeVar("T", SystemStatus, list[RootFolder], int) class RadarrDataUpdateCoordinator(DataUpdateCoordinator, Generic[T]): @@ -39,7 +39,6 @@ class RadarrDataUpdateCoordinator(DataUpdateCoordinator, Generic[T]): ) self.api_client = api_client self.host_configuration = host_configuration - self.system_version: str | None = None async def _async_update_data(self) -> T: """Get the latest data from Radarr.""" @@ -62,9 +61,9 @@ class RadarrDataUpdateCoordinator(DataUpdateCoordinator, Generic[T]): class StatusDataUpdateCoordinator(RadarrDataUpdateCoordinator): """Status update coordinator for Radarr.""" - async def _fetch_data(self) -> str: + async def _fetch_data(self) -> SystemStatus: """Fetch the data.""" - return (await self.api_client.async_get_system_status()).version + return await self.api_client.async_get_system_status() class DiskSpaceDataUpdateCoordinator(RadarrDataUpdateCoordinator): diff --git a/homeassistant/components/radarr/sensor.py b/homeassistant/components/radarr/sensor.py index ac678198cd7..6e11fb6d2bc 100644 --- a/homeassistant/components/radarr/sensor.py +++ b/homeassistant/components/radarr/sensor.py @@ -4,13 +4,15 @@ from __future__ import annotations from collections.abc import Callable from copy import deepcopy from dataclasses import dataclass +from datetime import timezone from typing import Generic -from aiopyarr import RootFolder +from aiopyarr import Diskspace, RootFolder import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, SensorEntityDescription, ) @@ -28,6 +30,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType @@ -36,11 +39,11 @@ from .const import DEFAULT_NAME, DOMAIN from .coordinator import RadarrDataUpdateCoordinator, T -def get_space(coordinator: RadarrDataUpdateCoordinator, name: str) -> str: +def get_space(data: list[Diskspace], name: str) -> str: """Get space.""" space = [ mount.freeSpace / 1024 ** BYTE_SIZES.index(DATA_GIGABYTES) - for mount in coordinator.data + for mount in data if name in mount.path ] return f"{space[0]:.2f}" @@ -61,7 +64,7 @@ def get_modified_description( class RadarrSensorEntityDescriptionMixIn(Generic[T]): """Mixin for required keys.""" - value: Callable[[RadarrDataUpdateCoordinator[T], str], str] + value_fn: Callable[[T, str], str] @dataclass @@ -82,7 +85,7 @@ SENSOR_TYPES: dict[str, RadarrSensorEntityDescription] = { name="Disk space", native_unit_of_measurement=DATA_GIGABYTES, icon="mdi:harddisk", - value=get_space, + value_fn=get_space, description_fn=get_modified_description, ), "movie": RadarrSensorEntityDescription( @@ -91,7 +94,15 @@ SENSOR_TYPES: dict[str, RadarrSensorEntityDescription] = { native_unit_of_measurement="Movies", icon="mdi:television", entity_registry_enabled_default=False, - value=lambda coordinator, _: coordinator.data, + value_fn=lambda data, _: data, + ), + "status": RadarrSensorEntityDescription( + key="start_time", + name="Start time", + device_class=SensorDeviceClass.TIMESTAMP, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda data, _: data.startTime.replace(tzinfo=timezone.utc), ), } @@ -182,4 +193,4 @@ class RadarrSensor(RadarrEntity, SensorEntity): @property def native_value(self) -> StateType: """Return the state of the sensor.""" - return self.entity_description.value(self.coordinator, self.folder_name) + return self.entity_description.value_fn(self.coordinator.data, self.folder_name) diff --git a/tests/components/radarr/test_sensor.py b/tests/components/radarr/test_sensor.py index a95885f1b1b..a4caaaa6a54 100644 --- a/tests/components/radarr/test_sensor.py +++ b/tests/components/radarr/test_sensor.py @@ -1,33 +1,32 @@ """The tests for Radarr sensor platform.""" -from datetime import timedelta +from unittest.mock import AsyncMock -from homeassistant.components.radarr.sensor import SENSOR_TYPES -from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT +from homeassistant.components.sensor import SensorDeviceClass +from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_UNIT_OF_MEASUREMENT from homeassistant.core import HomeAssistant -import homeassistant.util.dt as dt_util from . import setup_integration -from tests.common import async_fire_time_changed from tests.test_util.aiohttp import AiohttpClientMocker -async def test_sensors(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker): +async def test_sensors( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + entity_registry_enabled_by_default: AsyncMock, +): """Test for successfully setting up the Radarr platform.""" - for description in SENSOR_TYPES.values(): - description.entity_registry_enabled_default = True await setup_integration(hass, aioclient_mock) - next_update = dt_util.utcnow() + timedelta(seconds=30) - async_fire_time_changed(hass, next_update) - await hass.async_block_till_done() - state = hass.states.get("sensor.radarr_disk_space_downloads") assert state.state == "263.10" assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "GB" state = hass.states.get("sensor.radarr_movies") assert state.state == "1" assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "Movies" + state = hass.states.get("sensor.radarr_start_time") + assert state.state == "2020-09-01T23:50:20+00:00" + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TIMESTAMP async def test_windows(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker):