"""Test KNX light.""" from __future__ import annotations from datetime import timedelta from freezegun.api import FrozenDateTimeFactory import pytest from xknx.core import XknxConnectionState from xknx.devices.light import Light as XknxLight from homeassistant.components.knx.const import CONF_STATE_ADDRESS, KNX_ADDRESS, Platform from homeassistant.components.knx.schema import LightSchema from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_NAME, ATTR_COLOR_TEMP_KELVIN, ATTR_HS_COLOR, ATTR_RGBW_COLOR, ColorMode, ) from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON, EntityCategory from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from . import KnxEntityGenerator from .conftest import KNXTestKit from tests.common import async_fire_time_changed async def test_light_simple(hass: HomeAssistant, knx: KNXTestKit) -> None: """Test simple KNX light.""" test_address = "1/1/1" await knx.setup_integration( { LightSchema.PLATFORM: { CONF_NAME: "test", KNX_ADDRESS: test_address, } } ) knx.assert_state( "light.test", STATE_OFF, supported_color_modes=[ColorMode.ONOFF], ) # turn on light await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test"}, blocking=True, ) await knx.assert_write(test_address, True) knx.assert_state( "light.test", STATE_ON, color_mode=ColorMode.ONOFF, ) # turn off light await hass.services.async_call( "light", "turn_off", {"entity_id": "light.test"}, blocking=True, ) await knx.assert_write(test_address, False) knx.assert_state("light.test", STATE_OFF) # receive ON telegram await knx.receive_write(test_address, True) knx.assert_state("light.test", STATE_ON) # receive OFF telegram await knx.receive_write(test_address, False) knx.assert_state("light.test", STATE_OFF) # switch does not respond to read by default await knx.receive_read(test_address) await knx.assert_telegram_count(0) async def test_light_brightness(hass: HomeAssistant, knx: KNXTestKit) -> None: """Test dimmable KNX light.""" test_address = "1/1/1" test_brightness = "1/1/2" test_brightness_state = "1/1/3" await knx.setup_integration( { LightSchema.PLATFORM: { CONF_NAME: "test", KNX_ADDRESS: test_address, LightSchema.CONF_BRIGHTNESS_ADDRESS: test_brightness, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_brightness_state, } } ) # StateUpdater initialize state await knx.assert_read(test_brightness_state) knx.xknx.connection_manager.connection_state_changed(XknxConnectionState.CONNECTED) # turn on light via brightness await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 80}, blocking=True, ) await knx.assert_write(test_brightness, (80,)) # state is still OFF until controller reports otherwise knx.assert_state("light.test", STATE_OFF) await knx.receive_write(test_address, True) knx.assert_state( "light.test", STATE_ON, brightness=80, supported_color_modes=[ColorMode.BRIGHTNESS], color_mode=ColorMode.BRIGHTNESS, ) # receive brightness changes from KNX await knx.receive_write(test_brightness_state, (255,)) knx.assert_state("light.test", STATE_ON, brightness=255) await knx.receive_write(test_brightness, (128,)) knx.assert_state("light.test", STATE_ON, brightness=128) # turn off light via brightness await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 0}, blocking=True, ) await knx.assert_write(test_address, False) knx.assert_state("light.test", STATE_OFF) async def test_light_color_temp_absolute(hass: HomeAssistant, knx: KNXTestKit) -> None: """Test KNX light color temperature adjustable in Kelvin.""" test_address = "1/1/1" test_address_state = "1/1/2" test_brightness = "1/1/3" test_brightness_state = "1/1/4" test_ct = "1/1/5" test_ct_state = "1/1/6" await knx.setup_integration( { LightSchema.PLATFORM: [ { CONF_NAME: "test", KNX_ADDRESS: test_address, CONF_STATE_ADDRESS: test_address_state, LightSchema.CONF_BRIGHTNESS_ADDRESS: test_brightness, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_brightness_state, LightSchema.CONF_COLOR_TEMP_ADDRESS: test_ct, LightSchema.CONF_COLOR_TEMP_STATE_ADDRESS: test_ct_state, LightSchema.CONF_COLOR_TEMP_MODE: "absolute", }, ] } ) # StateUpdater initialize state await knx.assert_read(test_address_state) await knx.assert_read(test_brightness_state) await knx.receive_response(test_address_state, True) await knx.receive_response(test_brightness_state, (255,)) # # StateUpdater semaphore allows 2 concurrent requests await knx.assert_read(test_ct_state) await knx.receive_response(test_ct_state, (0x0A, 0x8C)) # 2700 Kelvin - 370 Mired knx.assert_state( "light.test", STATE_ON, brightness=255, supported_color_modes=[ColorMode.COLOR_TEMP], color_mode=ColorMode.COLOR_TEMP, color_temp=370, color_temp_kelvin=2700, ) # change color temperature from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_TEMP_KELVIN: 4000}, # 4000 - 0x0FA0 blocking=True, ) await knx.assert_write(test_ct, (0x0F, 0xA0)) knx.assert_state("light.test", STATE_ON, color_temp=250) # change color temperature from KNX await knx.receive_write(test_ct_state, (0x17, 0x70)) # 6000 Kelvin - 166 Mired knx.assert_state( "light.test", STATE_ON, color_temp=166, color_temp_kelvin=6000, ) async def test_light_color_temp_relative(hass: HomeAssistant, knx: KNXTestKit) -> None: """Test KNX light color temperature adjustable in percent.""" test_address = "1/1/1" test_address_state = "1/1/2" test_brightness = "1/1/3" test_brightness_state = "1/1/4" test_ct = "1/1/5" test_ct_state = "1/1/6" await knx.setup_integration( { LightSchema.PLATFORM: [ { CONF_NAME: "test", KNX_ADDRESS: test_address, CONF_STATE_ADDRESS: test_address_state, LightSchema.CONF_BRIGHTNESS_ADDRESS: test_brightness, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_brightness_state, LightSchema.CONF_COLOR_TEMP_ADDRESS: test_ct, LightSchema.CONF_COLOR_TEMP_STATE_ADDRESS: test_ct_state, LightSchema.CONF_COLOR_TEMP_MODE: "relative", LightSchema.CONF_MIN_KELVIN: 3000, LightSchema.CONF_MAX_KELVIN: 4000, }, ] } ) # StateUpdater initialize state await knx.assert_read(test_address_state) await knx.assert_read(test_brightness_state) await knx.receive_response(test_address_state, True) await knx.receive_response(test_brightness_state, (255,)) # # StateUpdater semaphore allows 2 concurrent requests await knx.assert_read(test_ct_state) await knx.receive_response(test_ct_state, (0xFF,)) # 100 % - 4000 K - 250 Mired knx.assert_state( "light.test", STATE_ON, brightness=255, supported_color_modes=[ColorMode.COLOR_TEMP], color_mode=ColorMode.COLOR_TEMP, color_temp=250, color_temp_kelvin=4000, ) # change color temperature from HA await hass.services.async_call( "light", "turn_on", { "entity_id": "light.test", ATTR_COLOR_TEMP_KELVIN: 3333, # 3333 Kelvin - 33.3 % - 0x55 }, blocking=True, ) await knx.assert_write(test_ct, (0x55,)) knx.assert_state( "light.test", STATE_ON, color_temp=300, color_temp_kelvin=3333, ) # change color temperature from KNX await knx.receive_write(test_ct_state, (0xE6,)) # 3901 Kelvin - 90.1 % - 256 Mired knx.assert_state( "light.test", STATE_ON, color_temp=256, color_temp_kelvin=3901, ) async def test_light_hs_color(hass: HomeAssistant, knx: KNXTestKit) -> None: """Test KNX light with hs color.""" test_address = "1/1/1" test_address_state = "1/1/2" test_brightness = "1/1/3" test_brightness_state = "1/1/4" test_hue = "1/1/5" test_hue_state = "1/1/6" test_sat = "1/1/7" test_sat_state = "1/1/8" await knx.setup_integration( { LightSchema.PLATFORM: [ { CONF_NAME: "test", KNX_ADDRESS: test_address, CONF_STATE_ADDRESS: test_address_state, LightSchema.CONF_BRIGHTNESS_ADDRESS: test_brightness, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_brightness_state, LightSchema.CONF_HUE_ADDRESS: test_hue, LightSchema.CONF_HUE_STATE_ADDRESS: test_hue_state, LightSchema.CONF_SATURATION_ADDRESS: test_sat, LightSchema.CONF_SATURATION_STATE_ADDRESS: test_sat_state, }, ] } ) # StateUpdater initialize state await knx.assert_read(test_address_state) await knx.assert_read(test_brightness_state) await knx.receive_response(test_address_state, True) await knx.receive_response(test_brightness_state, (255,)) # # StateUpdater semaphore allows 2 concurrent requests await knx.assert_read(test_hue_state) await knx.assert_read(test_sat_state) await knx.receive_response(test_hue_state, (0xFF,)) await knx.receive_response(test_sat_state, (0xFF,)) knx.assert_state( "light.test", STATE_ON, brightness=255, supported_color_modes=[ColorMode.HS], color_mode=ColorMode.HS, hs_color=(360, 100), ) # change color from HA - only hue await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "blue"}, # hue: 240, sat: 100 blocking=True, ) await knx.assert_write(test_hue, (0xAA,)) knx.assert_state("light.test", STATE_ON, brightness=255, hs_color=(240, 100)) # change color from HA - only saturation await hass.services.async_call( "light", "turn_on", { "entity_id": "light.test", ATTR_HS_COLOR: (240, 50), }, # hue: 60, sat: 12.157 blocking=True, ) await knx.assert_write(test_sat, (0x80,)) knx.assert_state("light.test", STATE_ON, brightness=255, hs_color=(240, 50)) # change color from HA - hue and sat await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "hotpink"}, # hue: 330, sat: 59 blocking=True, ) await knx.assert_write(test_hue, (0xEA,)) await knx.assert_write(test_sat, (0x96,)) knx.assert_state("light.test", STATE_ON, brightness=255, hs_color=(330, 59)) # change color and brightness from KNX await knx.receive_write(test_brightness, (0xB2,)) knx.assert_state("light.test", STATE_ON, brightness=178, hs_color=(330, 59)) await knx.receive_write(test_hue, (0x7D,)) knx.assert_state("light.test", STATE_ON, brightness=178, hs_color=(176, 59)) await knx.receive_write(test_sat, (0xD1,)) knx.assert_state("light.test", STATE_ON, brightness=178, hs_color=(176, 82)) async def test_light_xyy_color(hass: HomeAssistant, knx: KNXTestKit) -> None: """Test KNX light with xyy color.""" test_address = "1/1/1" test_address_state = "1/1/2" test_xyy = "1/1/5" test_xyy_state = "1/1/6" await knx.setup_integration( { LightSchema.PLATFORM: [ { CONF_NAME: "test", KNX_ADDRESS: test_address, CONF_STATE_ADDRESS: test_address_state, LightSchema.CONF_XYY_ADDRESS: test_xyy, LightSchema.CONF_XYY_STATE_ADDRESS: test_xyy_state, }, ] } ) # StateUpdater initialize state await knx.assert_read(test_address_state) await knx.assert_read(test_xyy_state) await knx.receive_response(test_address_state, True) await knx.receive_response(test_xyy_state, (0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x03)) knx.assert_state( "light.test", STATE_ON, brightness=204, supported_color_modes=[ColorMode.XY], color_mode=ColorMode.XY, xy_color=(0.8, 0.8), ) # change color and brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 139, ATTR_COLOR_NAME: "red"}, blocking=True, ) await knx.assert_write(test_xyy, (179, 116, 76, 139, 139, 3)) knx.assert_state("light.test", STATE_ON, brightness=139, xy_color=(0.701, 0.299)) # change brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 255}, blocking=True, ) await knx.assert_write(test_xyy, (0, 0, 0, 0, 255, 1)) knx.assert_state("light.test", STATE_ON, brightness=255, xy_color=(0.701, 0.299)) # change color from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "hotpink"}, blocking=True, ) await knx.assert_write(test_xyy, (120, 16, 63, 59, 0, 2)) knx.assert_state("light.test", STATE_ON, brightness=255, xy_color=(0.469, 0.247)) # change color and brightness from KNX await knx.receive_write(test_xyy, (0x85, 0x1E, 0x4F, 0x5C, 0x19, 0x03)) knx.assert_state("light.test", STATE_ON, brightness=25, xy_color=(0.52, 0.31)) # change brightness from KNX await knx.receive_write(test_xyy, (0x00, 0x00, 0x00, 0x00, 0x80, 0x01)) knx.assert_state("light.test", STATE_ON, brightness=128, xy_color=(0.52, 0.31)) # change color from KNX await knx.receive_write(test_xyy, (0x2E, 0x14, 0x40, 0x00, 0x00, 0x02)) knx.assert_state("light.test", STATE_ON, brightness=128, xy_color=(0.18, 0.25)) async def test_light_xyy_color_with_brightness( hass: HomeAssistant, knx: KNXTestKit ) -> None: """Test KNX light with xyy color and explicit brightness address.""" test_address = "1/1/1" test_address_state = "1/1/2" test_brightness = "1/1/3" test_brightness_state = "1/1/4" test_xyy = "1/1/5" test_xyy_state = "1/1/6" await knx.setup_integration( { LightSchema.PLATFORM: [ { CONF_NAME: "test", KNX_ADDRESS: test_address, CONF_STATE_ADDRESS: test_address_state, LightSchema.CONF_BRIGHTNESS_ADDRESS: test_brightness, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_brightness_state, LightSchema.CONF_XYY_ADDRESS: test_xyy, LightSchema.CONF_XYY_STATE_ADDRESS: test_xyy_state, }, ] } ) # StateUpdater initialize state await knx.assert_read(test_address_state) await knx.assert_read(test_brightness_state) await knx.receive_response(test_address_state, True) await knx.receive_response(test_brightness_state, (255,)) # # StateUpdater semaphore allows 2 concurrent requests await knx.assert_read(test_xyy_state) await knx.receive_response(test_xyy_state, (0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x03)) knx.assert_state( "light.test", STATE_ON, brightness=255, # brightness form xyy_color ignored when extra brightness GA is used supported_color_modes=[ColorMode.XY], color_mode=ColorMode.XY, xy_color=(0.8, 0.8), ) # change color from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "red"}, blocking=True, ) await knx.assert_write(test_xyy, (179, 116, 76, 139, 0, 2)) knx.assert_state("light.test", STATE_ON, brightness=255, xy_color=(0.701, 0.299)) # change brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 139}, blocking=True, ) await knx.assert_write(test_brightness, (0x8B,)) knx.assert_state("light.test", STATE_ON, brightness=139, xy_color=(0.701, 0.299)) # change color and brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 255, ATTR_COLOR_NAME: "hotpink"}, blocking=True, ) await knx.assert_write(test_xyy, (120, 16, 63, 59, 255, 3)) # brightness relies on brightness_state GA await knx.receive_write(test_brightness_state, (255,)) knx.assert_state("light.test", STATE_ON, brightness=255, xy_color=(0.469, 0.247)) # change color and brightness from KNX await knx.receive_write(test_xyy, (0x85, 0x1E, 0x4F, 0x5C, 0x00, 0x02)) knx.assert_state("light.test", STATE_ON, brightness=255, xy_color=(0.52, 0.31)) await knx.receive_write(test_brightness, (21,)) knx.assert_state("light.test", STATE_ON, brightness=21, xy_color=(0.52, 0.31)) async def test_light_rgb_individual(hass: HomeAssistant, knx: KNXTestKit) -> None: """Test KNX light with rgb color in individual GAs.""" test_red = "1/1/3" test_red_state = "1/1/4" test_green = "1/1/5" test_green_state = "1/1/6" test_blue = "1/1/7" test_blue_state = "1/1/8" await knx.setup_integration( { LightSchema.PLATFORM: [ { CONF_NAME: "test", LightSchema.CONF_INDIVIDUAL_COLORS: { LightSchema.CONF_RED: { LightSchema.CONF_BRIGHTNESS_ADDRESS: test_red, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_red_state, }, LightSchema.CONF_GREEN: { LightSchema.CONF_BRIGHTNESS_ADDRESS: test_green, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_green_state, }, LightSchema.CONF_BLUE: { LightSchema.CONF_BRIGHTNESS_ADDRESS: test_blue, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_blue_state, }, }, }, ] } ) # StateUpdater initialize state await knx.assert_read(test_red_state) await knx.assert_read(test_green_state) await knx.receive_response(test_red_state, (255,)) await knx.receive_response(test_green_state, (255,)) # # StateUpdater semaphore allows 2 concurrent requests await knx.assert_read(test_blue_state) await knx.receive_response(test_blue_state, (255,)) knx.assert_state( "light.test", STATE_ON, brightness=255, supported_color_modes=[ColorMode.RGB], color_mode=ColorMode.RGB, rgb_color=(255, 255, 255), ) # change color from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "red"}, blocking=True, ) await knx.assert_write(test_red, (255,)) await knx.assert_write(test_green, (0,)) await knx.assert_write(test_blue, (0,)) knx.assert_state("light.test", STATE_ON, brightness=255, rgb_color=(255, 0, 0)) # change brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 200}, blocking=True, ) await knx.assert_write(test_red, (200,)) await knx.assert_write(test_green, (0,)) await knx.assert_write(test_blue, (0,)) knx.assert_state("light.test", STATE_ON, brightness=200, rgb_color=(255, 0, 0)) # change only color, keep brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "hotpink"}, blocking=True, ) await knx.assert_write(test_red, (200,)) await knx.assert_write(test_green, (82,)) await knx.assert_write(test_blue, (141,)) knx.assert_state("light.test", STATE_ON, brightness=200, rgb_color=(255, 105, 180)) # change color and brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 100, ATTR_COLOR_NAME: "yellow"}, blocking=True, ) await knx.assert_write(test_red, (100,)) await knx.assert_write(test_green, (100,)) await knx.assert_write(test_blue, (0,)) knx.assert_state("light.test", STATE_ON, brightness=100, rgb_color=(255, 255, 0)) # turn OFF from KNX await knx.receive_write(test_red, (0,)) await knx.receive_write(test_green, (0,)) await knx.receive_write(test_blue, (0,)) knx.assert_state("light.test", STATE_OFF) # turn ON from KNX await knx.receive_write(test_red, (0,)) await knx.receive_write(test_green, (180,)) await knx.receive_write(test_blue, (0,)) knx.assert_state("light.test", STATE_ON, brightness=180, rgb_color=(0, 255, 0)) # turn OFF from HA await hass.services.async_call( "light", "turn_off", {"entity_id": "light.test"}, blocking=True, ) await knx.assert_write(test_red, (0,)) await knx.assert_write(test_green, (0,)) await knx.assert_write(test_blue, (0,)) knx.assert_state("light.test", STATE_OFF) # turn ON from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test"}, blocking=True, ) # color will not be restored - defaults to white await knx.assert_write(test_red, (255,)) await knx.assert_write(test_green, (255,)) await knx.assert_write(test_blue, (255,)) knx.assert_state("light.test", STATE_ON, brightness=255, rgb_color=(255, 255, 255)) # turn ON with brightness only from HA - defaults to white await knx.receive_write(test_red, (0,)) await knx.receive_write(test_green, (0,)) await knx.receive_write(test_blue, (0,)) await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 45}, blocking=True, ) await knx.assert_write(test_red, (45,)) await knx.assert_write(test_green, (45,)) await knx.assert_write(test_blue, (45,)) async def test_light_rgbw_individual( hass: HomeAssistant, knx: KNXTestKit, freezer: FrozenDateTimeFactory ) -> None: """Test KNX light with rgbw color in individual GAs.""" test_red = "1/1/3" test_red_state = "1/1/4" test_green = "1/1/5" test_green_state = "1/1/6" test_blue = "1/1/7" test_blue_state = "1/1/8" test_white = "1/1/9" test_white_state = "1/1/10" await knx.setup_integration( { LightSchema.PLATFORM: [ { CONF_NAME: "test", LightSchema.CONF_INDIVIDUAL_COLORS: { LightSchema.CONF_RED: { LightSchema.CONF_BRIGHTNESS_ADDRESS: test_red, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_red_state, }, LightSchema.CONF_GREEN: { LightSchema.CONF_BRIGHTNESS_ADDRESS: test_green, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_green_state, }, LightSchema.CONF_BLUE: { LightSchema.CONF_BRIGHTNESS_ADDRESS: test_blue, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_blue_state, }, LightSchema.CONF_WHITE: { LightSchema.CONF_BRIGHTNESS_ADDRESS: test_white, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_white_state, }, }, }, ] } ) # StateUpdater initialize state await knx.assert_read(test_red_state) await knx.assert_read(test_green_state) await knx.receive_response(test_red_state, (0,)) await knx.receive_response(test_green_state, (0,)) # # StateUpdater semaphore allows 2 concurrent requests await knx.assert_read(test_blue_state) await knx.assert_read(test_white_state) await knx.receive_response(test_blue_state, (0,)) await knx.receive_response(test_white_state, (255,)) knx.assert_state( "light.test", STATE_ON, brightness=255, supported_color_modes=[ColorMode.RGBW], color_mode=ColorMode.RGBW, rgbw_color=(0, 0, 0, 255), ) # change color from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "red"}, blocking=True, ) await knx.assert_write(test_red, (255,)) await knx.assert_write(test_green, (0,)) await knx.assert_write(test_blue, (0,)) await knx.assert_write(test_white, (0,)) knx.assert_state("light.test", STATE_ON, brightness=255, rgbw_color=(255, 0, 0, 0)) # change brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 200}, blocking=True, ) await knx.assert_write(test_red, (200,)) await knx.assert_write(test_green, (0,)) await knx.assert_write(test_blue, (0,)) await knx.assert_write(test_white, (0,)) knx.assert_state("light.test", STATE_ON, brightness=200, rgbw_color=(255, 0, 0, 0)) # change only color, keep brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "hotpink"}, blocking=True, ) await knx.assert_write(test_red, (200,)) await knx.assert_write(test_green, (0,)) await knx.assert_write(test_blue, (100,)) await knx.assert_write(test_white, (139,)) knx.assert_state( "light.test", STATE_ON, brightness=200, rgb_color=(255, 104, 179), # minor rounding error - expected (255, 105, 180) rgbw_color=(255, 0, 127, 177), # expected (255, 0, 128, 178) ) # change color and brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 100, ATTR_COLOR_NAME: "yellow"}, blocking=True, ) await knx.assert_write(test_red, (100,)) await knx.assert_write(test_green, (100,)) await knx.assert_write(test_blue, (0,)) await knx.assert_write(test_white, (0,)) knx.assert_state( "light.test", STATE_ON, brightness=100, rgbw_color=(255, 255, 0, 0) ) # turn OFF from KNX await knx.receive_write(test_red, (0,)) await knx.receive_write(test_green, (0,)) # # individual color debounce takes 0.2 seconds if not all 4 addresses received knx.assert_state("light.test", STATE_ON) freezer.tick(timedelta(seconds=XknxLight.DEBOUNCE_TIMEOUT)) async_fire_time_changed(hass) await knx.xknx.task_registry.block_till_done() knx.assert_state("light.test", STATE_OFF) # turn ON from KNX await knx.receive_write(test_red, (0,)) await knx.receive_write(test_green, (180,)) await knx.receive_write(test_blue, (0,)) await knx.receive_write(test_white, (0,)) knx.assert_state("light.test", STATE_ON, brightness=180, rgbw_color=(0, 255, 0, 0)) # turn OFF from HA await hass.services.async_call( "light", "turn_off", {"entity_id": "light.test"}, blocking=True, ) await knx.assert_write(test_red, (0,)) await knx.assert_write(test_green, (0,)) await knx.assert_write(test_blue, (0,)) await knx.assert_write(test_white, (0,)) knx.assert_state("light.test", STATE_OFF) # turn ON from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test"}, blocking=True, ) # color will not be restored - defaults to 100% on all channels await knx.assert_write(test_red, (255,)) await knx.assert_write(test_green, (255,)) await knx.assert_write(test_blue, (255,)) await knx.assert_write(test_white, (255,)) knx.assert_state( "light.test", STATE_ON, brightness=255, rgbw_color=(255, 255, 255, 255) ) # turn ON with brightness only from HA - defaults to white await knx.receive_write(test_red, (0,)) await knx.receive_write(test_green, (0,)) await knx.receive_write(test_blue, (0,)) await knx.receive_write(test_white, (0,)) await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 45}, blocking=True, ) await knx.assert_write(test_red, (0,)) await knx.assert_write(test_green, (0,)) await knx.assert_write(test_blue, (0,)) await knx.assert_write(test_white, (45,)) async def test_light_rgb(hass: HomeAssistant, knx: KNXTestKit) -> None: """Test KNX light with rgb color.""" test_address = "1/1/1" test_address_state = "1/1/2" test_rgb = "1/1/5" test_rgb_state = "1/1/6" await knx.setup_integration( { LightSchema.PLATFORM: [ { CONF_NAME: "test", KNX_ADDRESS: test_address, CONF_STATE_ADDRESS: test_address_state, LightSchema.CONF_COLOR_ADDRESS: test_rgb, LightSchema.CONF_COLOR_STATE_ADDRESS: test_rgb_state, }, ] } ) # StateUpdater initialize state await knx.assert_read(test_address_state) await knx.assert_read(test_rgb_state) await knx.receive_response(test_address_state, True) await knx.receive_response(test_rgb_state, (0xFF, 0xFF, 0xFF)) knx.assert_state( "light.test", STATE_ON, brightness=255, supported_color_modes=[ColorMode.RGB], color_mode=ColorMode.RGB, rgb_color=(255, 255, 255), ) # change color from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "red"}, blocking=True, ) await knx.assert_write(test_rgb, (255, 0, 0)) knx.assert_state("light.test", STATE_ON, brightness=255, rgb_color=(255, 0, 0)) # change brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 200}, blocking=True, ) await knx.assert_write(test_rgb, (200, 0, 0)) knx.assert_state("light.test", STATE_ON, brightness=200, rgb_color=(255, 0, 0)) # change color, keep brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "hotpink"}, blocking=True, ) await knx.assert_write(test_rgb, (200, 82, 141)) knx.assert_state( "light.test", STATE_ON, brightness=200, rgb_color=(255, 105, 180), ) # change color and brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 100, ATTR_COLOR_NAME: "yellow"}, blocking=True, ) await knx.assert_write(test_rgb, (100, 100, 0)) knx.assert_state("light.test", STATE_ON, brightness=100, rgb_color=(255, 255, 0)) # turn OFF from KNX await knx.receive_write(test_address_state, False) knx.assert_state("light.test", STATE_OFF) # receive color update from KNX - still OFF await knx.receive_write(test_rgb, (0, 180, 0)) knx.assert_state("light.test", STATE_OFF) # turn ON from KNX - include color update await knx.receive_write(test_address_state, True) knx.assert_state("light.test", STATE_ON, brightness=180, rgb_color=(0, 255, 0)) # turn OFF from HA await hass.services.async_call( "light", "turn_off", {"entity_id": "light.test"}, blocking=True, ) await knx.assert_write(test_address, False) knx.assert_state("light.test", STATE_OFF) # turn ON from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test"}, blocking=True, ) # color will be restored in no other state was received await knx.assert_write(test_address, True) knx.assert_state("light.test", STATE_ON, brightness=180, rgb_color=(0, 255, 0)) async def test_light_rgbw(hass: HomeAssistant, knx: KNXTestKit) -> None: """Test KNX light with rgbw color.""" test_address = "1/1/1" test_address_state = "1/1/2" test_rgbw = "1/1/5" test_rgbw_state = "1/1/6" await knx.setup_integration( { LightSchema.PLATFORM: [ { CONF_NAME: "test", KNX_ADDRESS: test_address, CONF_STATE_ADDRESS: test_address_state, LightSchema.CONF_RGBW_ADDRESS: test_rgbw, LightSchema.CONF_RGBW_STATE_ADDRESS: test_rgbw_state, }, ] } ) # StateUpdater initialize state await knx.assert_read(test_address_state) await knx.assert_read(test_rgbw_state) await knx.receive_response(test_address_state, True) await knx.receive_response(test_rgbw_state, (0xFF, 0x65, 0x66, 0x67, 0x00, 0x0F)) knx.assert_state( "light.test", STATE_ON, brightness=255, supported_color_modes=[ColorMode.RGBW], color_mode=ColorMode.RGBW, rgbw_color=(255, 101, 102, 103), ) # change color from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "red"}, blocking=True, ) await knx.assert_write(test_rgbw, (0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F)) knx.assert_state("light.test", STATE_ON, brightness=255, rgbw_color=(255, 0, 0, 0)) # change brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 200}, blocking=True, ) await knx.assert_write(test_rgbw, (0xC8, 0x00, 0x00, 0x00, 0x00, 0x0F)) knx.assert_state("light.test", STATE_ON, brightness=200, rgbw_color=(255, 0, 0, 0)) # change color, keep brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "hotpink"}, blocking=True, ) await knx.assert_write(test_rgbw, (200, 0, 100, 139, 0x00, 0x0F)) knx.assert_state( "light.test", STATE_ON, brightness=200, rgb_color=(255, 104, 179), # minor rounding error - expected (255, 105, 180) rgbw_color=(255, 0, 127, 177), # expected (255, 0, 128, 178) ) # change color and brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 100, ATTR_COLOR_NAME: "yellow"}, blocking=True, ) await knx.assert_write(test_rgbw, (100, 100, 0, 0, 0x00, 0x0F)) knx.assert_state( "light.test", STATE_ON, brightness=100, rgbw_color=(255, 255, 0, 0) ) # turn OFF from KNX await knx.receive_write(test_address_state, False) knx.assert_state("light.test", STATE_OFF) # receive color update from KNX - still OFF await knx.receive_write(test_rgbw, (0, 180, 0, 0, 0x00, 0x0F)) knx.assert_state("light.test", STATE_OFF) # turn ON from KNX - include color update await knx.receive_write(test_address_state, True) knx.assert_state("light.test", STATE_ON, brightness=180, rgbw_color=(0, 255, 0, 0)) # turn OFF from HA await hass.services.async_call( "light", "turn_off", {"entity_id": "light.test"}, blocking=True, ) await knx.assert_write(test_address, False) knx.assert_state("light.test", STATE_OFF) # turn ON from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test"}, blocking=True, ) # color will be restored if no other state was received await knx.assert_write(test_address, True) knx.assert_state("light.test", STATE_ON, brightness=180, rgbw_color=(0, 255, 0, 0)) async def test_light_rgbw_brightness(hass: HomeAssistant, knx: KNXTestKit) -> None: """Test KNX light with rgbw color with dedicated brightness.""" test_address = "1/1/1" test_address_state = "1/1/2" test_brightness = "1/1/3" test_brightness_state = "1/1/4" test_rgbw = "1/1/5" test_rgbw_state = "1/1/6" await knx.setup_integration( { LightSchema.PLATFORM: [ { CONF_NAME: "test", KNX_ADDRESS: test_address, CONF_STATE_ADDRESS: test_address_state, LightSchema.CONF_BRIGHTNESS_ADDRESS: test_brightness, LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS: test_brightness_state, LightSchema.CONF_RGBW_ADDRESS: test_rgbw, LightSchema.CONF_RGBW_STATE_ADDRESS: test_rgbw_state, }, ] } ) # StateUpdater initialize state await knx.assert_read(test_address_state) await knx.assert_read(test_brightness_state) await knx.receive_response(test_address_state, True) await knx.receive_response(test_brightness_state, (0xFF,)) await knx.assert_read(test_rgbw_state) await knx.receive_response(test_rgbw_state, (0xFF, 0x65, 0x66, 0x67, 0x00, 0x0F)) knx.assert_state( "light.test", STATE_ON, brightness=255, supported_color_modes=[ColorMode.RGBW], color_mode=ColorMode.RGBW, rgbw_color=(255, 101, 102, 103), ) # change color from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_NAME: "red"}, blocking=True, ) await knx.assert_write(test_rgbw, (0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F)) knx.assert_state("light.test", STATE_ON, brightness=255, rgbw_color=(255, 0, 0, 0)) # # update from dedicated brightness state await knx.receive_write(test_brightness_state, (0xF0,)) knx.assert_state("light.test", STATE_ON, brightness=240, rgbw_color=(255, 0, 0, 0)) # single encoded brightness - at least one primary color = 255 # # change brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 128}, blocking=True, ) await knx.assert_write(test_brightness, (128,)) knx.assert_state("light.test", STATE_ON, brightness=128, rgbw_color=(255, 0, 0, 0)) # # change color and brightness from HA await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_BRIGHTNESS: 128, ATTR_COLOR_NAME: "hotpink"}, blocking=True, ) await knx.assert_write(test_rgbw, (255, 0, 128, 178, 0x00, 0x0F)) await knx.assert_write(test_brightness, (128,)) knx.assert_state( "light.test", STATE_ON, brightness=128, rgb_color=(255, 105, 180), rgbw_color=(255, 0, 128, 178), ) # doubly encoded brightness # brightness is handled by dedicated brightness address only # # from dedicated rgbw state await knx.receive_write(test_rgbw_state, (0xC8, 0x00, 0x00, 0x00, 0x00, 0x0F)) knx.assert_state("light.test", STATE_ON, brightness=128, rgbw_color=(200, 0, 0, 0)) # # from HA - only color await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_RGBW_COLOR: (20, 30, 40, 50)}, blocking=True, ) await knx.assert_write(test_rgbw, (20, 30, 40, 50, 0x00, 0x0F)) knx.assert_state( "light.test", STATE_ON, brightness=128, rgbw_color=(20, 30, 40, 50) ) # # from HA - brightness and color await hass.services.async_call( "light", "turn_on", { "entity_id": "light.test", ATTR_BRIGHTNESS: 50, ATTR_RGBW_COLOR: (100, 200, 55, 12), }, blocking=True, ) await knx.assert_write(test_rgbw, (100, 200, 55, 12, 0x00, 0x0F)) await knx.assert_write(test_brightness, (50,)) knx.assert_state( "light.test", STATE_ON, brightness=50, rgbw_color=(100, 200, 55, 12) ) async def test_light_ui_create( hass: HomeAssistant, knx: KNXTestKit, create_ui_entity: KnxEntityGenerator, ) -> None: """Test creating a light.""" await knx.setup_integration({}) await create_ui_entity( platform=Platform.LIGHT, entity_data={"name": "test"}, knx_data={ "ga_switch": {"write": "1/1/1", "state": "2/2/2"}, "_light_color_mode_schema": "default", "sync_state": True, }, ) # created entity sends read-request to KNX bus await knx.assert_read("2/2/2") await knx.receive_response("2/2/2", True) knx.assert_state( "light.test", STATE_ON, supported_color_modes=[ColorMode.ONOFF], color_mode=ColorMode.ONOFF, ) @pytest.mark.parametrize( ("color_temp_mode", "raw_ct"), [ ("7.600", (0x10, 0x68)), ("9", (0x46, 0x69)), ("5.001", (0x74,)), ], ) async def test_light_ui_color_temp( hass: HomeAssistant, knx: KNXTestKit, create_ui_entity: KnxEntityGenerator, color_temp_mode: str, raw_ct: tuple[int, ...], ) -> None: """Test creating a color-temp light.""" await knx.setup_integration({}) await create_ui_entity( platform=Platform.LIGHT, entity_data={"name": "test"}, knx_data={ "ga_switch": {"write": "1/1/1", "state": "2/2/2"}, "ga_color_temp": { "write": "3/3/3", "dpt": color_temp_mode, }, "_light_color_mode_schema": "default", "sync_state": True, }, ) await knx.assert_read("2/2/2", True) await hass.services.async_call( "light", "turn_on", {"entity_id": "light.test", ATTR_COLOR_TEMP_KELVIN: 4200}, blocking=True, ) await knx.assert_write("3/3/3", raw_ct) knx.assert_state( "light.test", STATE_ON, supported_color_modes=[ColorMode.COLOR_TEMP], color_mode=ColorMode.COLOR_TEMP, color_temp_kelvin=pytest.approx(4200, abs=1), ) async def test_light_ui_multi_mode( hass: HomeAssistant, knx: KNXTestKit, create_ui_entity: KnxEntityGenerator, ) -> None: """Test creating a light with multiple color modes.""" await knx.setup_integration({}) await create_ui_entity( platform=Platform.LIGHT, entity_data={"name": "test"}, knx_data={ "color_temp_min": 2700, "color_temp_max": 6000, "_light_color_mode_schema": "default", "ga_switch": { "write": "1/1/1", "passive": [], "state": "2/2/2", }, "sync_state": True, "ga_brightness": { "write": "0/6/0", "state": "0/6/1", "passive": [], }, "ga_color_temp": { "write": "0/6/2", "dpt": "7.600", "state": "0/6/3", "passive": [], }, "ga_color": { "write": "0/6/4", "dpt": "251.600", "state": "0/6/5", "passive": [], }, }, ) await knx.assert_read("2/2/2", True) await knx.assert_read("0/6/1", (0xFF,)) await knx.assert_read("0/6/5", (0xFF, 0x65, 0x66, 0x67, 0x00, 0x0F)) await knx.assert_read("0/6/3", (0x12, 0x34)) await hass.services.async_call( "light", "turn_on", { "entity_id": "light.test", ATTR_COLOR_NAME: "hotpink", }, blocking=True, ) await knx.assert_write("0/6/4", (255, 0, 128, 178, 0, 15)) knx.assert_state( "light.test", STATE_ON, brightness=255, color_temp_kelvin=None, rgbw_color=(255, 0, 128, 178), supported_color_modes=[ ColorMode.COLOR_TEMP, ColorMode.RGBW, ], color_mode=ColorMode.RGBW, ) await hass.services.async_call( "light", "turn_on", { "entity_id": "light.test", ATTR_COLOR_TEMP_KELVIN: 4200, }, blocking=True, ) await knx.assert_write("0/6/2", (0x10, 0x68)) knx.assert_state( "light.test", STATE_ON, brightness=255, color_temp_kelvin=4200, rgbw_color=None, supported_color_modes=[ ColorMode.COLOR_TEMP, ColorMode.RGBW, ], color_mode=ColorMode.COLOR_TEMP, ) async def test_light_ui_load( hass: HomeAssistant, knx: KNXTestKit, load_config_store: None, entity_registry: er.EntityRegistry, ) -> None: """Test loading a light from storage.""" await knx.setup_integration({}) await knx.assert_read("1/0/21", response=True, ignore_order=True) # unrelated switch in config store await knx.assert_read("1/0/45", response=True, ignore_order=True) knx.assert_state( "light.test", STATE_ON, supported_color_modes=[ColorMode.ONOFF], color_mode=ColorMode.ONOFF, ) entity = entity_registry.async_get("light.test") assert entity.entity_category is EntityCategory.CONFIG