mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 15:47:12 +00:00
Minor polishing for tplink (#120868)
This commit is contained in:
parent
0ffebd4853
commit
90d622cd02
@ -77,16 +77,17 @@ class TPLinkClimateEntity(CoordinatedTPLinkEntity, ClimateEntity):
|
|||||||
parent: Device,
|
parent: Device,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the climate entity."""
|
"""Initialize the climate entity."""
|
||||||
super().__init__(device, coordinator, parent=parent)
|
self._state_feature = device.features["state"]
|
||||||
self._state_feature = self._device.features["state"]
|
self._mode_feature = device.features["thermostat_mode"]
|
||||||
self._mode_feature = self._device.features["thermostat_mode"]
|
self._temp_feature = device.features["temperature"]
|
||||||
self._temp_feature = self._device.features["temperature"]
|
self._target_feature = device.features["target_temperature"]
|
||||||
self._target_feature = self._device.features["target_temperature"]
|
|
||||||
|
|
||||||
self._attr_min_temp = self._target_feature.minimum_value
|
self._attr_min_temp = self._target_feature.minimum_value
|
||||||
self._attr_max_temp = self._target_feature.maximum_value
|
self._attr_max_temp = self._target_feature.maximum_value
|
||||||
self._attr_temperature_unit = UNIT_MAPPING[cast(str, self._temp_feature.unit)]
|
self._attr_temperature_unit = UNIT_MAPPING[cast(str, self._temp_feature.unit)]
|
||||||
|
|
||||||
|
super().__init__(device, coordinator, parent=parent)
|
||||||
|
|
||||||
@async_refresh_after
|
@async_refresh_after
|
||||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||||
"""Set target temperature."""
|
"""Set target temperature."""
|
||||||
|
@ -56,15 +56,21 @@ DEVICETYPES_WITH_SPECIALIZED_PLATFORMS = {
|
|||||||
DeviceType.Thermostat,
|
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
|
# Features excluded due to future platform additions
|
||||||
EXCLUDED_FEATURES = {
|
EXCLUDED_FEATURES = {
|
||||||
# update
|
# update
|
||||||
"current_firmware_version",
|
"current_firmware_version",
|
||||||
"available_firmware_version",
|
"available_firmware_version",
|
||||||
# fan
|
|
||||||
"fan_speed_level",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LEGACY_KEY_MAPPING = {
|
LEGACY_KEY_MAPPING = {
|
||||||
"current": ATTR_CURRENT_A,
|
"current": ATTR_CURRENT_A,
|
||||||
"current_consumption": ATTR_CURRENT_POWER_W,
|
"current_consumption": ATTR_CURRENT_POWER_W,
|
||||||
@ -179,15 +185,12 @@ class CoordinatedTPLinkEntity(CoordinatorEntity[TPLinkDataUpdateCoordinator], AB
|
|||||||
|
|
||||||
self._attr_unique_id = self._get_unique_id()
|
self._attr_unique_id = self._get_unique_id()
|
||||||
|
|
||||||
|
self._async_call_update_attrs()
|
||||||
|
|
||||||
def _get_unique_id(self) -> str:
|
def _get_unique_id(self) -> str:
|
||||||
"""Return unique ID for the entity."""
|
"""Return unique ID for the entity."""
|
||||||
return legacy_device_id(self._device)
|
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
|
@abstractmethod
|
||||||
@callback
|
@callback
|
||||||
def _async_update_attrs(self) -> None:
|
def _async_update_attrs(self) -> None:
|
||||||
@ -196,11 +199,7 @@ class CoordinatedTPLinkEntity(CoordinatorEntity[TPLinkDataUpdateCoordinator], AB
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_call_update_attrs(self) -> None:
|
def _async_call_update_attrs(self) -> None:
|
||||||
"""Call update_attrs and make entity unavailable on error.
|
"""Call update_attrs and make entity unavailable on errors."""
|
||||||
|
|
||||||
update_attrs can sometimes fail if a device firmware update breaks the
|
|
||||||
downstream library.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
self._async_update_attrs()
|
self._async_update_attrs()
|
||||||
except Exception as ex: # noqa: BLE001
|
except Exception as ex: # noqa: BLE001
|
||||||
@ -358,6 +357,7 @@ class CoordinatedTPLinkFeatureEntity(CoordinatedTPLinkEntity, ABC):
|
|||||||
and (
|
and (
|
||||||
feat.category is not Feature.Category.Primary
|
feat.category is not Feature.Category.Primary
|
||||||
or device.device_type not in DEVICETYPES_WITH_SPECIALIZED_PLATFORMS
|
or device.device_type not in DEVICETYPES_WITH_SPECIALIZED_PLATFORMS
|
||||||
|
or feat.id in FEATURES_ALLOW_LIST
|
||||||
)
|
)
|
||||||
and (
|
and (
|
||||||
desc := cls._description_for_feature(
|
desc := cls._description_for_feature(
|
||||||
|
@ -69,11 +69,12 @@ class TPLinkFanEntity(CoordinatedTPLinkEntity, FanEntity):
|
|||||||
parent: Device | None = None,
|
parent: Device | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the fan."""
|
"""Initialize the fan."""
|
||||||
super().__init__(device, coordinator, parent=parent)
|
|
||||||
self.fan_module = fan_module
|
self.fan_module = fan_module
|
||||||
# If _attr_name is None the entity name will be the device name
|
# If _attr_name is None the entity name will be the device name
|
||||||
self._attr_name = None if parent is None else device.alias
|
self._attr_name = None if parent is None else device.alias
|
||||||
|
|
||||||
|
super().__init__(device, coordinator, parent=parent)
|
||||||
|
|
||||||
@async_refresh_after
|
@async_refresh_after
|
||||||
async def async_turn_on(
|
async def async_turn_on(
|
||||||
self,
|
self,
|
||||||
|
@ -140,9 +140,7 @@ async def async_setup_entry(
|
|||||||
parent_coordinator = data.parent_coordinator
|
parent_coordinator = data.parent_coordinator
|
||||||
device = parent_coordinator.device
|
device = parent_coordinator.device
|
||||||
entities: list[TPLinkLightEntity | TPLinkLightEffectEntity] = []
|
entities: list[TPLinkLightEntity | TPLinkLightEffectEntity] = []
|
||||||
if (
|
if effect_module := device.modules.get(Module.LightEffect):
|
||||||
effect_module := device.modules.get(Module.LightEffect)
|
|
||||||
) and effect_module.has_custom_effects:
|
|
||||||
entities.append(
|
entities.append(
|
||||||
TPLinkLightEffectEntity(
|
TPLinkLightEffectEntity(
|
||||||
device,
|
device,
|
||||||
@ -151,17 +149,18 @@ async def async_setup_entry(
|
|||||||
effect_module=effect_module,
|
effect_module=effect_module,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
platform = entity_platform.async_get_current_platform()
|
if effect_module.has_custom_effects:
|
||||||
platform.async_register_entity_service(
|
platform = entity_platform.async_get_current_platform()
|
||||||
SERVICE_RANDOM_EFFECT,
|
platform.async_register_entity_service(
|
||||||
RANDOM_EFFECT_DICT,
|
SERVICE_RANDOM_EFFECT,
|
||||||
"async_set_random_effect",
|
RANDOM_EFFECT_DICT,
|
||||||
)
|
"async_set_random_effect",
|
||||||
platform.async_register_entity_service(
|
)
|
||||||
SERVICE_SEQUENCE_EFFECT,
|
platform.async_register_entity_service(
|
||||||
SEQUENCE_EFFECT_DICT,
|
SERVICE_SEQUENCE_EFFECT,
|
||||||
"async_set_sequence_effect",
|
SEQUENCE_EFFECT_DICT,
|
||||||
)
|
"async_set_sequence_effect",
|
||||||
|
)
|
||||||
elif Module.Light in device.modules:
|
elif Module.Light in device.modules:
|
||||||
entities.append(
|
entities.append(
|
||||||
TPLinkLightEntity(
|
TPLinkLightEntity(
|
||||||
@ -197,7 +196,6 @@ class TPLinkLightEntity(CoordinatedTPLinkEntity, LightEntity):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the light."""
|
"""Initialize the light."""
|
||||||
self._parent = parent
|
self._parent = parent
|
||||||
super().__init__(device, coordinator, parent=parent)
|
|
||||||
self._light_module = light_module
|
self._light_module = light_module
|
||||||
# If _attr_name is None the entity name will be the device name
|
# If _attr_name is None the entity name will be the device name
|
||||||
self._attr_name = None if parent is None else device.alias
|
self._attr_name = None if parent is None else device.alias
|
||||||
@ -215,7 +213,8 @@ class TPLinkLightEntity(CoordinatedTPLinkEntity, LightEntity):
|
|||||||
if len(self._attr_supported_color_modes) == 1:
|
if len(self._attr_supported_color_modes) == 1:
|
||||||
# If the light supports only a single color mode, set it now
|
# If the light supports only a single color mode, set it now
|
||||||
self._fixed_color_mode = next(iter(self._attr_supported_color_modes))
|
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:
|
def _get_unique_id(self) -> str:
|
||||||
"""Return unique ID for the entity."""
|
"""Return unique ID for the entity."""
|
||||||
@ -371,6 +370,7 @@ class TPLinkLightEffectEntity(TPLinkLightEntity):
|
|||||||
effect_module = self._effect_module
|
effect_module = self._effect_module
|
||||||
if effect_module.effect != LightEffect.LIGHT_EFFECTS_OFF:
|
if effect_module.effect != LightEffect.LIGHT_EFFECTS_OFF:
|
||||||
self._attr_effect = effect_module.effect
|
self._attr_effect = effect_module.effect
|
||||||
|
self._attr_color_mode = ColorMode.BRIGHTNESS
|
||||||
else:
|
else:
|
||||||
self._attr_effect = EFFECT_OFF
|
self._attr_effect = EFFECT_OFF
|
||||||
if effect_list := effect_module.effect_list:
|
if effect_list := effect_module.effect_list:
|
||||||
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
from kasa import Device, Feature
|
from kasa import Feature
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
@ -18,7 +18,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
|
|
||||||
from . import TPLinkConfigEntry
|
from . import TPLinkConfigEntry
|
||||||
from .const import UNIT_MAPPING
|
from .const import UNIT_MAPPING
|
||||||
from .coordinator import TPLinkDataUpdateCoordinator
|
|
||||||
from .entity import CoordinatedTPLinkFeatureEntity, TPLinkFeatureEntityDescription
|
from .entity import CoordinatedTPLinkFeatureEntity, TPLinkFeatureEntityDescription
|
||||||
|
|
||||||
|
|
||||||
@ -144,21 +143,6 @@ class TPLinkSensorEntity(CoordinatedTPLinkFeatureEntity, SensorEntity):
|
|||||||
|
|
||||||
entity_description: TPLinkSensorEntityDescription
|
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
|
@callback
|
||||||
def _async_update_attrs(self) -> None:
|
def _async_update_attrs(self) -> None:
|
||||||
"""Update the entity's attributes."""
|
"""Update the entity's attributes."""
|
||||||
|
@ -6,14 +6,13 @@ from dataclasses import dataclass
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from kasa import Device, Feature
|
from kasa import Feature
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import TPLinkConfigEntry
|
from . import TPLinkConfigEntry
|
||||||
from .coordinator import TPLinkDataUpdateCoordinator
|
|
||||||
from .entity import (
|
from .entity import (
|
||||||
CoordinatedTPLinkFeatureEntity,
|
CoordinatedTPLinkFeatureEntity,
|
||||||
TPLinkFeatureEntityDescription,
|
TPLinkFeatureEntityDescription,
|
||||||
@ -80,22 +79,6 @@ class TPLinkSwitch(CoordinatedTPLinkFeatureEntity, SwitchEntity):
|
|||||||
|
|
||||||
entity_description: TPLinkSwitchEntityDescription
|
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_refresh_after
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch on."""
|
"""Turn the switch on."""
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
"value": 121.1,
|
"value": 121.1,
|
||||||
"type": "Sensor",
|
"type": "Sensor",
|
||||||
"category": "Primary",
|
"category": "Primary",
|
||||||
"unit": "v",
|
"unit": "V",
|
||||||
"precision_hint": 1
|
"precision_hint": 1
|
||||||
},
|
},
|
||||||
"device_id": {
|
"device_id": {
|
||||||
|
@ -770,7 +770,7 @@
|
|||||||
'supported_features': 0,
|
'supported_features': 0,
|
||||||
'translation_key': 'voltage',
|
'translation_key': 'voltage',
|
||||||
'unique_id': '123456789ABCDEFGH_voltage',
|
'unique_id': '123456789ABCDEFGH_voltage',
|
||||||
'unit_of_measurement': 'v',
|
'unit_of_measurement': 'V',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_states[sensor.my_device_voltage-state]
|
# name: test_states[sensor.my_device_voltage-state]
|
||||||
@ -779,7 +779,7 @@
|
|||||||
'device_class': 'voltage',
|
'device_class': 'voltage',
|
||||||
'friendly_name': 'my_device Voltage',
|
'friendly_name': 'my_device Voltage',
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
'unit_of_measurement': 'v',
|
'unit_of_measurement': 'V',
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'sensor.my_device_voltage',
|
'entity_id': 'sensor.my_device_voltage',
|
||||||
|
@ -140,13 +140,17 @@ async def test_color_light(
|
|||||||
assert state.state == "on"
|
assert state.state == "on"
|
||||||
attributes = state.attributes
|
attributes = state.attributes
|
||||||
assert attributes[ATTR_BRIGHTNESS] == 128
|
assert attributes[ATTR_BRIGHTNESS] == 128
|
||||||
assert attributes[ATTR_COLOR_MODE] == "hs"
|
|
||||||
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "hs"]
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "hs"]
|
||||||
assert attributes[ATTR_MIN_MIREDS] == 111
|
# If effect is active, only the brightness can be controlled
|
||||||
assert attributes[ATTR_MAX_MIREDS] == 250
|
if attributes.get(ATTR_EFFECT) is not None:
|
||||||
assert attributes[ATTR_HS_COLOR] == (10, 30)
|
assert attributes[ATTR_COLOR_MODE] == "brightness"
|
||||||
assert attributes[ATTR_RGB_COLOR] == (255, 191, 178)
|
else:
|
||||||
assert attributes[ATTR_XY_COLOR] == (0.42, 0.336)
|
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(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN, "turn_off", BASE_PAYLOAD, blocking=True
|
LIGHT_DOMAIN, "turn_off", BASE_PAYLOAD, blocking=True
|
||||||
|
Loading…
x
Reference in New Issue
Block a user