parent
b820fed11a
commit
b70027aec1
|
@ -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."""
|
||||
|
|
|
@ -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)
|
|
@ -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__)
|
||||
|
|
|
@ -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."""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
|
@ -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
|
Loading…
Reference in New Issue