From a3b2b5c328a5e17929b2bcd54c9ed3c5acb44a36 Mon Sep 17 00:00:00 2001
From: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Date: Wed, 20 Jul 2022 12:03:30 +0200
Subject: [PATCH] Add zha typing [classmethods] (#75472)

---
 homeassistant/components/zha/button.py      | 14 +++++----
 homeassistant/components/zha/core/device.py |  8 +++--
 homeassistant/components/zha/entity.py      | 29 ++++++++++++-----
 homeassistant/components/zha/number.py      | 14 ++++++---
 homeassistant/components/zha/select.py      | 16 ++++++----
 homeassistant/components/zha/sensor.py      | 35 ++++++++++++---------
 homeassistant/components/zha/switch.py      | 10 ++++--
 7 files changed, 81 insertions(+), 45 deletions(-)

diff --git a/homeassistant/components/zha/button.py b/homeassistant/components/zha/button.py
index 0f98bfaad51..fcc040cbde2 100644
--- a/homeassistant/components/zha/button.py
+++ b/homeassistant/components/zha/button.py
@@ -4,7 +4,7 @@ from __future__ import annotations
 import abc
 import functools
 import logging
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, TypeVar
 
 import zigpy.exceptions
 from zigpy.zcl.foundation import Status
@@ -27,6 +27,8 @@ if TYPE_CHECKING:
     from .core.device import ZHADevice
 
 
+_ZHAIdentifyButtonSelfT = TypeVar("_ZHAIdentifyButtonSelfT", bound="ZHAIdentifyButton")
+
 MULTI_MATCH = functools.partial(ZHA_ENTITIES.multipass_match, Platform.BUTTON)
 CONFIG_DIAGNOSTIC_MATCH = functools.partial(
     ZHA_ENTITIES.config_diagnostic_match, Platform.BUTTON
@@ -66,7 +68,7 @@ class ZHAButton(ZhaEntity, ButtonEntity):
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
+        **kwargs: Any,
     ) -> None:
         """Init this button."""
         super().__init__(unique_id, zha_device, channels, **kwargs)
@@ -89,12 +91,12 @@ class ZHAIdentifyButton(ZHAButton):
 
     @classmethod
     def create_entity(
-        cls,
+        cls: type[_ZHAIdentifyButtonSelfT],
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
-    ) -> ZhaEntity | None:
+        **kwargs: Any,
+    ) -> _ZHAIdentifyButtonSelfT | None:
         """Entity Factory.
 
         Return entity if it is a supported configuration, otherwise return None
@@ -126,7 +128,7 @@ class ZHAAttributeButton(ZhaEntity, ButtonEntity):
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
+        **kwargs: Any,
     ) -> None:
         """Init this button."""
         super().__init__(unique_id, zha_device, channels, **kwargs)
diff --git a/homeassistant/components/zha/core/device.py b/homeassistant/components/zha/core/device.py
index 4719b6bf585..150241a091b 100644
--- a/homeassistant/components/zha/core/device.py
+++ b/homeassistant/components/zha/core/device.py
@@ -9,7 +9,7 @@ from functools import cached_property
 import logging
 import random
 import time
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, TypeVar
 
 from zigpy import types
 import zigpy.device
@@ -86,6 +86,8 @@ _LOGGER = logging.getLogger(__name__)
 _UPDATE_ALIVE_INTERVAL = (60, 90)
 _CHECKIN_GRACE_PERIODS = 2
 
+_ZHADeviceSelfT = TypeVar("_ZHADeviceSelfT", bound="ZHADevice")
+
 
 class DeviceStatus(Enum):
     """Status of a device."""
@@ -340,12 +342,12 @@ class ZHADevice(LogMixin):
 
     @classmethod
     def new(
-        cls,
+        cls: type[_ZHADeviceSelfT],
         hass: HomeAssistant,
         zigpy_dev: zigpy.device.Device,
         gateway: ZHAGateway,
         restored: bool = False,
-    ):
+    ) -> _ZHADeviceSelfT:
         """Create new device."""
         zha_dev = cls(hass, zigpy_dev, gateway)
         zha_dev.channels = channels.Channels.new(zha_dev)
diff --git a/homeassistant/components/zha/entity.py b/homeassistant/components/zha/entity.py
index 8b3627df9de..2f609555c79 100644
--- a/homeassistant/components/zha/entity.py
+++ b/homeassistant/components/zha/entity.py
@@ -5,7 +5,7 @@ import asyncio
 from collections.abc import Callable
 import functools
 import logging
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, TypeVar
 
 from homeassistant.const import ATTR_NAME
 from homeassistant.core import CALLBACK_TYPE, Event, callback
@@ -35,6 +35,9 @@ if TYPE_CHECKING:
     from .core.channels.base import ZigbeeChannel
     from .core.device import ZHADevice
 
+_ZhaEntitySelfT = TypeVar("_ZhaEntitySelfT", bound="ZhaEntity")
+_ZhaGroupEntitySelfT = TypeVar("_ZhaGroupEntitySelfT", bound="ZhaGroupEntity")
+
 _LOGGER = logging.getLogger(__name__)
 
 ENTITY_SUFFIX = "entity_suffix"
@@ -155,7 +158,7 @@ class BaseZhaEntity(LogMixin, entity.Entity):
 class ZhaEntity(BaseZhaEntity, RestoreEntity):
     """A base class for non group ZHA entities."""
 
-    def __init_subclass__(cls, id_suffix: str | None = None, **kwargs) -> None:
+    def __init_subclass__(cls, id_suffix: str | None = None, **kwargs: Any) -> None:
         """Initialize subclass.
 
         :param id_suffix: suffix to add to the unique_id of the entity. Used for multi
