"""Test the Kuler Sky lights.""" from unittest.mock import MagicMock, patch import pykulersky import pytest from pytest import approx from homeassistant.components.kulersky.const import ( DATA_ADDRESSES, DATA_DISCOVERY_SUBSCRIPTION, DOMAIN, ) from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_MODE, ATTR_HS_COLOR, ATTR_RGB_COLOR, ATTR_RGBW_COLOR, ATTR_SUPPORTED_COLOR_MODES, ATTR_XY_COLOR, COLOR_MODE_RGBW, SCAN_INTERVAL, ) from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, ATTR_SUPPORTED_FEATURES, STATE_OFF, STATE_ON, STATE_UNAVAILABLE, ) import homeassistant.util.dt as dt_util from tests.common import MockConfigEntry, async_fire_time_changed @pytest.fixture async def mock_entry(hass): """Create a mock light entity.""" return MockConfigEntry(domain=DOMAIN) @pytest.fixture async def mock_light(hass, mock_entry): """Create a mock light entity.""" light = MagicMock(spec=pykulersky.Light) light.address = "AA:BB:CC:11:22:33" light.name = "Bedroom" light.connect.return_value = True light.get_color.return_value = (0, 0, 0, 0) with patch( "homeassistant.components.kulersky.light.pykulersky.discover", return_value=[light], ): mock_entry.add_to_hass(hass) await hass.config_entries.async_setup(mock_entry.entry_id) await hass.async_block_till_done() assert light.connect.called yield light async def test_init(hass, mock_light): """Test platform setup.""" state = hass.states.get("light.bedroom") assert state.state == STATE_OFF assert dict(state.attributes) == { ATTR_FRIENDLY_NAME: "Bedroom", ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_RGBW], ATTR_SUPPORTED_FEATURES: 0, } with patch.object(hass.loop, "stop"): await hass.async_stop() await hass.async_block_till_done() assert mock_light.disconnect.called async def test_remove_entry(hass, mock_light, mock_entry): """Test platform setup.""" assert hass.data[DOMAIN][DATA_ADDRESSES] == {"AA:BB:CC:11:22:33"} assert DATA_DISCOVERY_SUBSCRIPTION in hass.data[DOMAIN] await hass.config_entries.async_remove(mock_entry.entry_id) assert mock_light.disconnect.called assert DOMAIN not in hass.data async def test_remove_entry_exceptions_caught(hass, mock_light, mock_entry): """Assert that disconnect exceptions are caught.""" mock_light.disconnect.side_effect = pykulersky.PykulerskyException("Mock error") await hass.config_entries.async_remove(mock_entry.entry_id) assert mock_light.disconnect.called async def test_update_exception(hass, mock_light): """Test platform setup.""" mock_light.get_color.side_effect = pykulersky.PykulerskyException await hass.helpers.entity_component.async_update_entity("light.bedroom") state = hass.states.get("light.bedroom") assert state is not None assert state.state == STATE_UNAVAILABLE async def test_light_turn_on(hass, mock_light): """Test KulerSkyLight turn_on.""" mock_light.get_color.return_value = (255, 255, 255, 255) await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: "light.bedroom"}, blocking=True, ) await hass.async_block_till_done() mock_light.set_color.assert_called_with(255, 255, 255, 255) mock_light.get_color.return_value = (50, 50, 50, 50) await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: "light.bedroom", ATTR_BRIGHTNESS: 50}, blocking=True, ) await hass.async_block_till_done() mock_light.set_color.assert_called_with(50, 50, 50, 50) mock_light.get_color.return_value = (50, 25, 13, 6) await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: "light.bedroom", ATTR_RGBW_COLOR: (255, 128, 64, 32)}, blocking=True, ) await hass.async_block_till_done() mock_light.set_color.assert_called_with(50, 25, 13, 6) # RGB color is converted to RGBW by assigning the white component to the white # channel, see color_rgb_to_rgbw mock_light.get_color.return_value = (0, 17, 50, 17) await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: "light.bedroom", ATTR_RGB_COLOR: (64, 128, 255)}, blocking=True, ) await hass.async_block_till_done() mock_light.set_color.assert_called_with(0, 17, 50, 17) # HS color is converted to RGBW by assigning the white component to the white # channel, see color_rgb_to_rgbw mock_light.get_color.return_value = (50, 41, 0, 50) await hass.services.async_call( "light", "turn_on", {ATTR_ENTITY_ID: "light.bedroom", ATTR_HS_COLOR: (50, 50)}, blocking=True, ) await hass.async_block_till_done() mock_light.set_color.assert_called_with(50, 41, 0, 50) async def test_light_turn_off(hass, mock_light): """Test KulerSkyLight turn_on.""" mock_light.get_color.return_value = (0, 0, 0, 0) await hass.services.async_call( "light", "turn_off", {ATTR_ENTITY_ID: "light.bedroom"}, blocking=True, ) await hass.async_block_till_done() mock_light.set_color.assert_called_with(0, 0, 0, 0) async def test_light_update(hass, mock_light): """Test KulerSkyLight update.""" utcnow = dt_util.utcnow() state = hass.states.get("light.bedroom") assert state.state == STATE_OFF assert dict(state.attributes) == { ATTR_FRIENDLY_NAME: "Bedroom", ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_RGBW], ATTR_SUPPORTED_FEATURES: 0, } # Test an exception during discovery mock_light.get_color.side_effect = pykulersky.PykulerskyException("TEST") utcnow = utcnow + SCAN_INTERVAL async_fire_time_changed(hass, utcnow) await hass.async_block_till_done() state = hass.states.get("light.bedroom") assert state.state == STATE_UNAVAILABLE assert dict(state.attributes) == { ATTR_FRIENDLY_NAME: "Bedroom", ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_RGBW], ATTR_SUPPORTED_FEATURES: 0, } mock_light.get_color.side_effect = None mock_light.get_color.return_value = (80, 160, 255, 0) utcnow = utcnow + SCAN_INTERVAL async_fire_time_changed(hass, utcnow) await hass.async_block_till_done() state = hass.states.get("light.bedroom") assert state.state == STATE_ON assert dict(state.attributes) == { ATTR_FRIENDLY_NAME: "Bedroom", ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_RGBW], ATTR_SUPPORTED_FEATURES: 0, ATTR_COLOR_MODE: COLOR_MODE_RGBW, ATTR_BRIGHTNESS: 255, ATTR_HS_COLOR: (approx(212.571), approx(68.627)), ATTR_RGB_COLOR: (80, 160, 255), ATTR_RGBW_COLOR: (80, 160, 255, 0), ATTR_XY_COLOR: (approx(0.17), approx(0.193)), } mock_light.get_color.side_effect = None mock_light.get_color.return_value = (80, 160, 200, 255) utcnow = utcnow + SCAN_INTERVAL async_fire_time_changed(hass, utcnow) await hass.async_block_till_done() state = hass.states.get("light.bedroom") assert state.state == STATE_ON assert dict(state.attributes) == { ATTR_FRIENDLY_NAME: "Bedroom", ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_RGBW], ATTR_SUPPORTED_FEATURES: 0, ATTR_COLOR_MODE: COLOR_MODE_RGBW, ATTR_BRIGHTNESS: 255, ATTR_HS_COLOR: (approx(199.701), approx(26.275)), ATTR_RGB_COLOR: (188, 233, 255), ATTR_RGBW_COLOR: (80, 160, 200, 255), ATTR_XY_COLOR: (approx(0.259), approx(0.306)), } mock_light.get_color.side_effect = None mock_light.get_color.return_value = (80, 160, 200, 240) utcnow = utcnow + SCAN_INTERVAL async_fire_time_changed(hass, utcnow) await hass.async_block_till_done() state = hass.states.get("light.bedroom") assert state.state == STATE_ON assert dict(state.attributes) == { ATTR_FRIENDLY_NAME: "Bedroom", ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_RGBW], ATTR_SUPPORTED_FEATURES: 0, ATTR_COLOR_MODE: COLOR_MODE_RGBW, ATTR_BRIGHTNESS: 240, ATTR_HS_COLOR: (approx(200.0), approx(27.059)), ATTR_RGB_COLOR: (186, 232, 255), ATTR_RGBW_COLOR: (85, 170, 212, 255), ATTR_XY_COLOR: (approx(0.257), approx(0.305)), }