Add binary sensor to Radarr (#79043)

* Add binary sensor to Radarr

* uno mas
This commit is contained in:
Robert Hillis 2022-09-25 17:50:09 -04:00 committed by GitHub
parent b820fed11a
commit b70027aec1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 106 additions and 9 deletions

View file

@ -17,7 +17,7 @@ from homeassistant.const import (
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 homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo, EntityDescription
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -25,12 +25,13 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DEFAULT_NAME, DOMAIN from .const import DEFAULT_NAME, DOMAIN
from .coordinator import ( from .coordinator import (
DiskSpaceDataUpdateCoordinator, DiskSpaceDataUpdateCoordinator,
HealthDataUpdateCoordinator,
MoviesDataUpdateCoordinator, MoviesDataUpdateCoordinator,
RadarrDataUpdateCoordinator, RadarrDataUpdateCoordinator,
StatusDataUpdateCoordinator, StatusDataUpdateCoordinator,
) )
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
@ -76,6 +77,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
coordinators: dict[str, RadarrDataUpdateCoordinator] = { coordinators: dict[str, RadarrDataUpdateCoordinator] = {
"status": StatusDataUpdateCoordinator(hass, host_configuration, radarr), "status": StatusDataUpdateCoordinator(hass, host_configuration, radarr),
"disk_space": DiskSpaceDataUpdateCoordinator(hass, host_configuration, radarr), "disk_space": DiskSpaceDataUpdateCoordinator(hass, host_configuration, radarr),
"health": HealthDataUpdateCoordinator(hass, host_configuration, radarr),
"movie": MoviesDataUpdateCoordinator(hass, host_configuration, radarr), "movie": MoviesDataUpdateCoordinator(hass, host_configuration, radarr),
} }
for coordinator in coordinators.values(): for coordinator in coordinators.values():
@ -98,6 +100,17 @@ class RadarrEntity(CoordinatorEntity[RadarrDataUpdateCoordinator]):
coordinator: RadarrDataUpdateCoordinator coordinator: RadarrDataUpdateCoordinator
def __init__(
self,
coordinator: RadarrDataUpdateCoordinator,
description: EntityDescription,
) -> None:
"""Create Radarr entity."""
super().__init__(coordinator)
self.entity_description = description
self._attr_name = f"{DEFAULT_NAME} {description.name}"
self._attr_unique_id = f"{coordinator.config_entry.entry_id}_{description.key}"
@property @property
def device_info(self) -> DeviceInfo: def device_info(self) -> DeviceInfo:
"""Return device information about the Radarr instance.""" """Return device information about the Radarr instance."""

View file

@ -0,0 +1,41 @@
"""Support for Radarr binary sensors."""
from __future__ import annotations
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import RadarrEntity
from .const import DOMAIN, HEALTH_ISSUES
BINARY_SENSOR_TYPE = BinarySensorEntityDescription(
key="health",
name="Health",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=BinarySensorDeviceClass.PROBLEM,
)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Radarr sensors based on a config entry."""
coordinator = hass.data[DOMAIN][entry.entry_id]["health"]
async_add_entities([RadarrBinarySensor(coordinator, BINARY_SENSOR_TYPE)])
class RadarrBinarySensor(RadarrEntity, BinarySensorEntity):
"""Implementation of a Radarr binary sensor."""
@property
def is_on(self) -> bool:
"""Return True if the entity is on."""
return any(report.source in HEALTH_ISSUES for report in self.coordinator.data)

View file

@ -8,4 +8,11 @@ DOMAIN: Final = "radarr"
DEFAULT_NAME = "Radarr" DEFAULT_NAME = "Radarr"
DEFAULT_URL = "http://127.0.0.1:7878" DEFAULT_URL = "http://127.0.0.1:7878"
HEALTH_ISSUES = (
"DownloadClientCheck",
"DownloadClientStatusCheck",
"IndexerRssCheck",
"IndexerSearchCheck",
)
LOGGER = logging.getLogger(__package__) LOGGER = logging.getLogger(__package__)

View file

@ -5,7 +5,7 @@ from abc import abstractmethod
from datetime import timedelta from datetime import timedelta
from typing import Generic, TypeVar, cast from typing import Generic, TypeVar, cast
from aiopyarr import RootFolder, SystemStatus, exceptions from aiopyarr import Health, RootFolder, SystemStatus, exceptions
from aiopyarr.models.host_configuration import PyArrHostConfiguration from aiopyarr.models.host_configuration import PyArrHostConfiguration
from aiopyarr.radarr_client import RadarrClient from aiopyarr.radarr_client import RadarrClient
@ -16,7 +16,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from .const import DOMAIN, LOGGER from .const import DOMAIN, LOGGER
T = TypeVar("T", SystemStatus, list[RootFolder], int) T = TypeVar("T", SystemStatus, list[RootFolder], list[Health], int)
class RadarrDataUpdateCoordinator(DataUpdateCoordinator, Generic[T]): class RadarrDataUpdateCoordinator(DataUpdateCoordinator, Generic[T]):
@ -74,6 +74,14 @@ class DiskSpaceDataUpdateCoordinator(RadarrDataUpdateCoordinator):
return cast(list, await self.api_client.async_get_root_folders()) return cast(list, await self.api_client.async_get_root_folders())
class HealthDataUpdateCoordinator(RadarrDataUpdateCoordinator):
"""Health update coordinator."""
async def _fetch_data(self) -> list[Health]:
"""Fetch the health data."""
return await self.api_client.async_get_failed_health_checks()
class MoviesDataUpdateCoordinator(RadarrDataUpdateCoordinator): class MoviesDataUpdateCoordinator(RadarrDataUpdateCoordinator):
"""Movies update coordinator.""" """Movies update coordinator."""

View file

@ -35,7 +35,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
from . import RadarrEntity from . import RadarrEntity
from .const import DEFAULT_NAME, DOMAIN from .const import DOMAIN
from .coordinator import RadarrDataUpdateCoordinator, T from .coordinator import RadarrDataUpdateCoordinator, T
@ -182,10 +182,7 @@ class RadarrSensor(RadarrEntity, SensorEntity):
folder_name: str = "", folder_name: str = "",
) -> None: ) -> None:
"""Create Radarr entity.""" """Create Radarr entity."""
super().__init__(coordinator) super().__init__(coordinator, description)
self.entity_description = description
self._attr_name = f"{DEFAULT_NAME} {description.name}"
self._attr_unique_id = f"{coordinator.config_entry.entry_id}_{description.key}"
self.folder_name = folder_name self.folder_name = folder_name
@property @property

View file

@ -82,6 +82,12 @@ def mock_connection(
headers={"Content-Type": CONTENT_TYPE_JSON}, headers={"Content-Type": CONTENT_TYPE_JSON},
) )
aioclient_mock.get(
f"{url}/api/v3/health",
text=load_fixture("radarr/health.json"),
headers={"Content-Type": CONTENT_TYPE_JSON},
)
if windows: if windows:
aioclient_mock.get( aioclient_mock.get(
f"{url}/api/v3/rootfolder", f"{url}/api/v3/rootfolder",

View file

@ -0,0 +1,8 @@
[
{
"source": "DownloadClientStatusCheck",
"type": "error",
"message": "All download clients are unavailable due to failures",
"wikiUrl": "https://wiki.servarr.com/radarr/system#completed-failed-download-handling"
}
]

View file

@ -0,0 +1,17 @@
"""The tests for Radarr binary sensor platform."""
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.const import ATTR_DEVICE_CLASS, STATE_ON
from homeassistant.core import HomeAssistant
from . import setup_integration
from tests.test_util.aiohttp import AiohttpClientMocker
async def test_binary_sensors(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker):
"""Test for binary sensor values."""
await setup_integration(hass, aioclient_mock)
state = hass.states.get("binary_sensor.radarr_health")
assert state.state == STATE_ON
assert state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.PROBLEM