Add binary sensor to Radarr (#79043)
* Add binary sensor to Radarr * uno mas
This commit is contained in:
parent
b820fed11a
commit
b70027aec1
8 changed files with 106 additions and 9 deletions
|
@ -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."""
|
||||||
|
|
41
homeassistant/components/radarr/binary_sensor.py
Normal file
41
homeassistant/components/radarr/binary_sensor.py
Normal 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)
|
|
@ -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__)
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
8
tests/components/radarr/fixtures/health.json
Normal file
8
tests/components/radarr/fixtures/health.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
17
tests/components/radarr/test_binary_sensor.py
Normal file
17
tests/components/radarr/test_binary_sensor.py
Normal 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
|
Loading…
Add table
Reference in a new issue