From 562a9df89ad13a7757c26497ba9a4936de7444f2 Mon Sep 17 00:00:00 2001 From: Robert Resch Date: Tue, 2 Jan 2024 21:15:45 +0100 Subject: [PATCH] Deprecate deebot.refresh (#493) --- custom_components/deebot/binary_sensor.py | 4 +- custom_components/deebot/entity.py | 26 +++++++++--- custom_components/deebot/image.py | 19 +++++---- custom_components/deebot/number.py | 4 +- custom_components/deebot/select.py | 6 +-- custom_components/deebot/sensor.py | 16 ++------ custom_components/deebot/switch.py | 6 +-- custom_components/deebot/translations/en.json | 13 ++++++ custom_components/deebot/vacuum.py | 41 +++++++++---------- translations.schema.json | 39 ++++++++++++++++++ 10 files changed, 113 insertions(+), 61 deletions(-) diff --git a/custom_components/deebot/binary_sensor.py b/custom_components/deebot/binary_sensor.py index ab25bea..7979f97 100644 --- a/custom_components/deebot/binary_sensor.py +++ b/custom_components/deebot/binary_sensor.py @@ -68,6 +68,4 @@ async def on_event(event: EventT) -> None: self._attr_icon = self.entity_description.icon_fn(self._attr_is_on) self.async_write_ha_state() - self.async_on_remove( - self._device.events.subscribe(self._capability.event, on_event) - ) + self._subscribe(self._capability.event, on_event) diff --git a/custom_components/deebot/entity.py b/custom_components/deebot/entity.py index d7a03bd..bbad973 100644 --- a/custom_components/deebot/entity.py +++ b/custom_components/deebot/entity.py @@ -1,5 +1,5 @@ """Deebot entity module.""" -from collections.abc import Callable +from collections.abc import Callable, Coroutine from dataclasses import dataclass from typing import Any, Generic, TypeVar @@ -33,8 +33,9 @@ class DeebotEntity(Entity, Generic[CapabilityT, _EntityDescriptionT]): # type: entity_description: _EntityDescriptionT _attr_should_poll = False - _always_available: bool = False _attr_has_entity_name = True + _always_available: bool = False + _subscribed_events: set[type[Event]] = set() def __init__( self, @@ -92,6 +93,21 @@ async def on_available(event: AvailabilityEvent) -> None: self._attr_available = event.available self.async_write_ha_state() - self.async_on_remove( - self._device.events.subscribe(AvailabilityEvent, on_available) - ) + self._subscribe(AvailabilityEvent, on_available) + + def _subscribe( + self, + event_type: type[EventT], + callback: Callable[[EventT], Coroutine[Any, Any, None]], + ) -> None: + """Subscribe to events.""" + self._subscribed_events.add(event_type) + self.async_on_remove(self._device.events.subscribe(event_type, callback)) + + async def async_update(self) -> None: + """Update the entity. + + Only used by the generic entity update service. + """ + for event_type in self._subscribed_events: + self._device.events.request_refresh(event_type) diff --git a/custom_components/deebot/image.py b/custom_components/deebot/image.py index c317a34..8ec1dea 100644 --- a/custom_components/deebot/image.py +++ b/custom_components/deebot/image.py @@ -71,8 +71,6 @@ async def async_added_to_hass(self) -> None: """Set up the event listeners now that hass is ready.""" await super().async_added_to_hass() - self._device.map.enable() - async def on_info(event: CachedMapInfoEvent) -> None: self._attr_extra_state_attributes["map_name"] = event.name @@ -80,14 +78,19 @@ async def on_changed(event: MapChangedEvent) -> None: self._attr_image_last_updated = event.when self.async_write_ha_state() - subscriptions = [ - self._device.events.subscribe(self._capability.chached_info.event, on_info), - self._device.events.subscribe(self._capability.changed.event, on_changed), - ] + self._subscribe(self._capability.chached_info.event, on_info) + self._subscribe(self._capability.changed.event, on_changed) def on_remove() -> None: - for unsubscribe in subscriptions: - unsubscribe() self._device.map.disable() self.async_on_remove(on_remove) + self._device.map.enable() + + async def async_update(self) -> None: + """Update the entity. + + Only used by the generic entity update service. + """ + await super().async_update() + self._device.map.refresh() diff --git a/custom_components/deebot/number.py b/custom_components/deebot/number.py index 99cc2bd..af6d91b 100644 --- a/custom_components/deebot/number.py +++ b/custom_components/deebot/number.py @@ -107,9 +107,7 @@ async def on_event(event: EventT) -> None: self._attr_icon = icon self.async_write_ha_state() - self.async_on_remove( - self._device.events.subscribe(self._capability.event, on_event) - ) + self._subscribe(self._capability.event, on_event) async def async_set_native_value(self, value: float) -> None: """Set new value.""" diff --git a/custom_components/deebot/select.py b/custom_components/deebot/select.py index 3713afe..f54f9d4 100644 --- a/custom_components/deebot/select.py +++ b/custom_components/deebot/select.py @@ -86,13 +86,11 @@ async def async_added_to_hass(self) -> None: """Set up the event listeners now that hass is ready.""" await super().async_added_to_hass() - async def on_water_info(event: EventT) -> None: + async def on_event(event: EventT) -> None: self._attr_current_option = self.entity_description.current_option_fn(event) self.async_write_ha_state() - self.async_on_remove( - self._device.events.subscribe(self._capability.event, on_water_info) - ) + self._subscribe(self._capability.event, on_event) async def async_select_option(self, option: str) -> None: """Change the selected option.""" diff --git a/custom_components/deebot/sensor.py b/custom_components/deebot/sensor.py index 461ee87..a09dd1d 100644 --- a/custom_components/deebot/sensor.py +++ b/custom_components/deebot/sensor.py @@ -276,9 +276,7 @@ async def on_event(event: Event) -> None: self._attr_extra_state_attributes = attr_fn(event) self.async_write_ha_state() - self.async_on_remove( - self._device.events.subscribe(self._capability.event, on_event) - ) + self._subscribe(self._capability.event, on_event) T = TypeVar("T", bound=Event) @@ -302,9 +300,7 @@ async def on_event(event: LifeSpanEvent) -> None: } self.async_write_ha_state() - self.async_on_remove( - self._device.events.subscribe(self._capability.event, on_event) - ) + self._subscribe(self._capability.event, on_event) class LastErrorSensor( @@ -333,9 +329,7 @@ async def on_event(event: ErrorEvent) -> None: self.async_write_ha_state() - self.async_on_remove( - self._device.events.subscribe(self._capability.event, on_event) - ) + self._subscribe(self._capability.event, on_event) class LastCleaningSensor( @@ -370,6 +364,4 @@ async def on_event(event: CleanLogEvent) -> None: self.async_write_ha_state() - self.async_on_remove( - self._device.events.subscribe(self._capability.event, on_event) - ) + self._subscribe(self._capability.event, on_event) diff --git a/custom_components/deebot/switch.py b/custom_components/deebot/switch.py index c9322c0..73e4139 100644 --- a/custom_components/deebot/switch.py +++ b/custom_components/deebot/switch.py @@ -91,13 +91,11 @@ async def async_added_to_hass(self) -> None: """Set up the event listeners now that hass is ready.""" await super().async_added_to_hass() - async def on_enable(event: EnableEvent) -> None: + async def on_event(event: EnableEvent) -> None: self._attr_is_on = event.enable self.async_write_ha_state() - self.async_on_remove( - self._device.events.subscribe(self._capability.event, on_enable) - ) + self._subscribe(self._capability.event, on_event) async def async_turn_on(self, **kwargs: Any) -> None: """Turn the entity on.""" diff --git a/custom_components/deebot/translations/en.json b/custom_components/deebot/translations/en.json index cae26e9..3ba3c6e 100644 --- a/custom_components/deebot/translations/en.json +++ b/custom_components/deebot/translations/en.json @@ -177,6 +177,19 @@ } } }, + "issues": { + "deprecated_service_refresh": { + "fix_flow": { + "step": { + "confirm": { + "description": "The service `deebot.refresh` is deprecated and will be removed.\nTo refresh an entity please use `homeassistant.update_entity` on the respective entity instead.\n\nPlease remove this service from your automations and scripts and select **submit** to close this issue.", + "title": "Deebot refresh service is being removed" + } + } + }, + "title": "Deebot refresh service is being removed" + } + }, "options": { "abort": { "cannot_connect": "Can't connect to the ecovacs API. Please check the logs", diff --git a/custom_components/deebot/vacuum.py b/custom_components/deebot/vacuum.py index 53e1c35..bb5c7b5 100644 --- a/custom_components/deebot/vacuum.py +++ b/custom_components/deebot/vacuum.py @@ -31,6 +31,7 @@ from homeassistant.helpers import entity_platform from homeassistant.helpers.config_validation import make_entity_service_schema from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.util import slugify from .const import ( @@ -158,31 +159,15 @@ async def on_status(event: StateEvent) -> None: self._attr_state = _STATE_TO_VACUUM_STATE[event.state] self.async_write_ha_state() - subscriptions = [ - self._device.events.subscribe(self._capability.battery.event, on_battery), - self._device.events.subscribe( - self._capability.fan_speed.event, on_fan_speed - ), - self._device.events.subscribe( - self._capability.stats.report.event, on_report_stats - ), - self._device.events.subscribe(self._capability.state.event, on_status), - ] + self._subscribe(self._capability.battery.event, on_battery) + self._subscribe(self._capability.fan_speed.event, on_fan_speed) + self._subscribe(self._capability.stats.report.event, on_report_stats) + self._subscribe(self._capability.state.event, on_status) if custom := self._capability.custom: - subscriptions.append( - self._device.events.subscribe(custom.event, on_custom_command) - ) + self._subscribe(custom.event, on_custom_command) if map_caps := self._capability.map: - subscriptions.append( - self._device.events.subscribe(map_caps.rooms.event, on_rooms) - ) - - def unsubscribe() -> None: - for sub in subscriptions: - sub() - - self.async_on_remove(unsubscribe) + self._subscribe(map_caps.rooms.event, on_rooms) @property def extra_state_attributes(self) -> Mapping[str, Any] | None: @@ -272,6 +257,18 @@ async def async_send_command( async def service_refresh(self, category: str) -> None: """Service to manually refresh.""" + _LOGGER.warning( + 'Service "deebot.refresh" is deprecated. To refresh an entity please use "homeassistant.update_entity" on the respective entity instead' + ) + async_create_issue( + self.hass, + DOMAIN, + "deprecated_service_refresh", + is_fixable=True, + severity=IssueSeverity.WARNING, + translation_key="deprecated_service_refresh", + ) + _LOGGER.debug("Manually refresh %s", category) event = REFRESH_STR_TO_EVENT_DTO.get(category, None) if event: diff --git a/translations.schema.json b/translations.schema.json index 6da69c1..30f0674 100644 --- a/translations.schema.json +++ b/translations.schema.json @@ -379,6 +379,45 @@ }, "type": "object" }, + "issues": { + "additionalProperties": false, + "properties": { + "deprecated_service_refresh": { + "additionalProperties": false, + "properties": { + "fix_flow": { + "additionalProperties": false, + "properties": { + "step": { + "additionalProperties": false, + "properties": { + "confirm": { + "additionalProperties": false, + "properties": { + "description": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "title": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, "options": { "additionalProperties": false, "properties": {