diff --git a/.strict-typing b/.strict-typing index 1f990bd81c5..13b2752e09c 100644 --- a/.strict-typing +++ b/.strict-typing @@ -162,6 +162,7 @@ homeassistant.components.lacrosse_view.* homeassistant.components.lametric.* homeassistant.components.laundrify.* homeassistant.components.lcn.* +homeassistant.components.lidarr.* homeassistant.components.lifx.* homeassistant.components.light.* homeassistant.components.litterrobot.* diff --git a/homeassistant/components/lidarr/__init__.py b/homeassistant/components/lidarr/__init__.py index 7fd3e799d88..9222164227b 100644 --- a/homeassistant/components/lidarr/__init__.py +++ b/homeassistant/components/lidarr/__init__.py @@ -1,6 +1,8 @@ """The Lidarr component.""" from __future__ import annotations +from typing import Any + from aiopyarr.lidarr_client import LidarrClient from aiopyarr.models.host_configuration import PyArrHostConfiguration @@ -18,6 +20,7 @@ from .coordinator import ( LidarrDataUpdateCoordinator, QueueDataUpdateCoordinator, StatusDataUpdateCoordinator, + T, WantedDataUpdateCoordinator, ) @@ -36,7 +39,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: session=async_get_clientsession(hass, host_configuration.verify_ssl), request_timeout=60, ) - coordinators: dict[str, LidarrDataUpdateCoordinator] = { + coordinators: dict[str, LidarrDataUpdateCoordinator[Any]] = { "disk_space": DiskSpaceDataUpdateCoordinator(hass, host_configuration, lidarr), "queue": QueueDataUpdateCoordinator(hass, host_configuration, lidarr), "status": StatusDataUpdateCoordinator(hass, host_configuration, lidarr), @@ -63,13 +66,15 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class LidarrEntity(CoordinatorEntity[LidarrDataUpdateCoordinator]): +class LidarrEntity(CoordinatorEntity[LidarrDataUpdateCoordinator[T]]): """Defines a base Lidarr entity.""" _attr_has_entity_name = True def __init__( - self, coordinator: LidarrDataUpdateCoordinator, description: EntityDescription + self, + coordinator: LidarrDataUpdateCoordinator[T], + description: EntityDescription, ) -> None: """Initialize the Lidarr entity.""" super().__init__(coordinator) diff --git a/homeassistant/components/lidarr/coordinator.py b/homeassistant/components/lidarr/coordinator.py index be789c6a32a..c02d6525871 100644 --- a/homeassistant/components/lidarr/coordinator.py +++ b/homeassistant/components/lidarr/coordinator.py @@ -3,7 +3,7 @@ from __future__ import annotations from abc import abstractmethod from datetime import timedelta -from typing import Generic, TypeVar, cast +from typing import Generic, TypeVar, Union, cast from aiopyarr import LidarrAlbum, LidarrQueue, LidarrRootFolder, exceptions from aiopyarr.lidarr_client import LidarrClient @@ -16,10 +16,10 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .const import DEFAULT_MAX_RECORDS, DOMAIN, LOGGER -T = TypeVar("T", list[LidarrRootFolder], LidarrQueue, str, LidarrAlbum) +T = TypeVar("T", bound=Union[list[LidarrRootFolder], LidarrQueue, str, LidarrAlbum]) -class LidarrDataUpdateCoordinator(DataUpdateCoordinator, Generic[T]): +class LidarrDataUpdateCoordinator(DataUpdateCoordinator[T], Generic[T]): """Data update coordinator for the Lidarr integration.""" config_entry: ConfigEntry @@ -59,15 +59,19 @@ class LidarrDataUpdateCoordinator(DataUpdateCoordinator, Generic[T]): raise NotImplementedError -class DiskSpaceDataUpdateCoordinator(LidarrDataUpdateCoordinator): +class DiskSpaceDataUpdateCoordinator( + LidarrDataUpdateCoordinator[list[LidarrRootFolder]] +): """Disk space update coordinator for Lidarr.""" async def _fetch_data(self) -> list[LidarrRootFolder]: """Fetch the data.""" - return cast(list, await self.api_client.async_get_root_folders()) + return cast( + list[LidarrRootFolder], await self.api_client.async_get_root_folders() + ) -class QueueDataUpdateCoordinator(LidarrDataUpdateCoordinator): +class QueueDataUpdateCoordinator(LidarrDataUpdateCoordinator[LidarrQueue]): """Queue update coordinator.""" async def _fetch_data(self) -> LidarrQueue: @@ -75,7 +79,7 @@ class QueueDataUpdateCoordinator(LidarrDataUpdateCoordinator): return await self.api_client.async_get_queue(page_size=DEFAULT_MAX_RECORDS) -class StatusDataUpdateCoordinator(LidarrDataUpdateCoordinator): +class StatusDataUpdateCoordinator(LidarrDataUpdateCoordinator[str]): """Status update coordinator for Lidarr.""" async def _fetch_data(self) -> str: @@ -83,7 +87,7 @@ class StatusDataUpdateCoordinator(LidarrDataUpdateCoordinator): return (await self.api_client.async_get_system_status()).version -class WantedDataUpdateCoordinator(LidarrDataUpdateCoordinator): +class WantedDataUpdateCoordinator(LidarrDataUpdateCoordinator[LidarrAlbum]): """Wanted update coordinator.""" async def _fetch_data(self) -> LidarrAlbum: diff --git a/homeassistant/components/lidarr/sensor.py b/homeassistant/components/lidarr/sensor.py index 8529d9a6469..2e5f9bb710f 100644 --- a/homeassistant/components/lidarr/sensor.py +++ b/homeassistant/components/lidarr/sensor.py @@ -4,10 +4,9 @@ from __future__ import annotations from collections.abc import Callable from copy import deepcopy from dataclasses import dataclass -from datetime import datetime -from typing import Generic +from typing import Any, Generic -from aiopyarr import LidarrQueueItem, LidarrRootFolder +from aiopyarr import LidarrQueue, LidarrQueueItem, LidarrRootFolder from homeassistant.components.sensor import ( SensorEntity, @@ -18,7 +17,6 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import DATA_GIGABYTES from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import StateType from . import LidarrEntity from .const import BYTE_SIZES, DOMAIN @@ -27,7 +25,7 @@ from .coordinator import LidarrDataUpdateCoordinator, T def get_space(data: list[LidarrRootFolder], name: str) -> str: """Get space.""" - space = [] + space: list[float] = [] for mount in data: if name in mount.path: mount.freeSpace = mount.freeSpace if mount.accessible else 0 @@ -36,8 +34,8 @@ def get_space(data: list[LidarrRootFolder], name: str) -> str: def get_modified_description( - description: LidarrSensorEntityDescription, mount: LidarrRootFolder -) -> tuple[LidarrSensorEntityDescription, str]: + description: LidarrSensorEntityDescription[T], mount: LidarrRootFolder +) -> tuple[LidarrSensorEntityDescription[T], str]: """Return modified description and folder name.""" desc = deepcopy(description) name = mount.path.rsplit("/")[-1].rsplit("\\")[-1] @@ -50,25 +48,23 @@ def get_modified_description( class LidarrSensorEntityDescriptionMixIn(Generic[T]): """Mixin for required keys.""" - value_fn: Callable[[T, str], str] + value_fn: Callable[[T, str], str | int] @dataclass class LidarrSensorEntityDescription( - SensorEntityDescription, LidarrSensorEntityDescriptionMixIn, Generic[T] + SensorEntityDescription, LidarrSensorEntityDescriptionMixIn[T], Generic[T] ): """Class to describe a Lidarr sensor.""" - attributes_fn: Callable[ - [T], dict[str, StateType | datetime] | None - ] = lambda _: None + attributes_fn: Callable[[T], dict[str, str] | None] = lambda _: None description_fn: Callable[ - [LidarrSensorEntityDescription, LidarrRootFolder], - tuple[LidarrSensorEntityDescription, str] | None, - ] = lambda _, __: None + [LidarrSensorEntityDescription[T], LidarrRootFolder], + tuple[LidarrSensorEntityDescription[T], str] | None, + ] | None = None -SENSOR_TYPES: dict[str, LidarrSensorEntityDescription] = { +SENSOR_TYPES: dict[str, LidarrSensorEntityDescription[Any]] = { "disk_space": LidarrSensorEntityDescription( key="disk_space", name="Disk space", @@ -78,7 +74,7 @@ SENSOR_TYPES: dict[str, LidarrSensorEntityDescription] = { state_class=SensorStateClass.TOTAL, description_fn=get_modified_description, ), - "queue": LidarrSensorEntityDescription( + "queue": LidarrSensorEntityDescription[LidarrQueue]( key="queue", name="Queue", native_unit_of_measurement="Albums", @@ -87,7 +83,7 @@ SENSOR_TYPES: dict[str, LidarrSensorEntityDescription] = { state_class=SensorStateClass.TOTAL, attributes_fn=lambda data: {i.title: queue_str(i) for i in data.records}, ), - "wanted": LidarrSensorEntityDescription( + "wanted": LidarrSensorEntityDescription[LidarrQueue]( key="wanted", name="Wanted", native_unit_of_measurement="Albums", @@ -108,10 +104,10 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up Lidarr sensors based on a config entry.""" - coordinators: dict[str, LidarrDataUpdateCoordinator] = hass.data[DOMAIN][ + coordinators: dict[str, LidarrDataUpdateCoordinator[Any]] = hass.data[DOMAIN][ entry.entry_id ] - entities = [] + entities: list[LidarrSensor[Any]] = [] for coordinator_type, description in SENSOR_TYPES.items(): coordinator = coordinators[coordinator_type] if coordinator_type != "disk_space": @@ -125,15 +121,15 @@ async def async_setup_entry( async_add_entities(entities) -class LidarrSensor(LidarrEntity, SensorEntity): +class LidarrSensor(LidarrEntity[T], SensorEntity): """Implementation of the Lidarr sensor.""" - entity_description: LidarrSensorEntityDescription + entity_description: LidarrSensorEntityDescription[T] def __init__( self, - coordinator: LidarrDataUpdateCoordinator, - description: LidarrSensorEntityDescription, + coordinator: LidarrDataUpdateCoordinator[T], + description: LidarrSensorEntityDescription[T], folder_name: str = "", ) -> None: """Create Lidarr entity.""" @@ -141,12 +137,12 @@ class LidarrSensor(LidarrEntity, SensorEntity): self.folder_name = folder_name @property - def extra_state_attributes(self) -> dict[str, StateType | datetime] | None: + def extra_state_attributes(self) -> dict[str, str] | None: """Return the state attributes of the sensor.""" return self.entity_description.attributes_fn(self.coordinator.data) @property - def native_value(self) -> StateType: + def native_value(self) -> str | int: """Return the state of the sensor.""" return self.entity_description.value_fn(self.coordinator.data, self.folder_name) diff --git a/mypy.ini b/mypy.ini index 31a5268abcf..c83f6cd048e 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1372,6 +1372,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.lidarr.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.lifx.*] check_untyped_defs = true disallow_incomplete_defs = true