Make tplink entities unavailable if camera is off (#133877)

This commit is contained in:
Steven B. 2024-12-23 15:36:57 +00:00 committed by GitHub
parent 5487e8673c
commit 0cbc77ad3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 40 additions and 17 deletions

View File

@ -96,6 +96,7 @@ class TPLinkBinarySensorEntity(CoordinatedTPLinkFeatureEntity, BinarySensorEntit
entity_description: TPLinkBinarySensorEntityDescription
@callback
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""Update the entity's attributes."""
self._attr_is_on = cast(bool | None, self._feature.value)
return True

View File

@ -52,15 +52,19 @@ BUTTON_DESCRIPTIONS: Final = [
),
TPLinkButtonEntityDescription(
key="pan_left",
available_fn=lambda dev: dev.is_on,
),
TPLinkButtonEntityDescription(
key="pan_right",
available_fn=lambda dev: dev.is_on,
),
TPLinkButtonEntityDescription(
key="tilt_up",
available_fn=lambda dev: dev.is_on,
),
TPLinkButtonEntityDescription(
key="tilt_down",
available_fn=lambda dev: dev.is_on,
),
]
@ -100,5 +104,6 @@ class TPLinkButtonEntity(CoordinatedTPLinkFeatureEntity, ButtonEntity):
"""Execute action."""
await self._feature.set_value(True)
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""No need to update anything."""
return self.entity_description.available_fn(self._device)

View File

@ -40,6 +40,7 @@ CAMERA_DESCRIPTIONS: tuple[TPLinkCameraEntityDescription, ...] = (
TPLinkCameraEntityDescription(
key="live_view",
translation_key="live_view",
available_fn=lambda dev: dev.is_on,
),
)
@ -111,9 +112,10 @@ class TPLinkCameraEntity(CoordinatedTPLinkEntity, Camera):
return f"{legacy_device_id(self._device)}-{self.entity_description.key}"
@callback
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""Update the entity's attributes."""
self._attr_is_on = self._camera_module.is_on
return self.entity_description.available_fn(self._device)
async def stream_source(self) -> str | None:
"""Return the source of the stream."""

View File

@ -113,7 +113,7 @@ class TPLinkClimateEntity(CoordinatedTPLinkEntity, ClimateEntity):
await self._state_feature.set_value(False)
@callback
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""Update the entity's attributes."""
self._attr_current_temperature = cast(float | None, self._temp_feature.value)
self._attr_target_temperature = cast(float | None, self._target_feature.value)
@ -131,11 +131,12 @@ class TPLinkClimateEntity(CoordinatedTPLinkEntity, ClimateEntity):
self._mode_feature.value,
)
self._attr_hvac_action = HVACAction.OFF
return
return True
self._attr_hvac_action = STATE_TO_ACTION[
cast(ThermostatState, self._mode_feature.value)
]
return True
def _get_unique_id(self) -> str:
"""Return unique id."""

View File

