Store ZHA light brightness when fading off to turn on at the correct brightness ()

* Use light's on_level in ZHA to turn on at the correct brightness

Previously, if the light is turned off with a time transition, the
brightness level stored in the light will be 1. The next time the light
is turned on with no explicit brightness, it will be at 1.

This is solved by storing the current brightness in on_level before
turning off, and then using that when turning on (by calling the
onOff cluster 'on' command).

* store off light level locally to avoid wearing device's flash memory

* store off brightness in HA attributes

* improve set/clear of off_brightness

* fix device_state_attributes; clear off_brightness when light goes on

* fix tests
pull/31598/head
Abílio Costa 2019-09-23 14:08:44 +01:00 committed by David F. Mulcahey
parent 2c80ce3195
commit 61634d0a64
2 changed files with 21 additions and 6 deletions
homeassistant/components/zha
tests/components/zha

View File

@ -25,8 +25,6 @@ from .entity import ZhaEntity
_LOGGER = logging.getLogger(__name__)
DEFAULT_DURATION = 5
CAPABILITIES_COLOR_LOOP = 0x4
CAPABILITIES_COLOR_XY = 0x08
CAPABILITIES_COLOR_TEMP = 0x10
@ -91,6 +89,7 @@ class Light(ZhaEntity, light.Light):
self._color_temp = None
self._hs_color = None
self._brightness = None
self._off_brightness = None
self._effect_list = []
self._effect = None
self._on_off_channel = self.cluster_channels.get(CHANNEL_ON_OFF)
@ -130,7 +129,8 @@ class Light(ZhaEntity, light.Light):
@property
def device_state_attributes(self):
"""Return state attributes."""
return self.state_attributes
attributes = {"off_brightness": self._off_brightness}
return attributes
def set_level(self, value):
"""Set the brightness of this light between 0..254.
@ -171,6 +171,8 @@ class Light(ZhaEntity, light.Light):
def async_set_state(self, state):
"""Set the state."""
self._state = bool(state)
if state:
self._off_brightness = None
self.async_schedule_update_ha_state()
async def async_added_to_hass(self):
@ -191,6 +193,8 @@ class Light(ZhaEntity, light.Light):
self._state = last_state.state == STATE_ON
if "brightness" in last_state.attributes:
self._brightness = last_state.attributes["brightness"]
if "off_brightness" in last_state.attributes:
self._off_brightness = last_state.attributes["off_brightness"]
if "color_temp" in last_state.attributes:
self._color_temp = last_state.attributes["color_temp"]
if "hs_color" in last_state.attributes:
@ -201,10 +205,13 @@ class Light(ZhaEntity, light.Light):
async def async_turn_on(self, **kwargs):
"""Turn the entity on."""
transition = kwargs.get(light.ATTR_TRANSITION)
duration = transition * 10 if transition is not None else DEFAULT_DURATION
duration = transition * 10 if transition else 0
brightness = kwargs.get(light.ATTR_BRIGHTNESS)
effect = kwargs.get(light.ATTR_EFFECT)
if brightness is None and self._off_brightness is not None:
brightness = self._off_brightness
t_log = {}
if (
brightness is not None or transition
@ -225,13 +232,14 @@ class Light(ZhaEntity, light.Light):
self._brightness = level
if brightness is None or brightness:
# since some lights don't always turn on with move_to_level_with_on_off,
# we should call the on command on the on_off cluster if brightness is not 0.
result = await self._on_off_channel.on()
t_log["on_off"] = result
if not isinstance(result, list) or result[1] is not Status.SUCCESS:
self.debug("turned on: %s", t_log)
return
self._state = True
if (
light.ATTR_COLOR_TEMP in kwargs
and self.supported_features & light.SUPPORT_COLOR_TEMP
@ -289,6 +297,7 @@ class Light(ZhaEntity, light.Light):
t_log["color_loop_set"] = result
self._effect = None
self._off_brightness = None
self.debug("turned on: %s", t_log)
self.async_schedule_update_ha_state()
@ -296,6 +305,7 @@ class Light(ZhaEntity, light.Light):
"""Turn the entity off."""
duration = kwargs.get(light.ATTR_TRANSITION)
supports_level = self.supported_features & light.SUPPORT_BRIGHTNESS
if duration and supports_level:
result = await self._level_channel.move_to_level_with_on_off(
0, duration * 10
@ -306,6 +316,11 @@ class Light(ZhaEntity, light.Light):
if not isinstance(result, list) or result[1] is not Status.SUCCESS:
return
self._state = False
if duration and supports_level:
# store current brightness so that the next turn_on uses it.
self._off_brightness = self._brightness
self.async_schedule_update_ha_state()
async def async_update(self):

View File

@ -230,7 +230,7 @@ async def async_test_level_on_off_from_hass(
4,
(types.uint8_t, types.uint16_t),
10,
5.0,
0,
expect_reply=True,
manufacturer=None,
)