From 7eb9875a9e11dd45f0fe20f0a54c268340154f77 Mon Sep 17 00:00:00 2001
From: "Mr. Bubbles" <manni@zapto.de>
Date: Wed, 26 Jun 2024 14:45:04 +0200
Subject: [PATCH] Add Base class for entities in PyLoad integration (#120563)

* Add Base class for entities

* Remove constructors
---
 homeassistant/components/pyload/button.py | 28 +----------
 homeassistant/components/pyload/entity.py | 37 ++++++++++++++
 homeassistant/components/pyload/sensor.py | 59 ++++++++++-------------
 homeassistant/components/pyload/switch.py | 28 +----------
 4 files changed, 66 insertions(+), 86 deletions(-)
 create mode 100644 homeassistant/components/pyload/entity.py

diff --git a/homeassistant/components/pyload/button.py b/homeassistant/components/pyload/button.py
index 0d8a232142a..950177f8751 100644
--- a/homeassistant/components/pyload/button.py
+++ b/homeassistant/components/pyload/button.py
@@ -11,13 +11,10 @@ from pyloadapi.api import PyLoadAPI
 
 from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
 from homeassistant.core import HomeAssistant
-from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
 from homeassistant.helpers.entity_platform import AddEntitiesCallback
-from homeassistant.helpers.update_coordinator import CoordinatorEntity
 
 from . import PyLoadConfigEntry
-from .const import DOMAIN, MANUFACTURER, SERVICE_NAME
-from .coordinator import PyLoadCoordinator
+from .entity import BasePyLoadEntity
 
 
 @dataclass(kw_only=True, frozen=True)
@@ -76,32 +73,11 @@ async def async_setup_entry(
     )
 
 
-class PyLoadBinarySensor(CoordinatorEntity[PyLoadCoordinator], ButtonEntity):
+class PyLoadBinarySensor(BasePyLoadEntity, ButtonEntity):
     """Representation of a pyLoad button."""
 
-    _attr_has_entity_name = True
     entity_description: PyLoadButtonEntityDescription
 
-    def __init__(
-        self,
-        coordinator: PyLoadCoordinator,
-        entity_description: PyLoadButtonEntityDescription,
-    ) -> None:
-        """Initialize the button."""
-        super().__init__(coordinator)
-        self._attr_unique_id = (
-            f"{coordinator.config_entry.entry_id}_{entity_description.key}"
-        )
-        self.entity_description = entity_description
-        self._attr_device_info = DeviceInfo(
-            entry_type=DeviceEntryType.SERVICE,
-            manufacturer=MANUFACTURER,
-            model=SERVICE_NAME,
-            configuration_url=coordinator.pyload.api_url,
-            identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
-            sw_version=coordinator.version,
-        )
-
     async def async_press(self) -> None:
         """Handle the button press."""
         await self.entity_description.press_fn(self.coordinator.pyload)
diff --git a/homeassistant/components/pyload/entity.py b/homeassistant/components/pyload/entity.py
new file mode 100644
index 00000000000..58e93431ca1
--- /dev/null
+++ b/homeassistant/components/pyload/entity.py
@@ -0,0 +1,37 @@
+"""Base entity for pyLoad."""
+
+from __future__ import annotations
+
+from homeassistant.components.button import EntityDescription
+from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
+from homeassistant.helpers.update_coordinator import CoordinatorEntity
+
+from .const import DOMAIN, MANUFACTURER, SERVICE_NAME
+from .coordinator import PyLoadCoordinator
+
+
+class BasePyLoadEntity(CoordinatorEntity[PyLoadCoordinator]):
+    """BaseEntity for pyLoad."""
+
+    _attr_has_entity_name = True
+
+    def __init__(
+        self,
+        coordinator: PyLoadCoordinator,
+        entity_description: EntityDescription,
+    ) -> None:
+        """Initialize the Entity."""
+        super().__init__(coordinator)
+
+        self._attr_unique_id = (
+            f"{coordinator.config_entry.entry_id}_{entity_description.key}"
+        )
+        self.entity_description = entity_description
+        self._attr_device_info = DeviceInfo(
+            entry_type=DeviceEntryType.SERVICE,
+            manufacturer=MANUFACTURER,
+            model=SERVICE_NAME,
+            configuration_url=coordinator.pyload.api_url,
+            identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
+            sw_version=coordinator.version,
+        )
diff --git a/homeassistant/components/pyload/sensor.py b/homeassistant/components/pyload/sensor.py
index bc90fdb7ccb..4a0502707b6 100644
--- a/homeassistant/components/pyload/sensor.py
+++ b/homeassistant/components/pyload/sensor.py
@@ -2,6 +2,8 @@
 
 from __future__ import annotations
 
+from collections.abc import Callable
+from dataclasses import dataclass
 from enum import StrEnum
 
 import voluptuous as vol
@@ -28,11 +30,9 @@ from homeassistant.const import (
 from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
 from homeassistant.data_entry_flow import FlowResultType
 import homeassistant.helpers.config_validation as cv
-from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
 from homeassistant.helpers.entity_platform import AddEntitiesCallback
 from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
-from homeassistant.helpers.update_coordinator import CoordinatorEntity
 
 from . import PyLoadConfigEntry
 from .const import (
@@ -41,11 +41,10 @@ from .const import (
     DEFAULT_PORT,
     DOMAIN,
     ISSUE_PLACEHOLDER,
-    MANUFACTURER,
-    SERVICE_NAME,
     UNIT_DOWNLOADS,
 )
-from .coordinator import PyLoadCoordinator
+from .coordinator import pyLoadData
+from .entity import BasePyLoadEntity
 
 
 class PyLoadSensorEntity(StrEnum):
@@ -58,40 +57,52 @@ class PyLoadSensorEntity(StrEnum):
     TOTAL = "total"
 
 
-SENSOR_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (
-    SensorEntityDescription(
+@dataclass(kw_only=True, frozen=True)
+class PyLoadSensorEntityDescription(SensorEntityDescription):
+    """Describes pyLoad switch entity."""
+
+    value_fn: Callable[[pyLoadData], StateType]
+
+
+SENSOR_DESCRIPTIONS: tuple[PyLoadSensorEntityDescription, ...] = (
+    PyLoadSensorEntityDescription(
         key=PyLoadSensorEntity.SPEED,
         translation_key=PyLoadSensorEntity.SPEED,
         device_class=SensorDeviceClass.DATA_RATE,
         native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
         suggested_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND,
         suggested_display_precision=1,
+        value_fn=lambda data: data.speed,
     ),
-    SensorEntityDescription(
+    PyLoadSensorEntityDescription(
         key=PyLoadSensorEntity.ACTIVE,
         translation_key=PyLoadSensorEntity.ACTIVE,
         native_unit_of_measurement=UNIT_DOWNLOADS,
         state_class=SensorStateClass.MEASUREMENT,
+        value_fn=lambda data: data.active,
     ),
-    SensorEntityDescription(
+    PyLoadSensorEntityDescription(
         key=PyLoadSensorEntity.QUEUE,
         translation_key=PyLoadSensorEntity.QUEUE,
         native_unit_of_measurement=UNIT_DOWNLOADS,
         state_class=SensorStateClass.MEASUREMENT,
+        value_fn=lambda data: data.queue,
     ),
-    SensorEntityDescription(
+    PyLoadSensorEntityDescription(
         key=PyLoadSensorEntity.TOTAL,
         translation_key=PyLoadSensorEntity.TOTAL,
         native_unit_of_measurement=UNIT_DOWNLOADS,
         state_class=SensorStateClass.MEASUREMENT,
+        value_fn=lambda data: data.total,
     ),
-    SensorEntityDescription(
+    PyLoadSensorEntityDescription(
         key=PyLoadSensorEntity.FREE_SPACE,
         translation_key=PyLoadSensorEntity.FREE_SPACE,
         device_class=SensorDeviceClass.DATA_SIZE,
         native_unit_of_measurement=UnitOfInformation.BYTES,
         suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
         suggested_display_precision=1,
+        value_fn=lambda data: data.free_space,
     ),
 )
 
@@ -173,32 +184,12 @@ async def async_setup_entry(
     )
 
 
-class PyLoadSensor(CoordinatorEntity[PyLoadCoordinator], SensorEntity):
+class PyLoadSensor(BasePyLoadEntity, SensorEntity):
     """Representation of a pyLoad sensor."""
 
-    _attr_has_entity_name = True
-
-    def __init__(
-        self,
-        coordinator: PyLoadCoordinator,
-        entity_description: SensorEntityDescription,
-    ) -> None:
-        """Initialize a new pyLoad sensor."""
-        super().__init__(coordinator)
-        self._attr_unique_id = (
-            f"{coordinator.config_entry.entry_id}_{entity_description.key}"
-        )
-        self.entity_description = entity_description
-        self._attr_device_info = DeviceInfo(
-            entry_type=DeviceEntryType.SERVICE,
-            manufacturer=MANUFACTURER,
-            model=SERVICE_NAME,
-            configuration_url=coordinator.pyload.api_url,
-            identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
-            sw_version=coordinator.version,
-        )
+    entity_description: PyLoadSensorEntityDescription
 
     @property
     def native_value(self) -> StateType:
         """Return the state of the sensor."""
-        return getattr(self.coordinator.data, self.entity_description.key)
+        return self.entity_description.value_fn(self.coordinator.data)
diff --git a/homeassistant/components/pyload/switch.py b/homeassistant/components/pyload/switch.py
index b0628005008..4ed3e925488 100644
--- a/homeassistant/components/pyload/switch.py
+++ b/homeassistant/components/pyload/switch.py
@@ -15,13 +15,10 @@ from homeassistant.components.switch import (
     SwitchEntityDescription,
 )
 from homeassistant.core import HomeAssistant
-from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
 from homeassistant.helpers.entity_platform import AddEntitiesCallback
-from homeassistant.helpers.update_coordinator import CoordinatorEntity
 
 from . import PyLoadConfigEntry
-from .const import DOMAIN, MANUFACTURER, SERVICE_NAME
-from .coordinator import PyLoadCoordinator
+from .entity import BasePyLoadEntity
 
 
 class PyLoadSwitch(StrEnum):
@@ -75,32 +72,11 @@ async def async_setup_entry(
     )
 
 
-class PyLoadSwitchEntity(CoordinatorEntity[PyLoadCoordinator], SwitchEntity):
+class PyLoadSwitchEntity(BasePyLoadEntity, SwitchEntity):
     """Representation of a pyLoad sensor."""
 
-    _attr_has_entity_name = True
     entity_description: PyLoadSwitchEntityDescription
 
-    def __init__(
-        self,
-        coordinator: PyLoadCoordinator,
-        entity_description: PyLoadSwitchEntityDescription,
-    ) -> None:
-        """Initialize the sensor."""
-        super().__init__(coordinator)
-        self._attr_unique_id = (
-            f"{coordinator.config_entry.entry_id}_{entity_description.key}"
-        )
-        self.entity_description = entity_description
-        self._attr_device_info = DeviceInfo(
-            entry_type=DeviceEntryType.SERVICE,
-            manufacturer=MANUFACTURER,
-            model=SERVICE_NAME,
-            configuration_url=coordinator.pyload.api_url,
-            identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
-            sw_version=coordinator.version,
-        )
-
     @property
     def is_on(self) -> bool | None:
         """Return the state of the device."""
-- 
GitLab