Fix Nanoleaf light state propagation after change from home asisstant (#144291)

* Fix Nanoleaf light state propagation after change from home asisstant

* Add tests to check if nanoleaf light is triggering async_write_ha_state

* Fix pylint for test case

* Fix use coordinator.async_refresh instead of async_write_ha_state

* Fix tests

---------

Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
Nils Müller 2025-05-18 22:26:02 +02:00 committed by GitHub
parent 78ac8ba841
commit c1fcd8ea7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 215 additions and 0 deletions

View File

@ -125,8 +125,10 @@ class NanoleafLight(NanoleafEntity, LightEntity):
await self._nanoleaf.turn_on()
if brightness:
await self._nanoleaf.set_brightness(int(brightness / 2.55))
await self.coordinator.async_refresh()
async def async_turn_off(self, **kwargs: Any) -> None:
"""Instruct the light to turn off."""
transition: float | None = kwargs.get(ATTR_TRANSITION)
await self._nanoleaf.turn_off(None if transition is None else int(transition))
await self.coordinator.async_refresh()

View File

@ -1 +1,13 @@
"""Tests for the Nanoleaf integration."""
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
"""Set up the component."""
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()

View File

@ -0,0 +1,49 @@
"""Common fixtures for Nanoleaf tests."""
from collections.abc import AsyncGenerator
from unittest.mock import AsyncMock, patch
import pytest
from homeassistant.components.nanoleaf import DOMAIN
from homeassistant.const import CONF_HOST, CONF_TOKEN
from tests.common import MockConfigEntry
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Mock a Nanoleaf config entry."""
return MockConfigEntry(
domain=DOMAIN,
data={
CONF_HOST: "10.0.0.10",
CONF_TOKEN: "1234567890abcdef",
},
)
@pytest.fixture
async def mock_nanoleaf() -> AsyncGenerator[AsyncMock]:
"""Mock a Nanoleaf device."""
with patch(
"homeassistant.components.nanoleaf.Nanoleaf", autospec=True
) as mock_nanoleaf:
client = mock_nanoleaf.return_value
client.model = "NO_TOUCH"
client.host = "10.0.0.10"
client.serial_no = "ABCDEF123456"
client.color_temperature_max = 4500
client.color_temperature_min = 1200
client.is_on = False
client.brightness = 50
client.color_temperature = 2700
client.hue = 120
client.saturation = 50
client.color_mode = "hs"
client.effect = "Rainbow"
client.effects_list = ["Rainbow", "Sunset", "Nemo"]
client.firmware_version = "4.0.0"
client.name = "Nanoleaf"
client.manufacturer = "Nanoleaf"
yield client

View File

@ -0,0 +1,84 @@
# serializer version: 1
# name: test_entities[light.nanoleaf-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'effect_list': list([
'Rainbow',
'Sunset',
'Nemo',
]),
'max_color_temp_kelvin': 4500,
'max_mireds': 833,
'min_color_temp_kelvin': 1200,
'min_mireds': 222,
'supported_color_modes': list([
<ColorMode.COLOR_TEMP: 'color_temp'>,
<ColorMode.HS: 'hs'>,
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.nanoleaf',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'nanoleaf',
'previous_unique_id': None,
'supported_features': <LightEntityFeature: 36>,
'translation_key': 'light',
'unique_id': 'ABCDEF123456',
'unit_of_measurement': None,
})
# ---
# name: test_entities[light.nanoleaf-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'brightness': None,
'color_mode': None,
'color_temp': None,
'color_temp_kelvin': None,
'effect': None,
'effect_list': list([
'Rainbow',
'Sunset',
'Nemo',
]),
'friendly_name': 'Nanoleaf',
'hs_color': None,
'max_color_temp_kelvin': 4500,
'max_mireds': 833,
'min_color_temp_kelvin': 1200,
'min_mireds': 222,
'rgb_color': None,
'supported_color_modes': list([
<ColorMode.COLOR_TEMP: 'color_temp'>,
<ColorMode.HS: 'hs'>,
]),
'supported_features': <LightEntityFeature: 36>,
'xy_color': None,
}),
'context': <ANY>,
'entity_id': 'light.nanoleaf',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@ -0,0 +1,68 @@
"""Tests for the Nanoleaf light platform."""
from unittest.mock import AsyncMock, patch
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.light import ATTR_EFFECT_LIST, DOMAIN as LIGHT_DOMAIN
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from tests.common import MockConfigEntry, snapshot_platform
async def test_entities(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
mock_nanoleaf: AsyncMock,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test all entities."""
with patch("homeassistant.components.nanoleaf.PLATFORMS", [Platform.LIGHT]):
await setup_integration(hass, mock_config_entry)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
@pytest.mark.parametrize("service", [SERVICE_TURN_ON, SERVICE_TURN_OFF])
async def test_turning_on_or_off_writes_state(
hass: HomeAssistant,
mock_nanoleaf: AsyncMock,
mock_config_entry: MockConfigEntry,
service: str,
) -> None:
"""Test turning on or off the light writes the state."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("light.nanoleaf").attributes[ATTR_EFFECT_LIST] == [
"Rainbow",
"Sunset",
"Nemo",
]
mock_nanoleaf.effects_list = ["Rainbow", "Sunset", "Nemo", "Something Else"]
await hass.services.async_call(
LIGHT_DOMAIN,
service,
{
ATTR_ENTITY_ID: "light.nanoleaf",
},
blocking=True,
)
assert hass.states.get("light.nanoleaf").attributes[ATTR_EFFECT_LIST] == [
"Rainbow",
"Sunset",
"Nemo",
"Something Else",
]