Add binary sensor to Radarr (#79043)

* Add binary sensor to Radarr

* uno mas
pull/79063/head^2
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.helpers.aiohttp_client import async_get_clientsession
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.typing import ConfigType
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 .coordinator import (
DiskSpaceDataUpdateCoordinator,
HealthDataUpdateCoordinator,
MoviesDataUpdateCoordinator,
RadarrDataUpdateCoordinator,
StatusDataUpdateCoordinator,
)
PLATFORMS = [Platform.SENSOR]
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
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] = {
"status": StatusDataUpdateCoordinator(hass, host_configuration, radarr),
"disk_space": DiskSpaceDataUpdateCoordinator(hass, host_configuration, radarr),
"health": HealthDataUpdateCoordinator(hass, host_configuration, radarr),
"movie": MoviesDataUpdateCoordinator(hass, host_configuration, radarr),
}
for coordinator in coordinators.values():
@ -98,6 +100,17 @@ class RadarrEntity(CoordinatorEntity[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
def device_info(self) -> DeviceInfo:
"""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_URL = "http://127.0.0.1:7878"
HEALTH_ISSUES = (
"DownloadClientCheck",
"DownloadClientStatusCheck",
"IndexerRssCheck",
"IndexerSearchCheck",
)
LOGGER = logging.getLogger(__package__)

View File

@ -5,7 +5,7 @@ from abc import abstractmethod
from datetime import timedelta
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.radarr_client import RadarrClient
@ -16,7 +16,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
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]):
@ -74,6 +74,14 @@ class DiskSpaceDataUpdateCoordinator(RadarrDataUpdateCoordinator):
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):
"""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 . import RadarrEntity
from .const import DEFAULT_NAME, DOMAIN
from .const import DOMAIN
from .coordinator import RadarrDataUpdateCoordinator, T
@ -182,10 +182,7 @@ class RadarrSensor(RadarrEntity, SensorEntity):
folder_name: str = "",
) -> 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}"
super().__init__(coordinator, description)
self.folder_name = folder_name
@property

View File

@ -82,6 +82,12 @@ def mock_connection(
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:
aioclient_mock.get(
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