Improve type hints in blebox (#80511)

* Add generics to blebox

* Remove walrus

* Move logic into each platform

* Code style

* Apply suggestion
This commit is contained in:
epenet 2022-10-19 17:49:40 +02:00 committed by GitHub
parent 6ea6782d23
commit 374d46ec09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 79 additions and 62 deletions

View file

@ -1,5 +1,6 @@
"""The BleBox devices integration."""
import logging
from typing import Generic, TypeVar
from blebox_uniapi.box import Box
from blebox_uniapi.error import Error
@ -8,7 +9,7 @@ from blebox_uniapi.session import ApiHost
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity import DeviceInfo, Entity
@ -28,6 +29,8 @@ PLATFORMS = [
PARALLEL_UPDATES = 0
_FeatureT = TypeVar("_FeatureT", bound=Feature)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up BleBox devices from a config entry."""
@ -64,26 +67,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
@callback
def create_blebox_entities(
hass, config_entry, async_add_entities, entity_klass, entity_type
):
"""Create entities from a BleBox product's features."""
product = hass.data[DOMAIN][config_entry.entry_id][PRODUCT]
entities = []
if entity_type in product.features:
for feature in product.features[entity_type]:
entities.append(entity_klass(feature))
async_add_entities(entities, True)
class BleBoxEntity(Entity):
class BleBoxEntity(Entity, Generic[_FeatureT]):
"""Implements a common class for entities representing a BleBox feature."""
def __init__(self, feature: Feature) -> None:
def __init__(self, feature: _FeatureT) -> None:
"""Initialize a BleBox entity."""
self._feature = feature
self._attr_name = feature.full_name

View file

@ -1,12 +1,16 @@
"""BleBox button entities implementation."""
from __future__ import annotations
from blebox_uniapi.box import Box
import blebox_uniapi.button
from homeassistant.components.button import ButtonEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BleBoxEntity, create_blebox_entities
from . import BleBoxEntity
from .const import DOMAIN, PRODUCT
async def async_setup_entry(
@ -15,20 +19,23 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up a BleBox button entry."""
create_blebox_entities(
hass, config_entry, async_add_entities, BleBoxButtonEntity, "buttons"
)
product: Box = hass.data[DOMAIN][config_entry.entry_id][PRODUCT]
entities = [
BleBoxButtonEntity(feature) for feature in product.features.get("buttons", [])
]
async_add_entities(entities, True)
class BleBoxButtonEntity(BleBoxEntity, ButtonEntity):
class BleBoxButtonEntity(BleBoxEntity[blebox_uniapi.button.Button], ButtonEntity):
"""Representation of BleBox buttons."""
def __init__(self, feature):
def __init__(self, feature: blebox_uniapi.button.Button) -> None:
"""Initialize a BleBox button feature."""
super().__init__(feature)
self._attr_icon = self.get_icon()
def get_icon(self):
def get_icon(self) -> str | None:
"""Return icon for endpoint."""
if "up" in self._feature.query_string:
return "mdi:arrow-up-circle"
@ -40,7 +47,7 @@ class BleBoxButtonEntity(BleBoxEntity, ButtonEntity):
return "mdi:arrow-up-circle"
if "close" in self._feature.query_string:
return "mdi:arrow-down-circle"
return ""
return None
async def async_press(self) -> None:
"""Handle the button press."""

View file

@ -2,6 +2,9 @@
from datetime import timedelta
from typing import Any
from blebox_uniapi.box import Box
import blebox_uniapi.climate
from homeassistant.components.climate import (
ClimateEntity,
ClimateEntityFeature,
@ -13,7 +16,8 @@ from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BleBoxEntity, create_blebox_entities
from . import BleBoxEntity
from .const import DOMAIN, PRODUCT
SCAN_INTERVAL = timedelta(seconds=5)
@ -24,13 +28,15 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up a BleBox climate entity."""
product: Box = hass.data[DOMAIN][config_entry.entry_id][PRODUCT]
create_blebox_entities(
hass, config_entry, async_add_entities, BleBoxClimateEntity, "climates"
)
entities = [
BleBoxClimateEntity(feature) for feature in product.features.get("climates", [])
]
async_add_entities(entities, True)
class BleBoxClimateEntity(BleBoxEntity, ClimateEntity):
class BleBoxClimateEntity(BleBoxEntity[blebox_uniapi.climate.Climate], ClimateEntity):
"""Representation of a BleBox climate feature (saunaBox)."""
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE

View file

@ -3,6 +3,9 @@ from __future__ import annotations
from typing import Any
from blebox_uniapi.box import Box
import blebox_uniapi.cover
from homeassistant.components.cover import (
ATTR_POSITION,
CoverDeviceClass,
@ -14,7 +17,8 @@ from homeassistant.const import STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_O
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BleBoxEntity, create_blebox_entities
from . import BleBoxEntity
from .const import DOMAIN, PRODUCT
BLEBOX_TO_COVER_DEVICE_CLASSES = {
"gate": CoverDeviceClass.GATE,
@ -44,16 +48,17 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up a BleBox entry."""
create_blebox_entities(
hass, config_entry, async_add_entities, BleBoxCoverEntity, "covers"
)
product: Box = hass.data[DOMAIN][config_entry.entry_id][PRODUCT]
entities = [
BleBoxCoverEntity(feature) for feature in product.features.get("covers", [])
]
async_add_entities(entities, True)
class BleBoxCoverEntity(BleBoxEntity, CoverEntity):
class BleBoxCoverEntity(BleBoxEntity[blebox_uniapi.cover.Cover], CoverEntity):
"""Representation of a BleBox cover feature."""
def __init__(self, feature):
def __init__(self, feature: blebox_uniapi.cover.Cover) -> None:
"""Initialize a BleBox cover feature."""
super().__init__(feature)
self._attr_device_class = BLEBOX_TO_COVER_DEVICE_CLASSES[feature.device_class]

View file

@ -5,6 +5,7 @@ from datetime import timedelta
import logging
from typing import Any
from blebox_uniapi.box import Box
import blebox_uniapi.light
from blebox_uniapi.light import BleboxColorMode
@ -23,7 +24,8 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BleBoxEntity, create_blebox_entities
from . import BleBoxEntity
from .const import DOMAIN, PRODUCT
_LOGGER = logging.getLogger(__name__)
@ -36,10 +38,11 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up a BleBox entry."""
create_blebox_entities(
hass, config_entry, async_add_entities, BleBoxLightEntity, "lights"
)
product: Box = hass.data[DOMAIN][config_entry.entry_id][PRODUCT]
entities = [
BleBoxLightEntity(feature) for feature in product.features.get("lights", [])
]
async_add_entities(entities, True)
COLOR_MODE_MAP = {
@ -53,11 +56,9 @@ COLOR_MODE_MAP = {
}
class BleBoxLightEntity(BleBoxEntity, LightEntity):
class BleBoxLightEntity(BleBoxEntity[blebox_uniapi.light.Light], LightEntity):
"""Representation of BleBox lights."""
_feature: blebox_uniapi.light.Light
def __init__(self, feature: blebox_uniapi.light.Light) -> None:
"""Initialize a BleBox light."""
super().__init__(feature)

View file

@ -1,6 +1,9 @@
"""BleBox sensor entities."""
from dataclasses import dataclass
from blebox_uniapi.box import Box
import blebox_uniapi.sensor
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
@ -11,7 +14,8 @@ from homeassistant.const import CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, TEMP_C
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BleBoxEntity, create_blebox_entities
from . import BleBoxEntity
from .const import DOMAIN, PRODUCT
@dataclass
@ -49,16 +53,17 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up a BleBox entry."""
create_blebox_entities(
hass, config_entry, async_add_entities, BleBoxSensorEntity, "sensors"
)
product: Box = hass.data[DOMAIN][config_entry.entry_id][PRODUCT]
entities = [
BleBoxSensorEntity(feature) for feature in product.features.get("sensors", [])
]
async_add_entities(entities, True)
class BleBoxSensorEntity(BleBoxEntity, SensorEntity):
class BleBoxSensorEntity(BleBoxEntity[blebox_uniapi.sensor.BaseSensor], SensorEntity):
"""Representation of a BleBox sensor feature."""
def __init__(self, feature):
def __init__(self, feature: blebox_uniapi.sensor.BaseSensor) -> None:
"""Initialize a BleBox sensor feature."""
super().__init__(feature)

View file

@ -2,12 +2,16 @@
from datetime import timedelta
from typing import Any
from blebox_uniapi.box import Box
import blebox_uniapi.switch
from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BleBoxEntity, create_blebox_entities
from . import BleBoxEntity
from .const import DOMAIN, PRODUCT
SCAN_INTERVAL = timedelta(seconds=5)
@ -18,15 +22,17 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up a BleBox switch entity."""
create_blebox_entities(
hass, config_entry, async_add_entities, BleBoxSwitchEntity, "switches"
)
product: Box = hass.data[DOMAIN][config_entry.entry_id][PRODUCT]
entities = [
BleBoxSwitchEntity(feature) for feature in product.features.get("switches", [])
]
async_add_entities(entities, True)
class BleBoxSwitchEntity(BleBoxEntity, SwitchEntity):
class BleBoxSwitchEntity(BleBoxEntity[blebox_uniapi.switch.Switch], SwitchEntity):
"""Representation of a BleBox switch feature."""
def __init__(self, feature):
def __init__(self, feature: blebox_uniapi.switch.Switch) -> None:
"""Initialize a BleBox switch feature."""
super().__init__(feature)
self._attr_device_class = SwitchDeviceClass.SWITCH

View file

@ -7,7 +7,7 @@ import blebox_uniapi
from homeassistant.components.blebox.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from .conftest import mock_config, patch_product_identify
from .conftest import mock_config, patch_product_identify, setup_product_mock
async def test_setup_failure(hass, caplog):
@ -44,7 +44,7 @@ async def test_setup_failure_on_connection(hass, caplog):
async def test_unload_config_entry(hass):
"""Test that unloading works properly."""
patch_product_identify(None)
setup_product_mock("switches", [])
entry = mock_config()
entry.add_to_hass(hass)