diff --git a/.coveragerc b/.coveragerc index cdbfd57024b..01c36d8a836 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1109,7 +1109,6 @@ omit = homeassistant/components/shelly/climate.py homeassistant/components/shelly/coordinator.py homeassistant/components/shelly/entity.py - homeassistant/components/shelly/light.py homeassistant/components/shelly/number.py homeassistant/components/shelly/sensor.py homeassistant/components/shelly/utils.py diff --git a/homeassistant/components/shelly/light.py b/homeassistant/components/shelly/light.py index 6c479ebc63f..dda9a41bb89 100644 --- a/homeassistant/components/shelly/light.py +++ b/homeassistant/components/shelly/light.py @@ -7,7 +7,7 @@ from aioshelly.block_device import Block from homeassistant.components.light import ( ATTR_BRIGHTNESS, - ATTR_COLOR_TEMP, + ATTR_COLOR_TEMP_KELVIN, ATTR_EFFECT, ATTR_RGB_COLOR, ATTR_RGBW_COLOR, @@ -20,10 +20,6 @@ from homeassistant.components.light import ( from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.util.color import ( - color_temperature_kelvin_to_mired, - color_temperature_mired_to_kelvin, -) from .const import ( DUAL_MODE_LIGHT_MODELS, @@ -49,10 +45,6 @@ from .utils import ( is_rpc_channel_type_light, ) -MIRED_MAX_VALUE_WHITE = color_temperature_kelvin_to_mired(KELVIN_MIN_VALUE_WHITE) -MIRED_MIN_VALUE = color_temperature_kelvin_to_mired(KELVIN_MAX_VALUE) -MIRED_MAX_VALUE_COLOR = color_temperature_kelvin_to_mired(KELVIN_MIN_VALUE_COLOR) - async def async_setup_entry( hass: HomeAssistant, @@ -133,14 +125,11 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): super().__init__(coordinator, block) self.control_result: dict[str, Any] | None = None self._attr_supported_color_modes = set() - self._attr_min_mireds = MIRED_MIN_VALUE - self._min_kelvin: int = KELVIN_MIN_VALUE_WHITE - self._attr_max_mireds = MIRED_MAX_VALUE_WHITE - self._max_kelvin: int = KELVIN_MAX_VALUE + self._attr_min_color_temp_kelvin = KELVIN_MIN_VALUE_WHITE + self._attr_max_color_temp_kelvin = KELVIN_MAX_VALUE if hasattr(block, "red") and hasattr(block, "green") and hasattr(block, "blue"): - self._attr_max_mireds = MIRED_MAX_VALUE_COLOR - self._min_kelvin = KELVIN_MIN_VALUE_COLOR + self._attr_min_color_temp_kelvin = KELVIN_MIN_VALUE_COLOR if coordinator.model in RGBW_MODELS: self._attr_supported_color_modes.add(ColorMode.RGBW) else: @@ -248,23 +237,20 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): return (*self.rgb_color, white) @property - def color_temp(self) -> int: - """Return the CT color value in mireds.""" + def color_temp_kelvin(self) -> int: + """Return the CT color value in kelvin.""" + color_temp = cast(int, self.block.colorTemp) if self.control_result: color_temp = self.control_result["temp"] - else: - color_temp = self.block.colorTemp - color_temp = min(self._max_kelvin, max(self._min_kelvin, color_temp)) - - return int(color_temperature_kelvin_to_mired(color_temp)) + return min( + self.max_color_temp_kelvin, + max(self.min_color_temp_kelvin, color_temp), + ) @property def effect_list(self) -> list[str] | None: """Return the list of supported effects.""" - if not self.supported_features & LightEntityFeature.EFFECT: - return None - if self.coordinator.model == "SHBLB-1": return list(SHBLB_1_RGB_EFFECTS.values()) @@ -273,9 +259,6 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): @property def effect(self) -> str | None: """Return the current effect.""" - if not self.supported_features & LightEntityFeature.EFFECT: - return None - if self.control_result: effect_index = self.control_result["effect"] else: @@ -309,12 +292,19 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): if hasattr(self.block, "brightness"): params["brightness"] = brightness_pct - if ATTR_COLOR_TEMP in kwargs and ColorMode.COLOR_TEMP in supported_color_modes: - color_temp = color_temperature_mired_to_kelvin(kwargs[ATTR_COLOR_TEMP]) - color_temp = min(self._max_kelvin, max(self._min_kelvin, color_temp)) + if ( + ATTR_COLOR_TEMP_KELVIN in kwargs + and ColorMode.COLOR_TEMP in supported_color_modes + ): # Color temperature change - used only in white mode, switch device mode to white + color_temp = kwargs[ATTR_COLOR_TEMP_KELVIN] set_mode = "white" - params["temp"] = int(color_temp) + params["temp"] = int( + min( + self.max_color_temp_kelvin, + max(self.min_color_temp_kelvin, color_temp), + ) + ) if ATTR_RGB_COLOR in kwargs and ColorMode.RGB in supported_color_modes: # Color channels change - used only in color mode, switch device mode to color @@ -328,7 +318,7 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): ATTR_RGBW_COLOR ] - if ATTR_EFFECT in kwargs and ATTR_COLOR_TEMP not in kwargs: + if ATTR_EFFECT in kwargs and ATTR_COLOR_TEMP_KELVIN not in kwargs: # Color effect change - used only in color mode, switch device mode to color set_mode = "color" if self.coordinator.model == "SHBLB-1": diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index 8890201ad6d..cca4aebb9ea 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -25,6 +25,36 @@ MOCK_SETTINGS = { "rollers": [{"positioning": True}], } + +def mock_light_set_state( + turn="on", + mode="color", + red=45, + green=55, + blue=65, + white=70, + gain=19, + temp=4050, + brightness=50, + effect=0, + transition=0, +): + """Mock light block set_state.""" + return { + "ison": turn == "on", + "mode": mode, + "red": red, + "green": green, + "blue": blue, + "white": white, + "gain": gain, + "temp": temp, + "brightness": brightness, + "effect": effect, + "transition": transition, + } + + MOCK_BLOCKS = [ Mock( sensor_ids={"inputEvent": "S", "inputEventCnt": 2}, @@ -43,6 +73,15 @@ MOCK_BLOCKS = [ } ), ), + Mock( + sensor_ids={}, + channel="0", + output=mock_light_set_state()["ison"], + colorTemp=mock_light_set_state()["temp"], + **mock_light_set_state(), + type="light", + set_state=AsyncMock(side_effect=mock_light_set_state), + ), ] MOCK_CONFIG = { diff --git a/tests/components/shelly/test_cover.py b/tests/components/shelly/test_cover.py index 3a032a9de20..51fef7dc030 100644 --- a/tests/components/shelly/test_cover.py +++ b/tests/components/shelly/test_cover.py @@ -1,4 +1,4 @@ -"""The scene tests for the myq platform.""" +"""Tests for Shelly cover platform.""" from homeassistant.components.cover import ( ATTR_CURRENT_POSITION, ATTR_POSITION, diff --git a/tests/components/shelly/test_diagnostics.py b/tests/components/shelly/test_diagnostics.py index 93d56027fab..a99b28d48e0 100644 --- a/tests/components/shelly/test_diagnostics.py +++ b/tests/components/shelly/test_diagnostics.py @@ -1,4 +1,4 @@ -"""The scene tests for the myq platform.""" +"""Tests for Shelly diagnostics platform.""" from aiohttp import ClientSession from homeassistant.components.diagnostics import REDACTED diff --git a/tests/components/shelly/test_light.py b/tests/components/shelly/test_light.py new file mode 100644 index 00000000000..b0162f43e13 --- /dev/null +++ b/tests/components/shelly/test_light.py @@ -0,0 +1,385 @@ +"""Tests for Shelly light platform.""" + +import pytest + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, + ATTR_COLOR_MODE, + ATTR_COLOR_TEMP_KELVIN, + ATTR_EFFECT, + ATTR_EFFECT_LIST, + ATTR_RGB_COLOR, + ATTR_RGBW_COLOR, + ATTR_SUPPORTED_COLOR_MODES, + ATTR_TRANSITION, + DOMAIN as LIGHT_DOMAIN, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, + ColorMode, + LightEntityFeature, +) +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_SUPPORTED_FEATURES, + STATE_OFF, + STATE_ON, +) + +from . import init_integration + +RELAY_BLOCK_ID = 0 +LIGHT_BLOCK_ID = 2 + + +async def test_block_device_rgbw_bulb(hass, mock_block_device): + """Test block device RGBW bulb.""" + await init_integration(hass, 1, model="SHBLB-1") + + # Test initial + state = hass.states.get("light.test_name_channel_1") + attributes = state.attributes + assert state.state == STATE_ON + assert attributes[ATTR_RGBW_COLOR] == (45, 55, 65, 70) + assert attributes[ATTR_BRIGHTNESS] == 48 + assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [ + ColorMode.COLOR_TEMP, + ColorMode.RGBW, + ] + assert attributes[ATTR_SUPPORTED_FEATURES] == LightEntityFeature.EFFECT + assert len(attributes[ATTR_EFFECT_LIST]) == 7 + assert attributes[ATTR_EFFECT] == "Off" + + # Turn off + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "light.test_name_channel_1"}, + blocking=True, + ) + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with( + turn="off" + ) + state = hass.states.get("light.test_name_channel_1") + assert state.state == STATE_OFF + + # Turn on, RGBW = [70, 80, 90, 20], brightness = 33, effect = Flash + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + { + ATTR_ENTITY_ID: "light.test_name_channel_1", + ATTR_RGBW_COLOR: [70, 80, 90, 30], + ATTR_BRIGHTNESS: 33, + ATTR_EFFECT: "Flash", + }, + blocking=True, + ) + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with( + turn="on", gain=13, brightness=13, red=70, green=80, blue=90, white=30, effect=3 + ) + state = hass.states.get("light.test_name_channel_1") + attributes = state.attributes + assert state.state == STATE_ON + assert attributes[ATTR_COLOR_MODE] == ColorMode.RGBW + assert attributes[ATTR_RGBW_COLOR] == (70, 80, 90, 30) + assert attributes[ATTR_BRIGHTNESS] == 33 + assert attributes[ATTR_EFFECT] == "Flash" + + # Turn on, COLOR_TEMP_KELVIN = 3500 + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_COLOR_TEMP_KELVIN: 3500}, + blocking=True, + ) + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with( + turn="on", temp=3500, mode="white" + ) + state = hass.states.get("light.test_name_channel_1") + attributes = state.attributes + assert state.state == STATE_ON + assert attributes[ATTR_COLOR_MODE] == ColorMode.COLOR_TEMP + assert attributes[ATTR_COLOR_TEMP_KELVIN] == 3500 + + +async def test_block_device_rgb_bulb(hass, mock_block_device, monkeypatch, caplog): + """Test block device RGB bulb.""" + monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "mode") + await init_integration(hass, 1, model="SHCB-1") + + # Test initial + state = hass.states.get("light.test_name_channel_1") + attributes = state.attributes + assert state.state == STATE_ON + assert attributes[ATTR_RGB_COLOR] == (45, 55, 65) + assert attributes[ATTR_BRIGHTNESS] == 48 + assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [ + ColorMode.COLOR_TEMP, + ColorMode.RGB, + ] + assert attributes[ATTR_SUPPORTED_FEATURES] == LightEntityFeature.EFFECT + assert len(attributes[ATTR_EFFECT_LIST]) == 4 + assert attributes[ATTR_EFFECT] == "Off" + + # Turn off + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "light.test_name_channel_1"}, + blocking=True, + ) + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with( + turn="off" + ) + state = hass.states.get("light.test_name_channel_1") + assert state.state == STATE_OFF + + # Turn on, RGB = [70, 80, 90], brightness = 33, effect = Flash + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + { + ATTR_ENTITY_ID: "light.test_name_channel_1", + ATTR_RGB_COLOR: [70, 80, 90], + ATTR_BRIGHTNESS: 33, + ATTR_EFFECT: "Flash", + }, + blocking=True, + ) + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with( + turn="on", gain=13, brightness=13, red=70, green=80, blue=90, effect=3 + ) + state = hass.states.get("light.test_name_channel_1") + attributes = state.attributes + assert state.state == STATE_ON + assert attributes[ATTR_COLOR_MODE] == ColorMode.RGB + assert attributes[ATTR_RGB_COLOR] == (70, 80, 90) + assert attributes[ATTR_BRIGHTNESS] == 33 + assert attributes[ATTR_EFFECT] == "Flash" + + # Turn on, COLOR_TEMP_KELVIN = 3500 + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_COLOR_TEMP_KELVIN: 3500}, + blocking=True, + ) + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with( + turn="on", temp=3500, mode="white" + ) + state = hass.states.get("light.test_name_channel_1") + attributes = state.attributes + assert state.state == STATE_ON + assert attributes[ATTR_COLOR_MODE] == ColorMode.COLOR_TEMP + assert attributes[ATTR_COLOR_TEMP_KELVIN] == 3500 + + # Turn on with unsupported effect + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_EFFECT: "Breath"}, + blocking=True, + ) + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with( + turn="on", mode="color" + ) + state = hass.states.get("light.test_name_channel_1") + attributes = state.attributes + assert state.state == STATE_ON + assert attributes[ATTR_EFFECT] == "Off" + assert "Effect 'Breath' not supported" in caplog.text + + +async def test_block_device_white_bulb(hass, mock_block_device, monkeypatch, caplog): + """Test block device white bulb.""" + monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "red") + monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "green") + monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "blue") + monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "mode") + monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "colorTemp") + monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "effect") + await init_integration(hass, 1, model="SHVIN-1") + + # Test initial + state = hass.states.get("light.test_name_channel_1") + attributes = state.attributes + assert state.state == STATE_ON + assert attributes[ATTR_BRIGHTNESS] == 128 + assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] + assert attributes[ATTR_SUPPORTED_FEATURES] == 0 + + # Turn off + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "light.test_name_channel_1"}, + blocking=True, + ) + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with( + turn="off" + ) + state = hass.states.get("light.test_name_channel_1") + assert state.state == STATE_OFF + + # Turn on, brightness = 33 + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_BRIGHTNESS: 33}, + blocking=True, + ) + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with( + turn="on", gain=13, brightness=13 + ) + state = hass.states.get("light.test_name_channel_1") + attributes = state.attributes + assert state.state == STATE_ON + assert attributes[ATTR_BRIGHTNESS] == 33 + + +@pytest.mark.parametrize( + "model", + [ + "SHBDUO-1", + "SHCB-1", + "SHDM-1", + "SHDM-2", + "SHRGBW2", + "SHVIN-1", + ], +) +async def test_block_device_support_transition( + hass, mock_block_device, model, monkeypatch +): + """Test block device supports transition.""" + monkeypatch.setitem( + mock_block_device.settings, "fw", "20220809-122808/v1.12-g99f7e0b" + ) + await init_integration(hass, 1, model=model) + + # Test initial + state = hass.states.get("light.test_name_channel_1") + attributes = state.attributes + assert attributes[ATTR_SUPPORTED_FEATURES] & LightEntityFeature.TRANSITION + + # Turn on, TRANSITION = 4 + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_TRANSITION: 4}, + blocking=True, + ) + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with( + turn="on", transition=4000 + ) + state = hass.states.get("light.test_name_channel_1") + assert state.state == STATE_ON + + # Turn off, TRANSITION = 6, limit to 5000ms + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "light.test_name_channel_1", ATTR_TRANSITION: 6}, + blocking=True, + ) + mock_block_device.blocks[LIGHT_BLOCK_ID].set_state.assert_called_once_with( + turn="off", transition=5000 + ) + state = hass.states.get("light.test_name_channel_1") + assert state.state == STATE_OFF + + +async def test_block_device_relay_app_type_light(hass, mock_block_device, monkeypatch): + """Test block device relay in app type set to light mode.""" + monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "red") + monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "green") + monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "blue") + monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "mode") + monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "gain") + monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "brightness") + monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "effect") + monkeypatch.delattr(mock_block_device.blocks[RELAY_BLOCK_ID], "colorTemp") + monkeypatch.setitem( + mock_block_device.settings["relays"][RELAY_BLOCK_ID], "appliance_type", "light" + ) + await init_integration(hass, 1) + assert hass.states.get("switch.test_name_channel_1") is None + + # Test initial + state = hass.states.get("light.test_name_channel_1") + attributes = state.attributes + assert state.state == STATE_ON + assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.ONOFF] + assert attributes[ATTR_SUPPORTED_FEATURES] == 0 + + # Turn off + mock_block_device.blocks[RELAY_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "light.test_name_channel_1"}, + blocking=True, + ) + mock_block_device.blocks[RELAY_BLOCK_ID].set_state.assert_called_once_with( + turn="off" + ) + state = hass.states.get("light.test_name_channel_1") + assert state.state == STATE_OFF + + # Turn on + mock_block_device.blocks[RELAY_BLOCK_ID].set_state.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_name_channel_1"}, + blocking=True, + ) + mock_block_device.blocks[RELAY_BLOCK_ID].set_state.assert_called_once_with( + turn="on" + ) + state = hass.states.get("light.test_name_channel_1") + assert state.state == STATE_ON + + +async def test_block_device_no_light_blocks(hass, mock_block_device, monkeypatch): + """Test block device without light blocks.""" + monkeypatch.setattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "type", "roller") + await init_integration(hass, 1) + assert hass.states.get("light.test_name_channel_1") is None + + +async def test_rpc_device_switch_type_lights_mode(hass, mock_rpc_device, monkeypatch): + """Test RPC device with switch in consumption type lights mode.""" + monkeypatch.setitem( + mock_rpc_device.config["sys"]["ui_data"], "consumption_types", ["lights"] + ) + await init_integration(hass, 2) + + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_switch_0"}, + blocking=True, + ) + assert hass.states.get("light.test_switch_0").state == STATE_ON + + monkeypatch.setitem(mock_rpc_device.status["switch:0"], "output", False) + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "light.test_switch_0"}, + blocking=True, + ) + mock_rpc_device.mock_update() + assert hass.states.get("light.test_switch_0").state == STATE_OFF diff --git a/tests/components/shelly/test_switch.py b/tests/components/shelly/test_switch.py index c2c7d90943d..458de9c655b 100644 --- a/tests/components/shelly/test_switch.py +++ b/tests/components/shelly/test_switch.py @@ -1,4 +1,4 @@ -"""The scene tests for the myq platform.""" +"""Tests for Shelly switch platform.""" from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.const import ( ATTR_ENTITY_ID,