From d60540d4f5ad09ed2f22478129b3be4ae646048a Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Mon, 20 Dec 2021 20:34:34 -0800 Subject: [PATCH] Cast types in wemo rather than converting (#62454) --- .../components/wemo/binary_sensor.py | 12 +++------ homeassistant/components/wemo/entity.py | 20 ++++++++++++--- homeassistant/components/wemo/fan.py | 13 ++-------- homeassistant/components/wemo/light.py | 25 +++++++------------ homeassistant/components/wemo/sensor.py | 3 ++- homeassistant/components/wemo/switch.py | 13 +++------- 6 files changed, 37 insertions(+), 49 deletions(-) diff --git a/homeassistant/components/wemo/binary_sensor.py b/homeassistant/components/wemo/binary_sensor.py index 766ca61c560..cde13d632fe 100644 --- a/homeassistant/components/wemo/binary_sensor.py +++ b/homeassistant/components/wemo/binary_sensor.py @@ -1,5 +1,6 @@ """Support for WeMo binary sensors.""" import asyncio +from typing import cast from pywemo import Insight, Maker @@ -10,7 +11,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN as WEMO_DOMAIN -from .entity import WemoEntity +from .entity import WemoBinaryStateEntity, WemoEntity from .wemo_device import DeviceCoordinator @@ -40,14 +41,9 @@ async def async_setup_entry( ) -class WemoBinarySensor(WemoEntity, BinarySensorEntity): +class WemoBinarySensor(WemoBinaryStateEntity, BinarySensorEntity): """Representation a WeMo binary sensor.""" - @property - def is_on(self) -> bool: - """Return true if the state is on. Standby is on.""" - return bool(self.wemo.get_state()) - class MakerBinarySensor(WemoEntity, BinarySensorEntity): """Maker device's sensor port.""" @@ -57,7 +53,7 @@ class MakerBinarySensor(WemoEntity, BinarySensorEntity): @property def is_on(self) -> bool: """Return true if the Maker's sensor is pulled low.""" - return bool(self.wemo.has_sensor) and self.wemo.sensor_state == 0 + return cast(int, self.wemo.has_sensor) != 0 and self.wemo.sensor_state == 0 class InsightBinarySensor(WemoBinarySensor): diff --git a/homeassistant/components/wemo/entity.py b/homeassistant/components/wemo/entity.py index 824fcacae7c..44f6b5a4e63 100644 --- a/homeassistant/components/wemo/entity.py +++ b/homeassistant/components/wemo/entity.py @@ -4,6 +4,7 @@ from __future__ import annotations from collections.abc import Generator import contextlib import logging +from typing import cast from pywemo.exceptions import ActionException @@ -40,9 +41,10 @@ class WemoEntity(CoordinatorEntity): @property def name(self) -> str: """Return the name of the device if any.""" + wemo_name: str = self.wemo.name if suffix := self.name_suffix: - return f"{self.wemo.name} {suffix}" - return str(self.wemo.name) + return f"{wemo_name} {suffix}" + return wemo_name @property def available(self) -> bool: @@ -59,9 +61,10 @@ class WemoEntity(CoordinatorEntity): @property def unique_id(self) -> str: """Return the id of this WeMo device.""" + serial_number: str = self.wemo.serialnumber if suffix := self.unique_id_suffix: - return f"{self.wemo.serialnumber}_{suffix}" - return str(self.wemo.serialnumber) + return f"{serial_number}_{suffix}" + return serial_number @property def device_info(self) -> DeviceInfo: @@ -82,3 +85,12 @@ class WemoEntity(CoordinatorEntity): except ActionException as err: _LOGGER.warning("Could not %s for %s (%s)", message, self.name, err) self._available = False + + +class WemoBinaryStateEntity(WemoEntity): + """Base for devices that return on/off state via device.get_state().""" + + @property + def is_on(self) -> bool: + """Return true if the state is on.""" + return cast(int, self.wemo.get_state()) != 0 diff --git a/homeassistant/components/wemo/fan.py b/homeassistant/components/wemo/fan.py index 003dfa7e633..03c5964a313 100644 --- a/homeassistant/components/wemo/fan.py +++ b/homeassistant/components/wemo/fan.py @@ -25,7 +25,7 @@ from .const import ( SERVICE_RESET_FILTER_LIFE, SERVICE_SET_HUMIDITY, ) -from .entity import WemoEntity +from .entity import WemoBinaryStateEntity from .wemo_device import DeviceCoordinator SCAN_INTERVAL = timedelta(seconds=10) @@ -38,10 +38,6 @@ ATTR_FILTER_LIFE = "filter_life" ATTR_FILTER_EXPIRED = "filter_expired" ATTR_WATER_LEVEL = "water_level" -# The WEMO_ constants below come from pywemo itself -WEMO_ON = 1 -WEMO_OFF = 0 - WEMO_HUMIDITY_45 = 0 WEMO_HUMIDITY_50 = 1 WEMO_HUMIDITY_55 = 2 @@ -102,7 +98,7 @@ async def async_setup_entry( ) -class WemoHumidifier(WemoEntity, FanEntity): +class WemoHumidifier(WemoBinaryStateEntity, FanEntity): """Representation of a WeMo humidifier.""" def __init__(self, coordinator: DeviceCoordinator) -> None: @@ -152,11 +148,6 @@ class WemoHumidifier(WemoEntity, FanEntity): self._last_fan_on_mode = self.wemo.fan_mode super()._handle_coordinator_update() - @property - def is_on(self) -> bool: - """Return true if the state is on.""" - return bool(self.wemo.get_state()) - def turn_on( self, speed: str | None = None, diff --git a/homeassistant/components/wemo/light.py b/homeassistant/components/wemo/light.py index eb19355f2b3..fbb1c84e449 100644 --- a/homeassistant/components/wemo/light.py +++ b/homeassistant/components/wemo/light.py @@ -2,7 +2,7 @@ from __future__ import annotations import asyncio -from typing import Any +from typing import Any, Optional, cast from pywemo.ouimeaux_device import bridge @@ -26,7 +26,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback import homeassistant.util.color as color_util from .const import DOMAIN as WEMO_DOMAIN -from .entity import WemoEntity +from .entity import WemoBinaryStateEntity, WemoEntity from .wemo_device import DeviceCoordinator SUPPORT_WEMO = ( @@ -101,7 +101,7 @@ class WemoLight(WemoEntity, LightEntity): @property def name(self) -> str: """Return the name of the device if any.""" - return str(self.light.name) + return cast(str, self.light.name) @property def available(self) -> bool: @@ -111,7 +111,7 @@ class WemoLight(WemoEntity, LightEntity): @property def unique_id(self) -> str: """Return the ID of this light.""" - return str(self.light.uniqueID) + return cast(str, self.light.uniqueID) @property def device_info(self) -> DeviceInfo: @@ -127,7 +127,7 @@ class WemoLight(WemoEntity, LightEntity): @property def brightness(self) -> int: """Return the brightness of this light between 0..255.""" - return int(self.light.state.get("level", 255)) + return cast(int, self.light.state.get("level", 255)) @property def hs_color(self) -> tuple[float, float] | None: @@ -139,14 +139,12 @@ class WemoLight(WemoEntity, LightEntity): @property def color_temp(self) -> int | None: """Return the color temperature of this light in mireds.""" - if (temp := self.light.state.get("temperature_mireds")) is not None: - return int(temp) - return None + return cast(Optional[int], self.light.state.get("temperature_mireds")) @property def is_on(self) -> bool: """Return true if device is on.""" - return bool(self.light.state.get("onoff") != WEMO_OFF) + return cast(int, self.light.state.get("onoff")) != WEMO_OFF @property def supported_features(self) -> int: @@ -194,7 +192,7 @@ class WemoLight(WemoEntity, LightEntity): self.schedule_update_ha_state() -class WemoDimmer(WemoEntity, LightEntity): +class WemoDimmer(WemoBinaryStateEntity, LightEntity): """Representation of a WeMo dimmer.""" @property @@ -205,14 +203,9 @@ class WemoDimmer(WemoEntity, LightEntity): @property def brightness(self) -> int: """Return the brightness of this light between 1 and 100.""" - wemo_brightness = int(self.wemo.get_brightness()) + wemo_brightness: int = self.wemo.get_brightness() return int((wemo_brightness * 255) / 100) - @property - def is_on(self) -> bool: - """Return true if the state is on.""" - return bool(self.wemo.get_state()) - def turn_on(self, **kwargs: Any) -> None: """Turn the dimmer on.""" # Wemo dimmer switches use a range of [0, 100] to control diff --git a/homeassistant/components/wemo/sensor.py b/homeassistant/components/wemo/sensor.py index c46e8928a03..eed5c510936 100644 --- a/homeassistant/components/wemo/sensor.py +++ b/homeassistant/components/wemo/sensor.py @@ -49,7 +49,8 @@ class InsightSensor(WemoEntity, SensorEntity): @property def name_suffix(self) -> str: """Return the name of the entity if any.""" - return str(self.entity_description.name) + assert self.entity_description.name + return self.entity_description.name @property def unique_id_suffix(self) -> str: diff --git a/homeassistant/components/wemo/switch.py b/homeassistant/components/wemo/switch.py index 39a6219b98b..9982274bbc0 100644 --- a/homeassistant/components/wemo/switch.py +++ b/homeassistant/components/wemo/switch.py @@ -3,7 +3,7 @@ from __future__ import annotations import asyncio from datetime import datetime, timedelta -from typing import Any +from typing import Any, cast from pywemo import CoffeeMaker, Insight, Maker @@ -16,7 +16,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util import convert from .const import DOMAIN as WEMO_DOMAIN -from .entity import WemoEntity +from .entity import WemoBinaryStateEntity from .wemo_device import DeviceCoordinator SCAN_INTERVAL = timedelta(seconds=10) @@ -57,7 +57,7 @@ async def async_setup_entry( ) -class WemoSwitch(WemoEntity, SwitchEntity): +class WemoSwitch(WemoBinaryStateEntity, SwitchEntity): """Representation of a WeMo switch.""" @property @@ -133,7 +133,7 @@ class WemoSwitch(WemoEntity, SwitchEntity): def detail_state(self) -> str: """Return the state of the device.""" if isinstance(self.wemo, CoffeeMaker): - return str(self.wemo.mode_string) + return cast(str, self.wemo.mode_string) if isinstance(self.wemo, Insight): standby_state = int(self.wemo.insight_params.get("state", 0)) if standby_state == WEMO_ON: @@ -152,11 +152,6 @@ class WemoSwitch(WemoEntity, SwitchEntity): return "mdi:coffee" return None - @property - def is_on(self) -> bool: - """Return true if the state is on. Standby is on.""" - return bool(self.wemo.get_state()) - def turn_on(self, **kwargs: Any) -> None: """Turn the switch on.""" with self._wemo_exception_handler("turn on"):