From 114bf0da3471d5bd91a436f0e180bf1e9abf73e7 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Thu, 25 Jan 2024 12:54:31 +0100 Subject: [PATCH] Update Lutron in callback (#108779) * Update Lutron in callback * Update Lutron in callback * Remove abstractmethod * Don't do IO in constructor * Split fetching and setting --- .../components/lutron/binary_sensor.py | 10 +++--- homeassistant/components/lutron/cover.py | 23 +++++------- homeassistant/components/lutron/entity.py | 12 +++++++ homeassistant/components/lutron/light.py | 27 ++++++-------- homeassistant/components/lutron/scene.py | 7 ++-- homeassistant/components/lutron/switch.py | 35 +++++++------------ 6 files changed, 50 insertions(+), 64 deletions(-) diff --git a/homeassistant/components/lutron/binary_sensor.py b/homeassistant/components/lutron/binary_sensor.py index 3adabfb3c9a..8cae9c9714a 100644 --- a/homeassistant/components/lutron/binary_sensor.py +++ b/homeassistant/components/lutron/binary_sensor.py @@ -52,13 +52,11 @@ class LutronOccupancySensor(LutronDevice, BinarySensorEntity): _lutron_device: OccupancyGroup _attr_device_class = BinarySensorDeviceClass.OCCUPANCY - @property - def is_on(self) -> bool: - """Return true if the binary sensor is on.""" - # Error cases will end up treated as unoccupied. - return self._lutron_device.state == OccupancyGroup.State.OCCUPIED - @property def extra_state_attributes(self) -> Mapping[str, Any] | None: """Return the state attributes.""" return {"lutron_integration_id": self._lutron_device.id} + + def _update_attrs(self) -> None: + """Update the state attributes.""" + self._attr_is_on = self._lutron_device.state == OccupancyGroup.State.OCCUPIED diff --git a/homeassistant/components/lutron/cover.py b/homeassistant/components/lutron/cover.py index 9aace54757f..cdcdf93ccbd 100644 --- a/homeassistant/components/lutron/cover.py +++ b/homeassistant/components/lutron/cover.py @@ -53,16 +53,6 @@ class LutronCover(LutronDevice, CoverEntity): _lutron_device: Output _attr_name = None - @property - def is_closed(self) -> bool: - """Return if the cover is closed.""" - return self._lutron_device.last_level() < 1 - - @property - def current_cover_position(self) -> int: - """Return the current position of cover.""" - return self._lutron_device.last_level() - def close_cover(self, **kwargs: Any) -> None: """Close the cover.""" self._lutron_device.level = 0 @@ -77,10 +67,15 @@ class LutronCover(LutronDevice, CoverEntity): position = kwargs[ATTR_POSITION] self._lutron_device.level = position - def update(self) -> None: - """Call when forcing a refresh of the device.""" - # Reading the property (rather than last_level()) fetches value - level = self._lutron_device.level + def _request_state(self) -> None: + """Request the state from the device.""" + self._lutron_device.level # pylint: disable=pointless-statement + + def _update_attrs(self) -> None: + """Update the state attributes.""" + level = self._lutron_device.last_level() + self._attr_is_closed = level < 1 + self._attr_current_cover_position = level _LOGGER.debug("Lutron ID: %d updated to %f", self._lutron_device.id, level) @property diff --git a/homeassistant/components/lutron/entity.py b/homeassistant/components/lutron/entity.py index 4e6d0066a47..461e5acb56d 100644 --- a/homeassistant/components/lutron/entity.py +++ b/homeassistant/components/lutron/entity.py @@ -27,10 +27,17 @@ class LutronBaseEntity(Entity): """Register callbacks.""" self._lutron_device.subscribe(self._update_callback, None) + def _request_state(self) -> None: + """Request the state.""" + + def _update_attrs(self) -> None: + """Update the entity's attributes.""" + def _update_callback( self, _device: LutronEntity, _context: None, _event: LutronEvent, _params: dict ) -> None: """Run when invoked by pylutron when the device state changes.""" + self._update_attrs() self.schedule_update_ha_state() @property @@ -41,6 +48,11 @@ class LutronBaseEntity(Entity): return None return f"{self._controller.guid}_{self._lutron_device.uuid}" + def update(self) -> None: + """Update the entity's state.""" + self._request_state() + self._update_attrs() + class LutronDevice(LutronBaseEntity): """Representation of a Lutron device entity.""" diff --git a/homeassistant/components/lutron/light.py b/homeassistant/components/lutron/light.py index fa7a45734fe..da991969228 100644 --- a/homeassistant/components/lutron/light.py +++ b/homeassistant/components/lutron/light.py @@ -54,14 +54,6 @@ class LutronLight(LutronDevice, LightEntity): _prev_brightness: int | None = None _attr_name = None - @property - def brightness(self) -> int: - """Return the brightness of the light.""" - new_brightness = to_hass_level(self._lutron_device.last_level()) - if new_brightness != 0: - self._prev_brightness = new_brightness - return new_brightness - def turn_on(self, **kwargs: Any) -> None: """Turn the light on.""" if ATTR_BRIGHTNESS in kwargs and self._lutron_device.is_dimmable: @@ -82,12 +74,15 @@ class LutronLight(LutronDevice, LightEntity): """Return the state attributes.""" return {"lutron_integration_id": self._lutron_device.id} - @property - def is_on(self) -> bool: - """Return true if device is on.""" - return self._lutron_device.last_level() > 0 + def _request_state(self) -> None: + """Request the state from the device.""" + self._lutron_device.level # pylint: disable=pointless-statement - def update(self) -> None: - """Call when forcing a refresh of the device.""" - if self._prev_brightness is None: - self._prev_brightness = to_hass_level(self._lutron_device.level) + def _update_attrs(self) -> None: + """Update the state attributes.""" + level = self._lutron_device.last_level() + self._attr_is_on = level > 0 + hass_level = to_hass_level(level) + self._attr_brightness = hass_level + if self._prev_brightness is None or hass_level != 0: + self._prev_brightness = hass_level diff --git a/homeassistant/components/lutron/scene.py b/homeassistant/components/lutron/scene.py index a4a505c8477..9485eddf78b 100644 --- a/homeassistant/components/lutron/scene.py +++ b/homeassistant/components/lutron/scene.py @@ -27,11 +27,8 @@ async def async_setup_entry( entry_data: LutronData = hass.data[DOMAIN][config_entry.entry_id] async_add_entities( - [ - LutronScene(area_name, keypad, device, entry_data.client) - for area_name, keypad, device, led in entry_data.scenes - ], - True, + LutronScene(area_name, keypad, device, entry_data.client) + for area_name, keypad, device, led in entry_data.scenes ) diff --git a/homeassistant/components/lutron/switch.py b/homeassistant/components/lutron/switch.py index 14331fa500d..0286fdef238 100644 --- a/homeassistant/components/lutron/switch.py +++ b/homeassistant/components/lutron/switch.py @@ -44,13 +44,6 @@ class LutronSwitch(LutronDevice, SwitchEntity): _lutron_device: Output - def __init__( - self, area_name: str, lutron_device: Output, controller: Lutron - ) -> None: - """Initialize the switch.""" - self._prev_state = None - super().__init__(area_name, lutron_device, controller) - def turn_on(self, **kwargs: Any) -> None: """Turn the switch on.""" self._lutron_device.level = 100 @@ -64,15 +57,13 @@ class LutronSwitch(LutronDevice, SwitchEntity): """Return the state attributes.""" return {"lutron_integration_id": self._lutron_device.id} - @property - def is_on(self) -> bool: - """Return true if device is on.""" - return self._lutron_device.last_level() > 0 + def _request_state(self) -> None: + """Request the state from the device.""" + self._lutron_device.level # pylint: disable=pointless-statement - def update(self) -> None: - """Call when forcing a refresh of the device.""" - if self._prev_state is None: - self._prev_state = self._lutron_device.level > 0 + def _update_attrs(self) -> None: + """Update the state attributes.""" + self._attr_is_on = self._lutron_device.last_level() > 0 class LutronLed(LutronKeypad, SwitchEntity): @@ -110,12 +101,10 @@ class LutronLed(LutronKeypad, SwitchEntity): "led": self._lutron_device.name, } - @property - def is_on(self) -> bool: - """Return true if device is on.""" - return self._lutron_device.last_state - - def update(self) -> None: - """Call when forcing a refresh of the device.""" - # The following property getter actually triggers an update in Lutron + def _request_state(self) -> None: + """Request the state from the device.""" self._lutron_device.state # pylint: disable=pointless-statement + + def _update_attrs(self) -> None: + """Update the state attributes.""" + self._attr_is_on = self._lutron_device.last_state