@ -89,6 +89,7 @@ class TPLinkFeatureEntityDescription(EntityDescription):
"""Base class for a TPLink feature based entity description."""
deprecated_info: DeprecatedInfo | None = None
available_fn: Callable[[Device], bool] = lambda _: True
@dataclass(frozen=True, kw_only=True)
@ -96,6 +97,7 @@ class TPLinkModuleEntityDescription(EntityDescription):
"""Base class for a TPLink module based entity description."""
deprecated_info: DeprecatedInfo | None = None
available_fn: Callable[[Device], bool] = lambda _: True
def async_refresh_after[_T: CoordinatedTPLinkEntity, **_P](
@ -207,15 +209,18 @@ class CoordinatedTPLinkEntity(CoordinatorEntity[TPLinkDataUpdateCoordinator], AB
@abstractmethod
@callback
def _async_update_attrs(self) -> None:
"""Platforms implement this to update the entity internals."""
def _async_update_attrs(self) -> bool:
"""Platforms implement this to update the entity internals.
The return value is used to the set the entity available attribute.
"""
raise NotImplementedError
@callback
def _async_call_update_attrs(self) -> None:
"""Call update_attrs and make entity unavailable on errors."""
try:
self._async_update_attrs()
available = self._async_update_attrs()
except Exception as ex: # noqa: BLE001
if self._attr_available:
_LOGGER.warning(
@ -226,7 +231,7 @@ class CoordinatedTPLinkEntity(CoordinatorEntity[TPLinkDataUpdateCoordinator], AB
)
self._attr_available = False
else:
self._attr_available = True
self._attr_available = available
@callback
def _handle_coordinator_update(self) -> None:

View File

@ -106,7 +106,7 @@ class TPLinkFanEntity(CoordinatedTPLinkEntity, FanEntity):
await self.fan_module.set_fan_speed_level(value_in_range)
@callback
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""Update the entity's attributes."""
fan_speed = self.fan_module.fan_speed_level
self._attr_is_on = fan_speed != 0
@ -114,3 +114,4 @@ class TPLinkFanEntity(CoordinatedTPLinkEntity, FanEntity):
self._attr_percentage = ranged_value_to_percentage(SPEED_RANGE, fan_speed)
else:
self._attr_percentage = None
return True

View File

@ -335,7 +335,7 @@ class TPLinkLightEntity(CoordinatedTPLinkEntity, LightEntity):
return ColorMode.HS
@callback
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""Update the entity's attributes."""
light_module = self._light_module
self._attr_is_on = light_module.state.light_on is True
@ -349,6 +349,8 @@ class TPLinkLightEntity(CoordinatedTPLinkEntity, LightEntity):
hue, saturation, _ = light_module.hsv
self._attr_hs_color = hue, saturation
return True
class TPLinkLightEffectEntity(TPLinkLightEntity):
"""Representation of a TPLink Smart Light Strip."""
@ -368,7 +370,7 @@ class TPLinkLightEffectEntity(TPLinkLightEntity):
_attr_supported_features = LightEntityFeature.TRANSITION | LightEntityFeature.EFFECT
@callback
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""Update the entity's attributes."""
super()._async_update_attrs()
effect_module = self._effect_module
@ -381,6 +383,7 @@ class TPLinkLightEffectEntity(TPLinkLightEntity):
self._attr_effect_list = effect_list
else:
self._attr_effect_list = None
return True
@async_refresh_after
async def async_turn_on(self, **kwargs: Any) -> None:

View File

@ -114,6 +114,7 @@ class TPLinkNumberEntity(CoordinatedTPLinkFeatureEntity, NumberEntity):
await self._feature.set_value(int(value))
@callback
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""Update the entity's attributes."""
self._attr_native_value = cast(float | None, self._feature.value)
return True

View File

@ -91,6 +91,7 @@ class TPLinkSelectEntity(CoordinatedTPLinkFeatureEntity, SelectEntity):
await self._feature.set_value(option)
@callback
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""Update the entity's attributes."""
self._attr_current_option = cast(str | None, self._feature.value)
return True

View File

@ -153,7 +153,7 @@ class TPLinkSensorEntity(CoordinatedTPLinkFeatureEntity, SensorEntity):
entity_description: TPLinkSensorEntityDescription
@callback
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""Update the entity's attributes."""
value = self._feature.value
if value is not None and self._feature.precision_hint is not None:
@ -171,3 +171,4 @@ class TPLinkSensorEntity(CoordinatedTPLinkFeatureEntity, SensorEntity):
# Map to homeassistant units and fallback to upstream one if none found
if (unit := self._feature.unit) is not None:
self._attr_native_unit_of_measurement = UNIT_MAPPING.get(unit, unit)
return True

View File

@ -56,6 +56,7 @@ class TPLinkSirenEntity(CoordinatedTPLinkEntity, SirenEntity):
await self._alarm_module.stop()
@callback
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""Update the entity's attributes."""
self._attr_is_on = self._alarm_module.active
return True

View File

@ -109,6 +109,7 @@ class TPLinkSwitch(CoordinatedTPLinkFeatureEntity, SwitchEntity):
await self._feature.set_value(False)
@callback
def _async_update_attrs(self) -> None:
def _async_update_attrs(self) -> bool:
"""Update the entity's attributes."""
self._attr_is_on = cast(bool | None, self._feature.value)
return True