From d004adf8335f0d6118fd80e250119e24bd6aa254 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 14 Jul 2022 22:04:19 +0200 Subject: [PATCH] Add entity descriptions in AdGuard Home sensors (#75179) --- homeassistant/components/adguard/__init__.py | 8 +- homeassistant/components/adguard/sensor.py | 286 +++++++------------ 2 files changed, 102 insertions(+), 192 deletions(-) diff --git a/homeassistant/components/adguard/__init__.py b/homeassistant/components/adguard/__init__.py index 0c43f84bdfc..e27aef6389d 100644 --- a/homeassistant/components/adguard/__init__.py +++ b/homeassistant/components/adguard/__init__.py @@ -138,8 +138,8 @@ class AdGuardHomeEntity(Entity): self, adguard: AdGuardHome, entry: ConfigEntry, - name: str, - icon: str, + name: str | None, + icon: str | None, enabled_default: bool = True, ) -> None: """Initialize the AdGuard Home entity.""" @@ -151,12 +151,12 @@ class AdGuardHomeEntity(Entity): self.adguard = adguard @property - def name(self) -> str: + def name(self) -> str | None: """Return the name of the entity.""" return self._name @property - def icon(self) -> str: + def icon(self) -> str | None: """Return the mdi icon of the entity.""" return self._icon diff --git a/homeassistant/components/adguard/sensor.py b/homeassistant/components/adguard/sensor.py index 2d3226aba59..19ed0b96801 100644 --- a/homeassistant/components/adguard/sensor.py +++ b/homeassistant/components/adguard/sensor.py @@ -1,11 +1,14 @@ """Support for AdGuard Home sensors.""" from __future__ import annotations +from collections.abc import Callable, Coroutine +from dataclasses import dataclass from datetime import timedelta +from typing import Any from adguardhome import AdGuardHome, AdGuardHomeConnectionError -from homeassistant.components.sensor import SensorEntity +from homeassistant.components.sensor import SensorEntity, SensorEntityDescription from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE, TIME_MILLISECONDS from homeassistant.core import HomeAssistant @@ -19,6 +22,81 @@ SCAN_INTERVAL = timedelta(seconds=300) PARALLEL_UPDATES = 4 +@dataclass +class AdGuardHomeEntityDescriptionMixin: + """Mixin for required keys.""" + + value_fn: Callable[[AdGuardHome], Callable[[], Coroutine[Any, Any, int | float]]] + + +@dataclass +class AdGuardHomeEntityDescription( + SensorEntityDescription, AdGuardHomeEntityDescriptionMixin +): + """Describes AdGuard Home sensor entity.""" + + +SENSORS: tuple[AdGuardHomeEntityDescription, ...] = ( + AdGuardHomeEntityDescription( + key="dns_queries", + name="DNS queries", + icon="mdi:magnify", + native_unit_of_measurement="queries", + value_fn=lambda adguard: adguard.stats.dns_queries, + ), + AdGuardHomeEntityDescription( + key="blocked_filtering", + name="DNS queries blocked", + icon="mdi:magnify-close", + native_unit_of_measurement="queries", + value_fn=lambda adguard: adguard.stats.blocked_filtering, + ), + AdGuardHomeEntityDescription( + key="blocked_percentage", + name="DNS queries blocked ratio", + icon="mdi:magnify-close", + native_unit_of_measurement=PERCENTAGE, + value_fn=lambda adguard: adguard.stats.blocked_percentage, + ), + AdGuardHomeEntityDescription( + key="blocked_parental", + name="Parental control blocked", + icon="mdi:human-male-girl", + native_unit_of_measurement="requests", + value_fn=lambda adguard: adguard.stats.replaced_parental, + ), + AdGuardHomeEntityDescription( + key="blocked_safebrowsing", + name="Safe browsing blocked", + icon="mdi:shield-half-full", + native_unit_of_measurement="requests", + value_fn=lambda adguard: adguard.stats.replaced_safebrowsing, + ), + AdGuardHomeEntityDescription( + key="enforced_safesearch", + name="Safe searches enforced", + icon="mdi:shield-search", + native_unit_of_measurement="requests", + value_fn=lambda adguard: adguard.stats.replaced_safesearch, + ), + AdGuardHomeEntityDescription( + key="average_speed", + name="Average processing speed", + icon="mdi:speedometer", + native_unit_of_measurement=TIME_MILLISECONDS, + value_fn=lambda adguard: adguard.stats.avg_processing_time, + ), + AdGuardHomeEntityDescription( + key="rules_count", + name="Rules count", + icon="mdi:counter", + native_unit_of_measurement="rules", + value_fn=lambda adguard: adguard.stats.avg_processing_time, + entity_registry_enabled_default=False, + ), +) + + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, @@ -34,215 +112,47 @@ async def async_setup_entry( hass.data[DOMAIN][entry.entry_id][DATA_ADGUARD_VERSION] = version - sensors = [ - AdGuardHomeDNSQueriesSensor(adguard, entry), - AdGuardHomeBlockedFilteringSensor(adguard, entry), - AdGuardHomePercentageBlockedSensor(adguard, entry), - AdGuardHomeReplacedParentalSensor(adguard, entry), - AdGuardHomeReplacedSafeBrowsingSensor(adguard, entry), - AdGuardHomeReplacedSafeSearchSensor(adguard, entry), - AdGuardHomeAverageProcessingTimeSensor(adguard, entry), - AdGuardHomeRulesCountSensor(adguard, entry), - ] - - async_add_entities(sensors, True) + async_add_entities( + [AdGuardHomeSensor(adguard, entry, description) for description in SENSORS], + True, + ) class AdGuardHomeSensor(AdGuardHomeDeviceEntity, SensorEntity): """Defines a AdGuard Home sensor.""" + entity_description: AdGuardHomeEntityDescription + def __init__( self, adguard: AdGuardHome, entry: ConfigEntry, - name: str, - icon: str, - measurement: str, - unit_of_measurement: str, - enabled_default: bool = True, + description: AdGuardHomeEntityDescription, ) -> None: """Initialize AdGuard Home sensor.""" - self._state: int | str | None = None - self._unit_of_measurement = unit_of_measurement - self.measurement = measurement + self.entity_description = description - super().__init__(adguard, entry, name, icon, enabled_default) - - @property - def unique_id(self) -> str: - """Return the unique ID for this sensor.""" - return "_".join( + self._attr_unique_id = "_".join( [ DOMAIN, - self.adguard.host, - str(self.adguard.port), + adguard.host, + str(adguard.port), "sensor", - self.measurement, + description.key, ] ) - @property - def native_value(self) -> int | str | None: - """Return the state of the sensor.""" - return self._state - - @property - def native_unit_of_measurement(self) -> str | None: - """Return the unit this state is expressed in.""" - return self._unit_of_measurement - - -class AdGuardHomeDNSQueriesSensor(AdGuardHomeSensor): - """Defines a AdGuard Home DNS Queries sensor.""" - - def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None: - """Initialize AdGuard Home sensor.""" super().__init__( adguard, entry, - "DNS queries", - "mdi:magnify", - "dns_queries", - "queries", + description.name, + description.icon, + description.entity_registry_enabled_default, ) async def _adguard_update(self) -> None: """Update AdGuard Home entity.""" - self._state = await self.adguard.stats.dns_queries() - - -class AdGuardHomeBlockedFilteringSensor(AdGuardHomeSensor): - """Defines a AdGuard Home blocked by filtering sensor.""" - - def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None: - """Initialize AdGuard Home sensor.""" - super().__init__( - adguard, - entry, - "DNS queries blocked", - "mdi:magnify-close", - "blocked_filtering", - "queries", - enabled_default=False, - ) - - async def _adguard_update(self) -> None: - """Update AdGuard Home entity.""" - self._state = await self.adguard.stats.blocked_filtering() - - -class AdGuardHomePercentageBlockedSensor(AdGuardHomeSensor): - """Defines a AdGuard Home blocked percentage sensor.""" - - def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None: - """Initialize AdGuard Home sensor.""" - super().__init__( - adguard, - entry, - "DNS queries blocked ratio", - "mdi:magnify-close", - "blocked_percentage", - PERCENTAGE, - ) - - async def _adguard_update(self) -> None: - """Update AdGuard Home entity.""" - percentage = await self.adguard.stats.blocked_percentage() - self._state = f"{percentage:.2f}" - - -class AdGuardHomeReplacedParentalSensor(AdGuardHomeSensor): - """Defines a AdGuard Home replaced by parental control sensor.""" - - def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None: - """Initialize AdGuard Home sensor.""" - super().__init__( - adguard, - entry, - "Parental control blocked", - "mdi:human-male-girl", - "blocked_parental", - "requests", - ) - - async def _adguard_update(self) -> None: - """Update AdGuard Home entity.""" - self._state = await self.adguard.stats.replaced_parental() - - -class AdGuardHomeReplacedSafeBrowsingSensor(AdGuardHomeSensor): - """Defines a AdGuard Home replaced by safe browsing sensor.""" - - def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None: - """Initialize AdGuard Home sensor.""" - super().__init__( - adguard, - entry, - "Safe browsing blocked", - "mdi:shield-half-full", - "blocked_safebrowsing", - "requests", - ) - - async def _adguard_update(self) -> None: - """Update AdGuard Home entity.""" - self._state = await self.adguard.stats.replaced_safebrowsing() - - -class AdGuardHomeReplacedSafeSearchSensor(AdGuardHomeSensor): - """Defines a AdGuard Home replaced by safe search sensor.""" - - def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None: - """Initialize AdGuard Home sensor.""" - super().__init__( - adguard, - entry, - "Safe searches enforced", - "mdi:shield-search", - "enforced_safesearch", - "requests", - ) - - async def _adguard_update(self) -> None: - """Update AdGuard Home entity.""" - self._state = await self.adguard.stats.replaced_safesearch() - - -class AdGuardHomeAverageProcessingTimeSensor(AdGuardHomeSensor): - """Defines a AdGuard Home average processing time sensor.""" - - def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None: - """Initialize AdGuard Home sensor.""" - super().__init__( - adguard, - entry, - "Average processing speed", - "mdi:speedometer", - "average_speed", - TIME_MILLISECONDS, - ) - - async def _adguard_update(self) -> None: - """Update AdGuard Home entity.""" - average = await self.adguard.stats.avg_processing_time() - self._state = f"{average:.2f}" - - -class AdGuardHomeRulesCountSensor(AdGuardHomeSensor): - """Defines a AdGuard Home rules count sensor.""" - - def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None: - """Initialize AdGuard Home sensor.""" - super().__init__( - adguard, - entry, - "Rules count", - "mdi:counter", - "rules_count", - "rules", - enabled_default=False, - ) - - async def _adguard_update(self) -> None: - """Update AdGuard Home entity.""" - self._state = await self.adguard.filtering.rules_count(allowlist=False) + value = await self.entity_description.value_fn(self.adguard)() + self._attr_native_value = value + if isinstance(value, float): + self._attr_native_value = f"{value:.2f}"