@@ -187,12 +190,12 @@ class ZhaEntity(BaseZhaEntity, RestoreEntity):
 
     @classmethod
     def create_entity(
-        cls,
+        cls: type[_ZhaEntitySelfT],
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
-    ) -> ZhaEntity | None:
+        **kwargs: Any,
+    ) -> _ZhaEntitySelfT | None:
         """Entity Factory.
 
         Return entity if it is a supported configuration, otherwise return None
@@ -257,7 +260,12 @@ class ZhaGroupEntity(BaseZhaEntity):
     """A base class for ZHA group entities."""
 
     def __init__(
-        self, entity_ids: list[str], unique_id: str, group_id: int, zha_device, **kwargs
+        self,
+        entity_ids: list[str],
+        unique_id: str,
+        group_id: int,
+        zha_device: ZHADevice,
+        **kwargs: Any,
     ) -> None:
         """Initialize a light group."""
         super().__init__(unique_id, zha_device, **kwargs)
@@ -279,8 +287,13 @@ class ZhaGroupEntity(BaseZhaEntity):
 
     @classmethod
     def create_entity(
-        cls, entity_ids: list[str], unique_id: str, group_id: int, zha_device, **kwargs
-    ) -> ZhaGroupEntity | None:
+        cls: type[_ZhaGroupEntitySelfT],
+        entity_ids: list[str],
+        unique_id: str,
+        group_id: int,
+        zha_device: ZHADevice,
+        **kwargs: Any,
+    ) -> _ZhaGroupEntitySelfT | None:
         """Group Entity Factory.
 
         Return entity if it is a supported configuration, otherwise return None
diff --git a/homeassistant/components/zha/number.py b/homeassistant/components/zha/number.py
index 76b1121c2f0..4252bf0e14c 100644
--- a/homeassistant/components/zha/number.py
+++ b/homeassistant/components/zha/number.py
@@ -3,7 +3,7 @@ from __future__ import annotations
 
 import functools
 import logging
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Any, TypeVar
 
 import zigpy.exceptions
 from zigpy.zcl.foundation import Status
