Files
core/tests/components/switchbot/test_switch.py
2025-09-26 12:11:59 +02:00

297 lines
8.4 KiB
Python

"""Test the switchbot switches."""
from collections.abc import Callable
from unittest.mock import AsyncMock, patch
import pytest
from switchbot.devices.device import SwitchbotOperationError
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from homeassistant.components.switch import (
DOMAIN as SWITCH_DOMAIN,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_ON,
)
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant, State
from homeassistant.exceptions import HomeAssistantError
from . import (
PLUG_MINI_EU_SERVICE_INFO,
RELAY_SWITCH_1_SERVICE_INFO,
RELAY_SWITCH_2PM_SERVICE_INFO,
WOHAND_SERVICE_INFO,
WORELAY_SWITCH_1PM_SERVICE_INFO,
)
from tests.common import MockConfigEntry, mock_restore_cache
from tests.components.bluetooth import inject_bluetooth_service_info
async def test_switchbot_switch_with_restore_state(
hass: HomeAssistant,
mock_entry_factory: Callable[[str], MockConfigEntry],
) -> None:
"""Test that Switchbot Switch restores state correctly after reboot."""
inject_bluetooth_service_info(hass, WOHAND_SERVICE_INFO)
entry = mock_entry_factory(sensor_type="bot")
entity_id = "switch.test_name"
mock_restore_cache(
hass,
[
State(
entity_id,
STATE_ON,
{"last_run_success": True},
)
],
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.switchbot.switch.switchbot.Switchbot.switch_mode",
return_value=False,
):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state.state == STATE_ON
assert state.attributes["last_run_success"] is True
@pytest.mark.parametrize(
("exception", "error_message"),
[
(
SwitchbotOperationError("Operation failed"),
"An error occurred while performing the action: Operation failed",
),
],
)
@pytest.mark.parametrize(
("service", "mock_method"),
[
(SERVICE_TURN_ON, "turn_on"),
(SERVICE_TURN_OFF, "turn_off"),
],
)
async def test_exception_handling_switch(
hass: HomeAssistant,
mock_entry_factory: Callable[[str], MockConfigEntry],
service: str,
mock_method: str,
exception: Exception,
error_message: str,
) -> None:
"""Test exception handling for switch service with exception."""
inject_bluetooth_service_info(hass, WOHAND_SERVICE_INFO)
entry = mock_entry_factory(sensor_type="bot")
entry.add_to_hass(hass)
entity_id = "switch.test_name"
patch_target = (
f"homeassistant.components.switchbot.switch.switchbot.Switchbot.{mock_method}"
)
with patch(patch_target, new=AsyncMock(side_effect=exception)):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
with pytest.raises(HomeAssistantError, match=error_message):
await hass.services.async_call(
SWITCH_DOMAIN,
service,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)
@pytest.mark.parametrize(
("sensor_type", "service_info"),
[
("plug_mini_eu", PLUG_MINI_EU_SERVICE_INFO),
("relay_switch_1", RELAY_SWITCH_1_SERVICE_INFO),
("relay_switch_1pm", WORELAY_SWITCH_1PM_SERVICE_INFO),
],
)
@pytest.mark.parametrize(
("service", "mock_method"),
[
(SERVICE_TURN_ON, "turn_on"),
(SERVICE_TURN_OFF, "turn_off"),
],
)
async def test_relay_switch_control(
hass: HomeAssistant,
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
sensor_type: str,
service_info: BluetoothServiceInfoBleak,
service: str,
mock_method: str,
) -> None:
"""Test Relay Switch control."""
inject_bluetooth_service_info(hass, service_info)
entry = mock_entry_encrypted_factory(sensor_type=sensor_type)
entry.add_to_hass(hass)
mocked_instance = AsyncMock(return_value=True)
with patch.multiple(
"homeassistant.components.switchbot.switch.switchbot.SwitchbotRelaySwitch",
update=AsyncMock(return_value=None),
**{mock_method: mocked_instance},
):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
entity_id = "switch.test_name"
await hass.services.async_call(
SWITCH_DOMAIN,
service,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)
mocked_instance.assert_awaited_once()
@pytest.mark.parametrize(
("service", "mock_method"),
[(SERVICE_TURN_ON, "turn_on"), (SERVICE_TURN_OFF, "turn_off")],
)
async def test_relay_switch_2pm_control(
hass: HomeAssistant,
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
service: str,
mock_method: str,
) -> None:
"""Test Relay Switch 2PM control."""
inject_bluetooth_service_info(hass, RELAY_SWITCH_2PM_SERVICE_INFO)
entry = mock_entry_encrypted_factory(sensor_type="relay_switch_2pm")
entry.add_to_hass(hass)
mocked_instance = AsyncMock(return_value=True)
with patch.multiple(
"homeassistant.components.switchbot.switch.switchbot.SwitchbotRelaySwitch2PM",
update=AsyncMock(return_value=None),
**{mock_method: mocked_instance},
):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
entity_id_1 = "switch.test_name_channel_1"
await hass.services.async_call(
SWITCH_DOMAIN,
service,
{ATTR_ENTITY_ID: entity_id_1},
blocking=True,
)
mocked_instance.assert_called_with(1)
entity_id_2 = "switch.test_name_channel_2"
await hass.services.async_call(
SWITCH_DOMAIN,
service,
{ATTR_ENTITY_ID: entity_id_2},
blocking=True,
)
mocked_instance.assert_called_with(2)
@pytest.mark.parametrize(
("sensor_type", "service_info", "entity_id", "mock_class"),
[
(
"relay_switch_1",
RELAY_SWITCH_1_SERVICE_INFO,
"switch.test_name",
"SwitchbotRelaySwitch",
),
(
"relay_switch_1pm",
WORELAY_SWITCH_1PM_SERVICE_INFO,
"switch.test_name",
"SwitchbotRelaySwitch",
),
(
"plug_mini_eu",
PLUG_MINI_EU_SERVICE_INFO,
"switch.test_name",
"SwitchbotRelaySwitch",
),
(
"relay_switch_2pm",
RELAY_SWITCH_2PM_SERVICE_INFO,
"switch.test_name_channel_1",
"SwitchbotRelaySwitch2PM",
),
(
"relay_switch_2pm",
RELAY_SWITCH_2PM_SERVICE_INFO,
"switch.test_name_channel_2",
"SwitchbotRelaySwitch2PM",
),
],
)
@pytest.mark.parametrize(
("service", "mock_method"),
[
(SERVICE_TURN_ON, "turn_on"),
(SERVICE_TURN_OFF, "turn_off"),
],
)
@pytest.mark.parametrize(
("exception", "error_message"),
[
(
SwitchbotOperationError("Operation failed"),
"An error occurred while performing the action: Operation failed",
),
],
)
async def test_relay_switch_control_with_exception(
hass: HomeAssistant,
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
sensor_type: str,
service_info: BluetoothServiceInfoBleak,
entity_id: str,
mock_class: str,
service: str,
mock_method: str,
exception: Exception,
error_message: str,
) -> None:
"""Test Relay Switch control with exception."""
inject_bluetooth_service_info(hass, service_info)
entry = mock_entry_encrypted_factory(sensor_type=sensor_type)
entry.add_to_hass(hass)
with patch.multiple(
f"homeassistant.components.switchbot.switch.switchbot.{mock_class}",
update=AsyncMock(return_value=None),
**{mock_method: AsyncMock(side_effect=exception)},
):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
with pytest.raises(HomeAssistantError, match=error_message):
await hass.services.async_call(
SWITCH_DOMAIN,
service,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)