Skip to content

Commit

Permalink
Minor polishing for tplink (#120868)
Browse files Browse the repository at this point in the history
  • Loading branch information
rytilahti authored Jul 2, 2024
1 parent 0ffebd4 commit 90d622c
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 78 deletions.
11 changes: 6 additions & 5 deletions homeassistant/components/tplink/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,17 @@ def __init__(
parent: Device,
) -> None:
"""Initialize the climate entity."""
super().__init__(device, coordinator, parent=parent)
self._state_feature = self._device.features["state"]
self._mode_feature = self._device.features["thermostat_mode"]
self._temp_feature = self._device.features["temperature"]
self._target_feature = self._device.features["target_temperature"]
self._state_feature = device.features["state"]
self._mode_feature = device.features["thermostat_mode"]
self._temp_feature = device.features["temperature"]
self._target_feature = device.features["target_temperature"]

self._attr_min_temp = self._target_feature.minimum_value
self._attr_max_temp = self._target_feature.maximum_value
self._attr_temperature_unit = UNIT_MAPPING[cast(str, self._temp_feature.unit)]

super().__init__(device, coordinator, parent=parent)

@async_refresh_after
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set target temperature."""
Expand Down
24 changes: 12 additions & 12 deletions homeassistant/components/tplink/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,21 @@
DeviceType.Thermostat,
}

# Primary features to always include even when the device type has its own platform
FEATURES_ALLOW_LIST = {
# lights have current_consumption and a specialized platform
"current_consumption"
}


# Features excluded due to future platform additions
EXCLUDED_FEATURES = {
# update
"current_firmware_version",
"available_firmware_version",
# fan
"fan_speed_level",
}


LEGACY_KEY_MAPPING = {
"current": ATTR_CURRENT_A,
"current_consumption": ATTR_CURRENT_POWER_W,
Expand Down Expand Up @@ -179,15 +185,12 @@ def __init__(

self._attr_unique_id = self._get_unique_id()

self._async_call_update_attrs()

def _get_unique_id(self) -> str:
"""Return unique ID for the entity."""
return legacy_device_id(self._device)

async def async_added_to_hass(self) -> None:
"""Handle being added to hass."""
self._async_call_update_attrs()
return await super().async_added_to_hass()

@abstractmethod
@callback
def _async_update_attrs(self) -> None:
Expand All @@ -196,11 +199,7 @@ def _async_update_attrs(self) -> None:

@callback
def _async_call_update_attrs(self) -> None:
"""Call update_attrs and make entity unavailable on error.
update_attrs can sometimes fail if a device firmware update breaks the
downstream library.
"""
"""Call update_attrs and make entity unavailable on errors."""
try:
self._async_update_attrs()
except Exception as ex: # noqa: BLE001
Expand Down Expand Up @@ -358,6 +357,7 @@ def _entities_for_device[
and (
feat.category is not Feature.Category.Primary
or device.device_type not in DEVICETYPES_WITH_SPECIALIZED_PLATFORMS
or feat.id in FEATURES_ALLOW_LIST
)
and (
desc := cls._description_for_feature(
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/tplink/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,12 @@ def __init__(
parent: Device | None = None,
) -> None:
"""Initialize the fan."""
super().__init__(device, coordinator, parent=parent)
self.fan_module = fan_module
# If _attr_name is None the entity name will be the device name
self._attr_name = None if parent is None else device.alias

super().__init__(device, coordinator, parent=parent)

@async_refresh_after
async def async_turn_on(
self,
Expand Down
32 changes: 16 additions & 16 deletions homeassistant/components/tplink/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,7 @@ async def async_setup_entry(
parent_coordinator = data.parent_coordinator
device = parent_coordinator.device
entities: list[TPLinkLightEntity | TPLinkLightEffectEntity] = []
if (
effect_module := device.modules.get(Module.LightEffect)
) and effect_module.has_custom_effects:
if effect_module := device.modules.get(Module.LightEffect):
entities.append(
TPLinkLightEffectEntity(
device,
Expand All @@ -151,17 +149,18 @@ async def async_setup_entry(
effect_module=effect_module,
)
)
platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service(
SERVICE_RANDOM_EFFECT,
RANDOM_EFFECT_DICT,
"async_set_random_effect",
)
platform.async_register_entity_service(
SERVICE_SEQUENCE_EFFECT,
SEQUENCE_EFFECT_DICT,
"async_set_sequence_effect",
)
if effect_module.has_custom_effects:
platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service(
SERVICE_RANDOM_EFFECT,
RANDOM_EFFECT_DICT,
"async_set_random_effect",
)
platform.async_register_entity_service(
SERVICE_SEQUENCE_EFFECT,
SEQUENCE_EFFECT_DICT,
"async_set_sequence_effect",
)
elif Module.Light in device.modules:
entities.append(
TPLinkLightEntity(
Expand Down Expand Up @@ -197,7 +196,6 @@ def __init__(
) -> None:
"""Initialize the light."""
self._parent = parent
super().__init__(device, coordinator, parent=parent)
self._light_module = light_module
# If _attr_name is None the entity name will be the device name
self._attr_name = None if parent is None else device.alias
Expand All @@ -215,7 +213,8 @@ def __init__(
if len(self._attr_supported_color_modes) == 1:
# If the light supports only a single color mode, set it now
self._fixed_color_mode = next(iter(self._attr_supported_color_modes))
self._async_call_update_attrs()

super().__init__(device, coordinator, parent=parent)

def _get_unique_id(self) -> str:
"""Return unique ID for the entity."""
Expand Down Expand Up @@ -371,6 +370,7 @@ def _async_update_attrs(self) -> None:
effect_module = self._effect_module
if effect_module.effect != LightEffect.LIGHT_EFFECTS_OFF:
self._attr_effect = effect_module.effect
self._attr_color_mode = ColorMode.BRIGHTNESS
else:
self._attr_effect = EFFECT_OFF
if effect_list := effect_module.effect_list:
Expand Down
18 changes: 1 addition & 17 deletions homeassistant/components/tplink/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from dataclasses import dataclass
from typing import cast

from kasa import Device, Feature
from kasa import Feature

from homeassistant.components.sensor import (
SensorDeviceClass,
Expand All @@ -18,7 +18,6 @@

from . import TPLinkConfigEntry
from .const import UNIT_MAPPING
from .coordinator import TPLinkDataUpdateCoordinator
from .entity import CoordinatedTPLinkFeatureEntity, TPLinkFeatureEntityDescription


Expand Down Expand Up @@ -144,21 +143,6 @@ class TPLinkSensorEntity(CoordinatedTPLinkFeatureEntity, SensorEntity):

entity_description: TPLinkSensorEntityDescription

def __init__(
self,
device: Device,
coordinator: TPLinkDataUpdateCoordinator,
*,
feature: Feature,
description: TPLinkSensorEntityDescription,
parent: Device | None = None,
) -> None:
"""Initialize the sensor."""
super().__init__(
device, coordinator, description=description, feature=feature, parent=parent
)
self._async_call_update_attrs()

@callback
def _async_update_attrs(self) -> None:
"""Update the entity's attributes."""
Expand Down
19 changes: 1 addition & 18 deletions homeassistant/components/tplink/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@
import logging
from typing import Any

from kasa import Device, Feature
from kasa import Feature

from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import TPLinkConfigEntry
from .coordinator import TPLinkDataUpdateCoordinator
from .entity import (
CoordinatedTPLinkFeatureEntity,
TPLinkFeatureEntityDescription,
Expand Down Expand Up @@ -80,22 +79,6 @@ class TPLinkSwitch(CoordinatedTPLinkFeatureEntity, SwitchEntity):

entity_description: TPLinkSwitchEntityDescription

def __init__(
self,
device: Device,
coordinator: TPLinkDataUpdateCoordinator,
*,
feature: Feature,
description: TPLinkSwitchEntityDescription,
parent: Device | None = None,
) -> None:
"""Initialize the switch."""
super().__init__(
device, coordinator, description=description, feature=feature, parent=parent
)

self._async_call_update_attrs()

@async_refresh_after
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
Expand Down
2 changes: 1 addition & 1 deletion tests/components/tplink/fixtures/features.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"value": 121.1,
"type": "Sensor",
"category": "Primary",
"unit": "v",
"unit": "V",
"precision_hint": 1
},
"device_id": {
Expand Down
4 changes: 2 additions & 2 deletions tests/components/tplink/snapshots/test_sensor.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,7 @@
'supported_features': 0,
'translation_key': 'voltage',
'unique_id': '123456789ABCDEFGH_voltage',
'unit_of_measurement': 'v',
'unit_of_measurement': 'V',
})
# ---
# name: test_states[sensor.my_device_voltage-state]
Expand All @@ -779,7 +779,7 @@
'device_class': 'voltage',
'friendly_name': 'my_device Voltage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'v',
'unit_of_measurement': 'V',
}),
'context': <ANY>,
'entity_id': 'sensor.my_device_voltage',
Expand Down
16 changes: 10 additions & 6 deletions tests/components/tplink/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,17 @@ async def test_color_light(
assert state.state == "on"
attributes = state.attributes
assert attributes[ATTR_BRIGHTNESS] == 128
assert attributes[ATTR_COLOR_MODE] == "hs"
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "hs"]
assert attributes[ATTR_MIN_MIREDS] == 111
assert attributes[ATTR_MAX_MIREDS] == 250
assert attributes[ATTR_HS_COLOR] == (10, 30)
assert attributes[ATTR_RGB_COLOR] == (255, 191, 178)
assert attributes[ATTR_XY_COLOR] == (0.42, 0.336)
# If effect is active, only the brightness can be controlled
if attributes.get(ATTR_EFFECT) is not None:
assert attributes[ATTR_COLOR_MODE] == "brightness"
else:
assert attributes[ATTR_COLOR_MODE] == "hs"
assert attributes[ATTR_MIN_MIREDS] == 111
assert attributes[ATTR_MAX_MIREDS] == 250
assert attributes[ATTR_HS_COLOR] == (10, 30)
assert attributes[ATTR_RGB_COLOR] == (255, 191, 178)
assert attributes[ATTR_XY_COLOR] == (0.42, 0.336)

await hass.services.async_call(
LIGHT_DOMAIN, "turn_off", BASE_PAYLOAD, blocking=True
Expand Down

0 comments on commit 90d622c

Please sign in to comment.