@@ -33,6 +33,10 @@ if TYPE_CHECKING:
 
 _LOGGER = logging.getLogger(__name__)
 
+_ZHANumberConfigurationEntitySelfT = TypeVar(
+    "_ZHANumberConfigurationEntitySelfT", bound="ZHANumberConfigurationEntity"
+)
+
 STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, Platform.NUMBER)
 CONFIG_DIAGNOSTIC_MATCH = functools.partial(
     ZHA_ENTITIES.config_diagnostic_match, Platform.NUMBER
@@ -368,12 +372,12 @@ class ZHANumberConfigurationEntity(ZhaEntity, NumberEntity):
 
     @classmethod
     def create_entity(
-        cls,
+        cls: type[_ZHANumberConfigurationEntitySelfT],
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
-    ) -> ZhaEntity | None:
+        **kwargs: Any,
+    ) -> _ZHANumberConfigurationEntitySelfT | None:
         """Entity Factory.
 
         Return entity if it is a supported configuration, otherwise return None
@@ -397,7 +401,7 @@ class ZHANumberConfigurationEntity(ZhaEntity, NumberEntity):
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
+        **kwargs: Any,
     ) -> None:
         """Init this number configuration entity."""
         self._channel: ZigbeeChannel = channels[0]
diff --git a/homeassistant/components/zha/select.py b/homeassistant/components/zha/select.py
index e2835d4acd4..503c5a013a8 100644
--- a/homeassistant/components/zha/select.py
+++ b/homeassistant/components/zha/select.py
@@ -4,7 +4,7 @@ from __future__ import annotations
 from enum import Enum
 import functools
 import logging
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Any, TypeVar
 
 from zigpy import types
 from zigpy.zcl.clusters.general import OnOff
@@ -34,6 +34,10 @@ if TYPE_CHECKING:
     from .core.device import ZHADevice
 
 
+_ZCLEnumSelectEntitySelfT = TypeVar(
+    "_ZCLEnumSelectEntitySelfT", bound="ZCLEnumSelectEntity"
+)
+
 CONFIG_DIAGNOSTIC_MATCH = functools.partial(
     ZHA_ENTITIES.config_diagnostic_match, Platform.SELECT
 )
@@ -72,7 +76,7 @@ class ZHAEnumSelectEntity(ZhaEntity, SelectEntity):
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
+        **kwargs: Any,
     ) -> None:
         """Init this select entity."""
         self._attr_name = self._enum.__name__
@@ -154,12 +158,12 @@ class ZCLEnumSelectEntity(ZhaEntity, SelectEntity):
 
     @classmethod
     def create_entity(
-        cls,
+        cls: type[_ZCLEnumSelectEntitySelfT],
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
-    ) -> ZhaEntity | None:
+        **kwargs: Any,
+    ) -> _ZCLEnumSelectEntitySelfT | None:
         """Entity Factory.
 
         Return entity if it is a supported configuration, otherwise return None
@@ -183,7 +187,7 @@ class ZCLEnumSelectEntity(ZhaEntity, SelectEntity):
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
+        **kwargs: Any,
     ) -> None:
         """Init this select entity."""
         self._attr_options = [entry.name.replace("_", " ") for entry in self._enum]
diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py
index 513ba5510b5..f42e88041ef 100644
--- a/homeassistant/components/zha/sensor.py
+++ b/homeassistant/components/zha/sensor.py
@@ -3,7 +3,7 @@ from __future__ import annotations
 
 import functools
 import numbers
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, TypeVar
 
 from homeassistant.components.climate.const import HVACAction
 from homeassistant.components.sensor import (
@@ -69,6 +69,13 @@ if TYPE_CHECKING:
     from .core.channels.base import ZigbeeChannel
     from .core.device import ZHADevice
 
+_SensorSelfT = TypeVar("_SensorSelfT", bound="Sensor")
+_BatterySelfT = TypeVar("_BatterySelfT", bound="Battery")
+_ThermostatHVACActionSelfT = TypeVar(
+    "_ThermostatHVACActionSelfT", bound="ThermostatHVACAction"
+)
+_RSSISensorSelfT = TypeVar("_RSSISensorSelfT", bound="RSSISensor")
+
 PARALLEL_UPDATES = 5
 
 BATTERY_SIZES = {
@@ -126,7 +133,7 @@ class Sensor(ZhaEntity, SensorEntity):
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
+        **kwargs: Any,
     ) -> None:
         """Init this sensor."""
         super().__init__(unique_id, zha_device, channels, **kwargs)
@@ -134,12 +141,12 @@ class Sensor(ZhaEntity, SensorEntity):
 
     @classmethod
     def create_entity(
-        cls,
+        cls: type[_SensorSelfT],
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
-    ) -> ZhaEntity | None:
+        **kwargs: Any,
+    ) -> _SensorSelfT | None:
         """Entity Factory.
 
         Return entity if it is a supported configuration, otherwise return None
@@ -214,12 +221,12 @@ class Battery(Sensor):
 
     @classmethod
     def create_entity(
-        cls,
+        cls: type[_BatterySelfT],
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
-    ) -> ZhaEntity | None:
+        **kwargs: Any,
+    ) -> _BatterySelfT | None:
         """Entity Factory.
 
         Unlike any other entity, PowerConfiguration cluster may not support
@@ -641,12 +648,12 @@ class ThermostatHVACAction(Sensor, id_suffix="hvac_action"):
 
     @classmethod
     def create_entity(
-        cls,
+        cls: type[_ThermostatHVACActionSelfT],
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
-    ) -> ZhaEntity | None:
+        **kwargs: Any,
+    ) -> _ThermostatHVACActionSelfT | None:
         """Entity Factory.
 
         Return entity if it is a supported configuration, otherwise return None
@@ -767,12 +774,12 @@ class RSSISensor(Sensor, id_suffix="rssi"):
 
     @classmethod
     def create_entity(
-        cls,
+        cls: type[_RSSISensorSelfT],
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
-        **kwargs,
-    ) -> ZhaEntity | None:
+        **kwargs: Any,
+    ) -> _RSSISensorSelfT | None:
         """Entity Factory.
 
         Return entity if it is a supported configuration, otherwise return None
diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py
index 3b044bb7646..881401f31da 100644
--- a/homeassistant/components/zha/switch.py
+++ b/homeassistant/components/zha/switch.py
@@ -3,7 +3,7 @@ from __future__ import annotations
 
 import functools
 import logging
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, TypeVar
 
 import zigpy.exceptions
 from zigpy.zcl.clusters.general import OnOff
@@ -31,6 +31,10 @@ if TYPE_CHECKING:
     from .core.channels.base import ZigbeeChannel
     from .core.device import ZHADevice
 
+_ZHASwitchConfigurationEntitySelfT = TypeVar(
+    "_ZHASwitchConfigurationEntitySelfT", bound="ZHASwitchConfigurationEntity"
+)
+
 STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, Platform.SWITCH)
 GROUP_MATCH = functools.partial(ZHA_ENTITIES.group_match, Platform.SWITCH)
 CONFIG_DIAGNOSTIC_MATCH = functools.partial(
@@ -172,12 +176,12 @@ class ZHASwitchConfigurationEntity(ZhaEntity, SwitchEntity):
 
     @classmethod
     def create_entity(
-        cls,
+        cls: type[_ZHASwitchConfigurationEntitySelfT],
         unique_id: str,
         zha_device: ZHADevice,
         channels: list[ZigbeeChannel],
         **kwargs: Any,
-    ) -> ZhaEntity | None:
+    ) -> _ZHASwitchConfigurationEntitySelfT | None:
         """Entity Factory.
 
         Return entity if it is a supported configuration, otherwise return None
-- 
GitLab