From b11a75d438d7e83f4da593e270123f3c0ff617d7 Mon Sep 17 00:00:00 2001 From: YogevBokobza Date: Fri, 13 Feb 2026 22:32:55 +0200 Subject: [PATCH] Add Switcher heater support (#162588) Co-authored-by: Shay Levy --- .../components/switcher_kis/manifest.json | 2 +- .../components/switcher_kis/sensor.py | 9 +- .../components/switcher_kis/switch.py | 11 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/switcher_kis/consts.py | 26 +++- .../snapshots/test_diagnostics.ambr | 2 +- tests/components/switcher_kis/test_init.py | 116 +++++++++++++++++- tests/components/switcher_kis/test_sensor.py | 53 ++++++++ .../components/switcher_kis/test_services.py | 99 +++++++++++++++ tests/components/switcher_kis/test_switch.py | 51 ++++++++ 11 files changed, 360 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/switcher_kis/manifest.json b/homeassistant/components/switcher_kis/manifest.json index c1ad6b0428fa..2a90f7bc4054 100644 --- a/homeassistant/components/switcher_kis/manifest.json +++ b/homeassistant/components/switcher_kis/manifest.json @@ -7,6 +7,6 @@ "iot_class": "local_push", "loggers": ["aioswitcher"], "quality_scale": "silver", - "requirements": ["aioswitcher==6.0.0"], + "requirements": ["aioswitcher==6.1.0"], "single_config_entry": true } diff --git a/homeassistant/components/switcher_kis/sensor.py b/homeassistant/components/switcher_kis/sensor.py index e1c1f8830940..d253c3407885 100644 --- a/homeassistant/components/switcher_kis/sensor.py +++ b/homeassistant/components/switcher_kis/sensor.py @@ -81,7 +81,7 @@ TEMPERATURE_SENSORS: list[SwitcherSensorEntityDescription] = [ ] POWER_PLUG_SENSORS = POWER_SENSORS -WATER_HEATER_SENSORS = [*POWER_SENSORS, *TIME_SENSORS] +HEATER_SENSORS = [*POWER_SENSORS, *TIME_SENSORS] THERMOSTAT_SENSORS = TEMPERATURE_SENSORS @@ -100,10 +100,13 @@ async def async_setup_entry( SwitcherSensorEntity(coordinator, description) for description in POWER_PLUG_SENSORS ) - elif coordinator.data.device_type.category == DeviceCategory.WATER_HEATER: + elif coordinator.data.device_type.category in [ + DeviceCategory.WATER_HEATER, + DeviceCategory.HEATER, + ]: async_add_entities( SwitcherSensorEntity(coordinator, description) - for description in WATER_HEATER_SENSORS + for description in HEATER_SENSORS ) elif coordinator.data.device_type.category == DeviceCategory.THERMOSTAT: async_add_entities( diff --git a/homeassistant/components/switcher_kis/switch.py b/homeassistant/components/switcher_kis/switch.py index 9e16f48a63ba..d79b319fc6e9 100644 --- a/homeassistant/components/switcher_kis/switch.py +++ b/homeassistant/components/switcher_kis/switch.py @@ -79,8 +79,11 @@ async def async_setup_entry( if coordinator.data.device_type.category == DeviceCategory.POWER_PLUG: entities.append(SwitcherPowerPlugSwitchEntity(coordinator)) - elif coordinator.data.device_type.category == DeviceCategory.WATER_HEATER: - entities.append(SwitcherWaterHeaterSwitchEntity(coordinator)) + elif coordinator.data.device_type.category in [ + DeviceCategory.WATER_HEATER, + DeviceCategory.HEATER, + ]: + entities.append(SwitcherHeaterSwitchEntity(coordinator)) elif coordinator.data.device_type.category in ( DeviceCategory.SHUTTER, DeviceCategory.SINGLE_SHUTTER_DUAL_LIGHT, @@ -143,8 +146,8 @@ class SwitcherPowerPlugSwitchEntity(SwitcherBaseSwitchEntity): _attr_device_class = SwitchDeviceClass.OUTLET -class SwitcherWaterHeaterSwitchEntity(SwitcherBaseSwitchEntity): - """Representation of a Switcher water heater switch entity.""" +class SwitcherHeaterSwitchEntity(SwitcherBaseSwitchEntity): + """Representation of a Switcher heater switch entity.""" _attr_device_class = SwitchDeviceClass.SWITCH diff --git a/requirements_all.txt b/requirements_all.txt index 76b2e8dac25d..54dbe6391c93 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -413,7 +413,7 @@ aiosteamist==1.0.1 aiostreammagic==2.12.1 # homeassistant.components.switcher_kis -aioswitcher==6.0.0 +aioswitcher==6.1.0 # homeassistant.components.syncthing aiosyncthing==0.7.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8f25cb30f069..d52b8891fb03 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -398,7 +398,7 @@ aiosteamist==1.0.1 aiostreammagic==2.12.1 # homeassistant.components.switcher_kis -aioswitcher==6.0.0 +aioswitcher==6.1.0 # homeassistant.components.syncthing aiosyncthing==0.7.1 diff --git a/tests/components/switcher_kis/consts.py b/tests/components/switcher_kis/consts.py index 57454e38062a..6ebfa8263d69 100644 --- a/tests/components/switcher_kis/consts.py +++ b/tests/components/switcher_kis/consts.py @@ -6,6 +6,7 @@ from aioswitcher.device import ( ShutterChildLock, ShutterDirection, SwitcherDualShutterSingleLight, + SwitcherHeater, SwitcherLight, SwitcherPowerPlug, SwitcherShutter, @@ -28,6 +29,7 @@ DUMMY_DEVICE_ID6 = "bcdc64" DUMMY_DEVICE_ID7 = "bcdd64" DUMMY_DEVICE_ID8 = "bcde64" DUMMY_DEVICE_ID9 = "bcdf64" +DUMMY_DEVICE_ID10 = "cafe66" DUMMY_DEVICE_KEY1 = "18" DUMMY_DEVICE_KEY2 = "01" DUMMY_DEVICE_KEY3 = "12" @@ -37,8 +39,9 @@ DUMMY_DEVICE_KEY6 = "16" DUMMY_DEVICE_KEY7 = "17" DUMMY_DEVICE_KEY8 = "18" DUMMY_DEVICE_KEY9 = "19" +DUMMY_DEVICE_KEY10 = "20" DUMMY_DEVICE_NAME1 = "Plug 23BC" -DUMMY_DEVICE_NAME2 = "Heater FE12" +DUMMY_DEVICE_NAME2 = "WaterHeater FE12" DUMMY_DEVICE_NAME3 = "Breeze AB39" DUMMY_DEVICE_NAME4 = "Runner DD77" DUMMY_DEVICE_NAME5 = "RunnerS11 6CF5" @@ -46,9 +49,11 @@ DUMMY_DEVICE_NAME6 = "RunnerS12 A9BE" DUMMY_DEVICE_NAME7 = "Light 36BB" DUMMY_DEVICE_NAME8 = "Light 36CB" DUMMY_DEVICE_NAME9 = "Light 36DB" +DUMMY_DEVICE_NAME10 = "Heater FE66" DUMMY_DEVICE_PASSWORD = "12345678" DUMMY_ELECTRIC_CURRENT1 = 0.5 DUMMY_ELECTRIC_CURRENT2 = 12.8 +DUMMY_ELECTRIC_CURRENT3 = 10.0 DUMMY_IP_ADDRESS1 = "192.168.100.157" DUMMY_IP_ADDRESS2 = "192.168.100.158" DUMMY_IP_ADDRESS3 = "192.168.100.159" @@ -58,6 +63,7 @@ DUMMY_IP_ADDRESS6 = "192.168.100.162" DUMMY_IP_ADDRESS7 = "192.168.100.163" DUMMY_IP_ADDRESS8 = "192.168.100.164" DUMMY_IP_ADDRESS9 = "192.168.100.165" +DUMMY_IP_ADDRESS10 = "192.168.100.166" DUMMY_MAC_ADDRESS1 = "A1:B2:C3:45:67:D8" DUMMY_MAC_ADDRESS2 = "A1:B2:C3:45:67:D9" DUMMY_MAC_ADDRESS3 = "A1:B2:C3:45:67:DA" @@ -67,6 +73,7 @@ DUMMY_MAC_ADDRESS6 = "A1:B2:C3:45:67:DD" DUMMY_MAC_ADDRESS7 = "A1:B2:C3:45:67:DE" DUMMY_MAC_ADDRESS8 = "A1:B2:C3:45:67:DF" DUMMY_MAC_ADDRESS9 = "A1:B2:C3:45:67:DG" +DUMMY_MAC_ADDRESS10 = "A1:B2:C3:45:67:DH" DUMMY_TOKEN_NEEDED1 = False DUMMY_TOKEN_NEEDED2 = False DUMMY_TOKEN_NEEDED3 = False @@ -76,9 +83,11 @@ DUMMY_TOKEN_NEEDED6 = True DUMMY_TOKEN_NEEDED7 = True DUMMY_TOKEN_NEEDED8 = True DUMMY_TOKEN_NEEDED9 = True +DUMMY_TOKEN_NEEDED10 = True DUMMY_PHONE_ID = "1234" DUMMY_POWER_CONSUMPTION1 = 100 DUMMY_POWER_CONSUMPTION2 = 2780 +DUMMY_POWER_CONSUMPTION3 = 1000 DUMMY_REMAINING_TIME = "01:29:32" DUMMY_TIMER_MINUTES_SET = "90" DUMMY_THERMOSTAT_MODE = ThermostatMode.COOL @@ -127,6 +136,21 @@ DUMMY_WATER_HEATER_DEVICE = SwitcherWaterHeater( DUMMY_AUTO_SHUT_DOWN, ) +DUMMY_HEATER_DEVICE = SwitcherHeater( + DeviceType.HEATER, + DeviceState.ON, + DUMMY_DEVICE_ID10, + DUMMY_DEVICE_KEY10, + DUMMY_IP_ADDRESS10, + DUMMY_MAC_ADDRESS10, + DUMMY_DEVICE_NAME10, + DUMMY_TOKEN_NEEDED10, + DUMMY_POWER_CONSUMPTION3, + DUMMY_ELECTRIC_CURRENT3, + DUMMY_REMAINING_TIME, + DUMMY_AUTO_SHUT_DOWN, +) + DUMMY_SHUTTER_DEVICE = SwitcherShutter( DeviceType.RUNNER, DeviceState.ON, diff --git a/tests/components/switcher_kis/snapshots/test_diagnostics.ambr b/tests/components/switcher_kis/snapshots/test_diagnostics.ambr index 78d9faf15a60..e54218860908 100644 --- a/tests/components/switcher_kis/snapshots/test_diagnostics.ambr +++ b/tests/components/switcher_kis/snapshots/test_diagnostics.ambr @@ -18,7 +18,7 @@ 'ip_address': '**REDACTED**', 'last_data_update': '2022-09-28T16:42:12.706017', 'mac_address': '**REDACTED**', - 'name': 'Heater FE12', + 'name': 'WaterHeater FE12', 'power_consumption': 2780, 'remaining_time': '01:29:32', 'token_needed': False, diff --git a/tests/components/switcher_kis/test_init.py b/tests/components/switcher_kis/test_init.py index afef28dec7b9..f15e3999fd69 100644 --- a/tests/components/switcher_kis/test_init.py +++ b/tests/components/switcher_kis/test_init.py @@ -14,7 +14,15 @@ from homeassistant.setup import async_setup_component from homeassistant.util import dt as dt_util, slugify from . import init_integration -from .consts import DUMMY_DEVICE_ID1, DUMMY_DEVICE_ID4, DUMMY_SWITCHER_DEVICES +from .consts import ( + DUMMY_DEVICE_ID1, + DUMMY_DEVICE_ID4, + DUMMY_DEVICE_ID10, + DUMMY_HEATER_DEVICE, + DUMMY_SWITCHER_DEVICES, + DUMMY_TOKEN as TOKEN, + DUMMY_USERNAME as USERNAME, +) from tests.common import async_fire_time_changed from tests.typing import WebSocketGenerator @@ -71,6 +79,57 @@ async def test_update_fail( assert state.state != STATE_UNAVAILABLE +async def test_update_fail_token_needed( + hass: HomeAssistant, + mock_bridge, + caplog: pytest.LogCaptureFixture, + freezer: FrozenDateTimeFactory, +) -> None: + """Test entities state unavailable when updates fail..""" + entry = await init_integration(hass, USERNAME, TOKEN) + assert mock_bridge + + device = DUMMY_HEATER_DEVICE + + mock_bridge.mock_callbacks([DUMMY_HEATER_DEVICE]) + await hass.async_block_till_done() + + assert mock_bridge.is_running is True + assert len(entry.runtime_data) == 1 + + freezer.tick(timedelta(seconds=MAX_UPDATE_INTERVAL_SEC + 1)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert ( + f"Device {device.name} did not send update for {MAX_UPDATE_INTERVAL_SEC} seconds" + in caplog.text + ) + + entity_id = f"switch.{slugify(device.name)}" + state = hass.states.get(entity_id) + assert state.state == STATE_UNAVAILABLE + + entity_id = f"sensor.{slugify(device.name)}_power" + state = hass.states.get(entity_id) + assert state.state == STATE_UNAVAILABLE + + mock_bridge.mock_callbacks([DUMMY_HEATER_DEVICE]) + await hass.async_block_till_done() + async_fire_time_changed( + hass, dt_util.utcnow() + timedelta(seconds=MAX_UPDATE_INTERVAL_SEC - 2) + ) + await hass.async_block_till_done() + + entity_id = f"switch.{slugify(device.name)}" + state = hass.states.get(entity_id) + assert state.state != STATE_UNAVAILABLE + + entity_id = f"sensor.{slugify(device.name)}_power" + state = hass.states.get(entity_id) + assert state.state != STATE_UNAVAILABLE + + async def test_entry_unload(hass: HomeAssistant, mock_bridge) -> None: """Test entry unload.""" entry = await init_integration(hass) @@ -139,3 +198,58 @@ async def test_remove_device( assert ( device_registry.async_get_device(identifiers={(DOMAIN, dead_device_id)}) is None ) + + +async def test_remove_device_token_needed( + hass: HomeAssistant, + mock_bridge, + hass_ws_client: WebSocketGenerator, + device_registry: dr.DeviceRegistry, +) -> None: + """Test being able to remove a disconnected device.""" + assert await async_setup_component(hass, "config", {}) + entry = await init_integration(hass, USERNAME, TOKEN) + entry_id = entry.entry_id + assert mock_bridge + + mock_bridge.mock_callbacks([DUMMY_HEATER_DEVICE]) + await hass.async_block_till_done() + + assert mock_bridge.is_running is True + assert len(entry.runtime_data) == 1 + + live_device_id = DUMMY_DEVICE_ID10 + dead_device_id = DUMMY_DEVICE_ID4 + + assert len(dr.async_entries_for_config_entry(device_registry, entry_id)) == 1 + + # Create a dead device + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, dead_device_id)}, + manufacturer="Switcher", + model="Switcher Model", + name="Switcher Device", + ) + await hass.async_block_till_done() + assert len(dr.async_entries_for_config_entry(device_registry, entry_id)) == 2 + + # Try to remove a live device - fails + device = device_registry.async_get_device(identifiers={(DOMAIN, live_device_id)}) + client = await hass_ws_client(hass) + response = await client.remove_device(device.id, entry_id) + assert not response["success"] + assert len(dr.async_entries_for_config_entry(device_registry, entry_id)) == 2 + assert ( + device_registry.async_get_device(identifiers={(DOMAIN, live_device_id)}) + is not None + ) + + # Try to remove a dead device - succeeds + device = device_registry.async_get_device(identifiers={(DOMAIN, dead_device_id)}) + response = await client.remove_device(device.id, entry_id) + assert response["success"] + assert len(dr.async_entries_for_config_entry(device_registry, entry_id)) == 1 + assert ( + device_registry.async_get_device(identifiers={(DOMAIN, dead_device_id)}) is None + ) diff --git a/tests/components/switcher_kis/test_sensor.py b/tests/components/switcher_kis/test_sensor.py index 1a6c2ccb6875..91eed01ff7ca 100644 --- a/tests/components/switcher_kis/test_sensor.py +++ b/tests/components/switcher_kis/test_sensor.py @@ -7,9 +7,12 @@ from homeassistant.util import slugify from . import init_integration from .consts import ( + DUMMY_HEATER_DEVICE, DUMMY_PLUG_DEVICE, DUMMY_SWITCHER_SENSORS_DEVICES, DUMMY_THERMOSTAT_DEVICE, + DUMMY_TOKEN as TOKEN, + DUMMY_USERNAME as USERNAME, DUMMY_WATER_HEATER_DEVICE, ) @@ -37,6 +40,17 @@ DEVICE_SENSORS_TUPLE = ( ), ) +DEVICE_SENSORS_TOKEN_NEEDED_TUPLE = ( + ( + DUMMY_HEATER_DEVICE, + [ + ("power", "power_consumption"), + ("current", "electric_current"), + ("remaining_time", "remaining_time"), + ], + ), +) + @pytest.mark.parametrize("mock_bridge", [DUMMY_SWITCHER_SENSORS_DEVICES], indirect=True) async def test_sensor_platform(hass: HomeAssistant, mock_bridge) -> None: @@ -54,6 +68,22 @@ async def test_sensor_platform(hass: HomeAssistant, mock_bridge) -> None: assert state.state == str(getattr(device, field)) +@pytest.mark.parametrize("mock_bridge", [[DUMMY_HEATER_DEVICE]], indirect=True) +async def test_sensor_platform_token_needed(hass: HomeAssistant, mock_bridge) -> None: + """Test sensor platform.""" + entry = await init_integration(hass, USERNAME, TOKEN) + assert mock_bridge + + assert mock_bridge.is_running is True + assert len(entry.runtime_data) == 1 + + for device, sensors in DEVICE_SENSORS_TOKEN_NEEDED_TUPLE: + for sensor, field in sensors: + entity_id = f"sensor.{slugify(device.name)}_{sensor}" + state = hass.states.get(entity_id) + assert state.state == str(getattr(device, field)) + + @pytest.mark.parametrize("mock_bridge", [[DUMMY_WATER_HEATER_DEVICE]], indirect=True) async def test_sensor_update( hass: HomeAssistant, mock_bridge, monkeypatch: pytest.MonkeyPatch @@ -75,3 +105,26 @@ async def test_sensor_update( state = hass.states.get(entity_id) assert state.state == "1431" + + +@pytest.mark.parametrize("mock_bridge", [[DUMMY_HEATER_DEVICE]], indirect=True) +async def test_sensor_update_token_needed( + hass: HomeAssistant, mock_bridge, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test sensor update.""" + await init_integration(hass, USERNAME, TOKEN) + assert mock_bridge + + device = DUMMY_HEATER_DEVICE + field = "power_consumption" + entity_id = f"sensor.{slugify(device.name)}_power" + + state = hass.states.get(entity_id) + assert state.state == str(getattr(device, field)) + + monkeypatch.setattr(device, field, 1431) + mock_bridge.mock_callbacks([device]) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state.state == "1431" diff --git a/tests/components/switcher_kis/test_services.py b/tests/components/switcher_kis/test_services.py index ab2414b2681f..e41857ce7386 100644 --- a/tests/components/switcher_kis/test_services.py +++ b/tests/components/switcher_kis/test_services.py @@ -23,8 +23,11 @@ from homeassistant.util import slugify from . import init_integration from .consts import ( DUMMY_AUTO_OFF_SET, + DUMMY_HEATER_DEVICE, DUMMY_PLUG_DEVICE, DUMMY_TIMER_MINUTES_SET, + DUMMY_TOKEN as TOKEN, + DUMMY_USERNAME as USERNAME, DUMMY_WATER_HEATER_DEVICE, ) @@ -69,6 +72,46 @@ async def test_turn_on_with_timer_service( assert state.state == STATE_ON +@pytest.mark.parametrize("mock_bridge", [[DUMMY_HEATER_DEVICE]], indirect=True) +async def test_turn_on_with_timer_service_token_needed( + hass: HomeAssistant, mock_bridge, mock_api, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test the turn on with timer service.""" + await init_integration(hass, USERNAME, TOKEN) + assert mock_bridge + + device = DUMMY_HEATER_DEVICE + entity_id = f"{SWITCH_DOMAIN}.{slugify(device.name)}" + + # Test initial state - off + monkeypatch.setattr(device, "device_state", DeviceState.OFF) + mock_bridge.mock_callbacks([DUMMY_HEATER_DEVICE]) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state.state == STATE_OFF + + with patch( + "homeassistant.components.switcher_kis.entity.SwitcherApi.control_device" + ) as mock_control_device: + await hass.services.async_call( + DOMAIN, + SERVICE_TURN_ON_WITH_TIMER_NAME, + { + ATTR_ENTITY_ID: entity_id, + CONF_TIMER_MINUTES: DUMMY_TIMER_MINUTES_SET, + }, + blocking=True, + ) + + assert mock_api.call_count == 2 + mock_control_device.assert_called_once_with( + Command.ON, int(DUMMY_TIMER_MINUTES_SET) + ) + state = hass.states.get(entity_id) + assert state.state == STATE_ON + + @pytest.mark.parametrize("mock_bridge", [[DUMMY_WATER_HEATER_DEVICE]], indirect=True) async def test_set_auto_off_service(hass: HomeAssistant, mock_bridge, mock_api) -> None: """Test the set auto off service.""" @@ -94,6 +137,31 @@ async def test_set_auto_off_service(hass: HomeAssistant, mock_bridge, mock_api) ) +@pytest.mark.parametrize("mock_bridge", [[DUMMY_HEATER_DEVICE]], indirect=True) +async def test_set_auto_off_service_token_needed( + hass: HomeAssistant, mock_bridge, mock_api +) -> None: + """Test the set auto off service.""" + await init_integration(hass, USERNAME, TOKEN) + assert mock_bridge + + device = DUMMY_HEATER_DEVICE + entity_id = f"{SWITCH_DOMAIN}.{slugify(device.name)}" + + with patch( + "homeassistant.components.switcher_kis.entity.SwitcherApi.set_auto_shutdown" + ) as mock_set_auto_shutdown: + await hass.services.async_call( + DOMAIN, + SERVICE_SET_AUTO_OFF_NAME, + {ATTR_ENTITY_ID: entity_id, CONF_AUTO_OFF: DUMMY_AUTO_OFF_SET}, + blocking=True, + ) + + assert mock_api.call_count == 2 + mock_set_auto_shutdown.assert_called_once_with(time_period_str(DUMMY_AUTO_OFF_SET)) + + @pytest.mark.parametrize("mock_bridge", [[DUMMY_WATER_HEATER_DEVICE]], indirect=True) async def test_set_auto_off_service_fail( hass: HomeAssistant, mock_bridge, mock_api @@ -125,6 +193,37 @@ async def test_set_auto_off_service_fail( assert state.state == STATE_UNAVAILABLE +@pytest.mark.parametrize("mock_bridge", [[DUMMY_HEATER_DEVICE]], indirect=True) +async def test_set_auto_off_service_fail_token_needed( + hass: HomeAssistant, mock_bridge, mock_api +) -> None: + """Test set auto off service failed.""" + await init_integration(hass, USERNAME, TOKEN) + assert mock_bridge + + device = DUMMY_HEATER_DEVICE + entity_id = f"{SWITCH_DOMAIN}.{slugify(device.name)}" + + with ( + patch( + "homeassistant.components.switcher_kis.entity.SwitcherApi.set_auto_shutdown", + return_value=None, + ) as mock_set_auto_shutdown, + pytest.raises(HomeAssistantError), + ): + await hass.services.async_call( + DOMAIN, + SERVICE_SET_AUTO_OFF_NAME, + {ATTR_ENTITY_ID: entity_id, CONF_AUTO_OFF: DUMMY_AUTO_OFF_SET}, + blocking=True, + ) + + assert mock_api.call_count == 2 + mock_set_auto_shutdown.assert_called_once_with(time_period_str(DUMMY_AUTO_OFF_SET)) + state = hass.states.get(entity_id) + assert state.state == STATE_UNAVAILABLE + + @pytest.mark.parametrize("mock_bridge", [[DUMMY_PLUG_DEVICE]], indirect=True) async def test_plug_unsupported_services( hass: HomeAssistant, mock_bridge, mock_api, caplog: pytest.LogCaptureFixture diff --git a/tests/components/switcher_kis/test_switch.py b/tests/components/switcher_kis/test_switch.py index 52391f4dd081..36181f8f8c84 100644 --- a/tests/components/switcher_kis/test_switch.py +++ b/tests/components/switcher_kis/test_switch.py @@ -23,6 +23,7 @@ from homeassistant.util import slugify from . import init_integration from .consts import ( DUMMY_DUAL_SHUTTER_SINGLE_LIGHT_DEVICE as DEVICE3, + DUMMY_HEATER_DEVICE, DUMMY_PLUG_DEVICE, DUMMY_SHUTTER_DEVICE as DEVICE, DUMMY_SINGLE_SHUTTER_DUAL_LIGHT_DEVICE as DEVICE2, @@ -87,6 +88,56 @@ async def test_switch( assert state.state == STATE_OFF +@pytest.mark.parametrize("mock_bridge", [[DUMMY_HEATER_DEVICE]], indirect=True) +async def test_switch_token_needed( + hass: HomeAssistant, mock_bridge, mock_api, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test the switch.""" + await init_integration(hass, USERNAME, TOKEN) + assert mock_bridge + + device = DUMMY_HEATER_DEVICE + entity_id = f"{SWITCH_DOMAIN}.{slugify(device.name)}" + + # Test initial state - on + state = hass.states.get(entity_id) + assert state.state == STATE_ON + + # Test state change on --> off + monkeypatch.setattr(device, "device_state", DeviceState.OFF) + mock_bridge.mock_callbacks([device]) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state.state == STATE_OFF + + # Test turning on + with patch( + "homeassistant.components.switcher_kis.entity.SwitcherApi.control_device", + ) as mock_control_device: + await hass.services.async_call( + SWITCH_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + + assert mock_api.call_count == 2 + mock_control_device.assert_called_once_with(Command.ON) + state = hass.states.get(entity_id) + assert state.state == STATE_ON + + # Test turning off + with patch( + "homeassistant.components.switcher_kis.entity.SwitcherApi.control_device" + ) as mock_control_device: + await hass.services.async_call( + SWITCH_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + + assert mock_api.call_count == 4 + mock_control_device.assert_called_once_with(Command.OFF) + state = hass.states.get(entity_id) + assert state.state == STATE_OFF + + @pytest.mark.parametrize("mock_bridge", [[DUMMY_WATER_HEATER_DEVICE]], indirect=True) async def test_switch_ignore_previous_async_state( hass: HomeAssistant, mock_bridge, mock_api