mirror of
https://github.com/home-assistant/core.git
synced 2026-09-25 07:51:46 -05:00
Update Lutron light state without waiting for the controller (#181086)
This commit is contained in:
@@ -96,6 +96,8 @@ class LutronLight(LutronDevice, LightEntity):
|
||||
if ATTR_TRANSITION in kwargs:
|
||||
args["fade_time_seconds"] = kwargs[ATTR_TRANSITION]
|
||||
self._lutron_device.set_level(**args)
|
||||
# Publish now rather than waiting for the controller to report back.
|
||||
self._publish_level()
|
||||
|
||||
@override
|
||||
def turn_off(self, **kwargs: Any) -> None:
|
||||
@@ -104,6 +106,18 @@ class LutronLight(LutronDevice, LightEntity):
|
||||
if ATTR_TRANSITION in kwargs:
|
||||
args["fade_time_seconds"] = kwargs[ATTR_TRANSITION]
|
||||
self._lutron_device.set_level(**args)
|
||||
self._publish_level()
|
||||
|
||||
def _publish_level(self) -> None:
|
||||
"""Publish the device's current level without waiting for a report.
|
||||
|
||||
Reads `last_level()` like `_update_callback` does, so a report that
|
||||
lands around the same time cannot be overwritten by a separately held
|
||||
assumed value -- whichever reached the library last is what gets
|
||||
published.
|
||||
"""
|
||||
self._update_attrs()
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
@property
|
||||
@override
|
||||
|
||||
@@ -48,6 +48,13 @@ def mock_lutron() -> Generator[MagicMock]:
|
||||
light.is_dimmable = True
|
||||
light.type = "LIGHT"
|
||||
light.last_level.return_value = 0
|
||||
|
||||
# pylutron's Output.set_level() stores the new level, which last_level()
|
||||
# then returns. Mirror that so the mock behaves like the library.
|
||||
def _set_level(new_level, fade_time_seconds=None):
|
||||
light.last_level.return_value = new_level
|
||||
|
||||
light.set_level.side_effect = _set_level
|
||||
area.outputs.append(light)
|
||||
|
||||
# Mock a switch
|
||||
|
||||
@@ -226,3 +226,79 @@ async def test_light_brightness_restore(
|
||||
)
|
||||
# HA level 127 -> Lutron level ~49.8
|
||||
light.set_level.assert_called_with(new_level=pytest.approx(50.0, abs=0.5))
|
||||
|
||||
|
||||
async def test_light_state_published_without_waiting_for_a_report(
|
||||
hass: HomeAssistant, mock_lutron: MagicMock, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""State follows the command without waiting for the controller to report.
|
||||
|
||||
The subscription still delivers the authoritative value later, but it can
|
||||
be slow, and on a HomeWorks QS system driven by keypad scenes it never
|
||||
arrives for zone levels at all.
|
||||
"""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
light = mock_lutron.areas[0].outputs[0]
|
||||
light.last_level.return_value = 0
|
||||
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity_id = "light.test_area_test_light"
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
||||
# No callback is fired here: nothing reports the change back.
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 128},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes[ATTR_BRIGHTNESS] == 128
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
||||
|
||||
async def test_a_later_report_wins_over_the_commanded_value(
|
||||
hass: HomeAssistant, mock_lutron: MagicMock, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""The controller remains authoritative when it does report."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
light = mock_lutron.areas[0].outputs[0]
|
||||
light.last_level.return_value = 0
|
||||
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity_id = "light.test_area_test_light"
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 128},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(entity_id).attributes[ATTR_BRIGHTNESS] == 128
|
||||
|
||||
# The controller says the zone actually landed on 100%.
|
||||
light.last_level.return_value = 100
|
||||
callback = light.subscribe.call_args[0][0]
|
||||
callback(light, None, None, None)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).attributes[ATTR_BRIGHTNESS] == 255
|
||||
|
||||
Reference in New Issue
Block a user