1
0
mirror of https://github.com/home-assistant/core.git synced 2026-02-15 07:36:16 +00:00

Add dimming functionality to the Lunatone light entity (#154508)

Co-authored-by: Dave T <17680170+davet2001@users.noreply.github.com>
This commit is contained in:
MoonDevLT
2025-10-24 16:40:42 +02:00
committed by GitHub
parent c38e02072e
commit 4d525dee48
3 changed files with 109 additions and 7 deletions

View File

@@ -5,11 +5,17 @@ from __future__ import annotations
import asyncio
from typing import Any
from homeassistant.components.light import ColorMode, LightEntity
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ColorMode,
LightEntity,
brightness_supported,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util.color import brightness_to_value, value_to_brightness
from .const import DOMAIN
from .coordinator import LunatoneConfigEntry, LunatoneDevicesDataUpdateCoordinator
@@ -42,8 +48,10 @@ class LunatoneLight(
):
"""Representation of a Lunatone light."""
_attr_color_mode = ColorMode.ONOFF
_attr_supported_color_modes = {ColorMode.ONOFF}
BRIGHTNESS_SCALE = (1, 100)
_last_brightness = 255
_attr_has_entity_name = True
_attr_name = None
_attr_should_poll = False
@@ -82,6 +90,25 @@ class LunatoneLight(
"""Return True if light is on."""
return self._device is not None and self._device.is_on
@property
def brightness(self) -> int:
"""Return the brightness of this light between 0..255."""
if self._device is None:
return 0
return value_to_brightness(self.BRIGHTNESS_SCALE, self._device.brightness)
@property
def color_mode(self) -> ColorMode:
"""Return the color mode of the light."""
if self._device is not None and self._device.is_dimmable:
return ColorMode.BRIGHTNESS
return ColorMode.ONOFF
@property
def supported_color_modes(self) -> set[ColorMode]:
"""Return the supported color modes."""
return {self.color_mode}
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
@@ -91,13 +118,27 @@ class LunatoneLight(
async def async_turn_on(self, **kwargs: Any) -> None:
"""Instruct the light to turn on."""
assert self._device
await self._device.switch_on()
if brightness_supported(self.supported_color_modes):
brightness = kwargs.get(ATTR_BRIGHTNESS, self._last_brightness)
await self._device.fade_to_brightness(
brightness_to_value(self.BRIGHTNESS_SCALE, brightness)
)
else:
await self._device.switch_on()
await asyncio.sleep(STATUS_UPDATE_DELAY)
await self.coordinator.async_refresh()
async def async_turn_off(self, **kwargs: Any) -> None:
"""Instruct the light to turn off."""
assert self._device
await self._device.switch_off()
if brightness_supported(self.supported_color_modes):
self._last_brightness = self.brightness
await self._device.fade_to_brightness(0)
else:
await self._device.switch_off()
await asyncio.sleep(STATUS_UPDATE_DELAY)
await self.coordinator.async_refresh()

View File

@@ -27,6 +27,7 @@ def mock_setup_entry() -> Generator[AsyncMock]:
@pytest.fixture
def mock_lunatone_devices() -> Generator[AsyncMock]:
"""Mock a Lunatone devices object."""
state = {"is_dimmable": False}
def build_devices_mock(devices: Devices):
device_list = []
@@ -36,6 +37,10 @@ def mock_lunatone_devices() -> Generator[AsyncMock]:
device.id = device.data.id
device.name = device.data.name
device.is_on = device.data.features.switchable.status
device.brightness = device.data.features.dimmable.status
type(device).is_dimmable = PropertyMock(
side_effect=lambda s=state: s["is_dimmable"]
)
device_list.append(device)
return device_list
@@ -47,6 +52,7 @@ def mock_lunatone_devices() -> Generator[AsyncMock]:
type(devices).devices = PropertyMock(
side_effect=lambda d=devices: build_devices_mock(d)
)
devices.set_is_dimmable = lambda value, s=state: s.update(is_dimmable=value)
yield devices

View File

@@ -4,7 +4,7 @@ from unittest.mock import AsyncMock
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.components.light import ATTR_BRIGHTNESS, DOMAIN as LIGHT_DOMAIN
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
@@ -47,7 +47,6 @@ async def test_turn_on_off(
mock_lunatone_devices: AsyncMock,
mock_lunatone_info: AsyncMock,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test the light can be turned on and off."""
await setup_integration(hass, mock_config_entry)
@@ -77,3 +76,59 @@ async def test_turn_on_off(
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_OFF
async def test_turn_on_off_with_brightness(
hass: HomeAssistant,
mock_lunatone_devices: AsyncMock,
mock_lunatone_info: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test the light can be turned on with brightness."""
expected_brightness = 128
brightness_percentages = iter([50.0, 0.0, 50.0])
mock_lunatone_devices.set_is_dimmable(True)
await setup_integration(hass, mock_config_entry)
async def fake_update():
brightness = next(brightness_percentages)
device = mock_lunatone_devices.data.devices[0]
device.features.switchable.status = brightness > 0
device.features.dimmable.status = brightness
mock_lunatone_devices.async_update.side_effect = fake_update
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: TEST_ENTITY_ID, ATTR_BRIGHTNESS: expected_brightness},
blocking=True,
)
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_ON
assert state.attributes["brightness"] == expected_brightness
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
blocking=True,
)
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_OFF
assert not state.attributes["brightness"]
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
blocking=True,
)
state = hass.states.get(TEST_ENTITY_ID)
assert state.state == STATE_ON
assert state.attributes["brightness"] == expected_brightness