mirror of
https://github.com/home-assistant/core.git
synced 2026-10-05 21:57:39 +01:00
1046 lines
31 KiB
Python
1046 lines
31 KiB
Python
"""Tests for the Bosch SHC switch platform."""
|
|
|
|
from collections.abc import Generator
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from boschshcpy import (
|
|
BypassService,
|
|
CameraLightService,
|
|
SilentModeService,
|
|
ThermostatService,
|
|
)
|
|
import pytest
|
|
|
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
|
from homeassistant.const import (
|
|
ATTR_ENTITY_ID,
|
|
SERVICE_TURN_OFF,
|
|
SERVICE_TURN_ON,
|
|
Platform,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import entity_registry as er
|
|
|
|
from .conftest import (
|
|
camera_eyes_device,
|
|
light_switch_bsm_device,
|
|
micromodule_relay_device,
|
|
motion_detector2_device,
|
|
presence_simulation_system_device,
|
|
setup_integration,
|
|
shutter_contact2_device,
|
|
shutter_contact2_plus_device,
|
|
smart_plug_compact_device,
|
|
smart_plug_device,
|
|
smoke_detector_device,
|
|
thermostat_device,
|
|
thermostat_gen2_device,
|
|
twinguard_device,
|
|
)
|
|
|
|
from tests.common import MockConfigEntry
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def platforms() -> Generator[None]:
|
|
"""Restrict bosch_shc setup to the switch platform."""
|
|
with patch("homeassistant.components.bosch_shc.PLATFORMS", [Platform.SWITCH]):
|
|
yield
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"thermostats": [thermostat_device(child_lock=ThermostatService.State.OFF)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_thermostat_child_lock(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A thermostat's enum-based child lock is exposed and controllable."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.thermostats[0]
|
|
|
|
state = hass.states.get("switch.thermostat_child_lock")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.thermostat_child_lock"},
|
|
blocking=True,
|
|
)
|
|
assert device.child_lock is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.thermostat_child_lock"},
|
|
blocking=True,
|
|
)
|
|
assert device.child_lock is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"thermostats": [
|
|
thermostat_device(
|
|
supports_silentmode=True,
|
|
silentmode=SilentModeService.State.MODE_NORMAL,
|
|
)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_thermostat_silent_mode(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A thermostat's silent mode is exposed and controllable as a switch."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.thermostats[0]
|
|
|
|
state = hass.states.get("switch.thermostat_whisper_mode")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.thermostat_whisper_mode"},
|
|
blocking=True,
|
|
)
|
|
assert device.silentmode is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.thermostat_whisper_mode"},
|
|
blocking=True,
|
|
)
|
|
assert device.silentmode is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"thermostats": [thermostat_device(supports_silentmode=False)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_thermostat_no_silent_mode_support(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""No switch is created for a thermostat without silent-mode support."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert hass.states.get("switch.thermostat_whisper_mode") is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"micromodule_relays": [micromodule_relay_device(child_lock=False)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_micromodule_relay_child_lock(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A ChildProtection device's bool-based child lock is exposed and controllable."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.micromodule_relays[0]
|
|
|
|
state = hass.states.get("switch.relay_child_lock")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.relay_child_lock"},
|
|
blocking=True,
|
|
)
|
|
assert device.child_lock is True
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"light_switches_bsm": [light_switch_bsm_device(child_lock=False)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_light_switch_bsm_child_lock_unique_id(
|
|
hass: HomeAssistant,
|
|
entity_registry: er.EntityRegistry,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A BSM light switch's primary switch and child-lock switch use distinct unique_ids."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
lightswitch_entry = entity_registry.async_get("switch.light_switch")
|
|
child_lock_entry = entity_registry.async_get("switch.light_switch_child_lock")
|
|
assert lightswitch_entry is not None
|
|
assert child_lock_entry is not None
|
|
assert lightswitch_entry.unique_id != child_lock_entry.unique_id
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"presence_simulation_system": presence_simulation_system_device(enabled=False)}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_presence_simulation_system(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""The presence simulation system is exposed and controllable as a switch."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.presence_simulation_system
|
|
|
|
state = hass.states.get("switch.presence_simulation")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.presence_simulation"},
|
|
blocking=True,
|
|
)
|
|
assert device.enabled is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.presence_simulation"},
|
|
blocking=True,
|
|
)
|
|
assert device.enabled is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"presence_simulation_system": presence_simulation_system_device(enabled=False)}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_presence_simulation_system_push_update(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A controller-side push update reaches the switch without polling."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.presence_simulation_system
|
|
service = device.device_services[0]
|
|
on_state_changed = service.subscribe_callback.call_args.args[1]
|
|
|
|
device.enabled = True
|
|
on_state_changed()
|
|
await hass.async_block_till_done()
|
|
|
|
assert hass.states.get("switch.presence_simulation").state == "on"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"presence_simulation_system": None}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_no_presence_simulation_system(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""No switch is created when the controller has no presence simulation system."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert hass.states.get("switch.presence_simulation") is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"smart_plugs": [smart_plug_device()]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_smart_plug_routing_switch_name(
|
|
hass: HomeAssistant,
|
|
entity_registry: er.EntityRegistry,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""The Smart Plug's routing switch is named "Range extension", not "Routing"."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
entry = entity_registry.async_get("switch.smart_plug_range_extension")
|
|
assert entry is not None
|
|
state = hass.states.get("switch.smart_plug_range_extension")
|
|
assert state is not None
|
|
assert state.attributes["friendly_name"] == "Smart Plug Range extension"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"shutter_contacts2": [
|
|
shutter_contact2_device(bypass=BypassService.State.BYPASS_INACTIVE)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_shutter_contact2_bypass(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Door/Window Contact II's alarm bypass is exposed and controllable."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.shutter_contacts2[0]
|
|
|
|
state = hass.states.get("switch.shutter_contact_break_function")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.shutter_contact_break_function"},
|
|
blocking=True,
|
|
)
|
|
assert device.bypass is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.shutter_contact_break_function"},
|
|
blocking=True,
|
|
)
|
|
assert device.bypass is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"shutter_contacts2": [shutter_contact2_device(bypass_infinite=False)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_shutter_contact2_bypass_infinite(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Door/Window Contact II's bypass-never-expires option is exposed and controllable."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.shutter_contacts2[0]
|
|
|
|
state = hass.states.get("switch.shutter_contact_break_function_never_expires")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.shutter_contact_break_function_never_expires"},
|
|
blocking=True,
|
|
)
|
|
device.set_bypass_configuration.assert_called_once_with(infinite=True)
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.shutter_contact_break_function_never_expires"},
|
|
blocking=True,
|
|
)
|
|
device.set_bypass_configuration.assert_called_with(infinite=False)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"shutter_contacts2": [shutter_contact2_device()]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_shutter_contact2_bypass_unique_id(
|
|
hass: HomeAssistant,
|
|
entity_registry: er.EntityRegistry,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Door/Window Contact II's two bypass switches use distinct unique_ids."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
bypass_entry = entity_registry.async_get("switch.shutter_contact_break_function")
|
|
bypass_infinite_entry = entity_registry.async_get(
|
|
"switch.shutter_contact_break_function_never_expires"
|
|
)
|
|
assert bypass_entry is not None
|
|
assert bypass_infinite_entry is not None
|
|
assert bypass_entry.unique_id != bypass_infinite_entry.unique_id
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"shutter_contacts2": [shutter_contact2_plus_device(vibration_enabled=False)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_shutter_contact2_plus_vibration_enabled(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Door/Window Contact II Plus's vibration detection is exposed and controllable."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.shutter_contacts2[0]
|
|
|
|
state = hass.states.get("switch.shutter_contact_vibration_detection")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.shutter_contact_vibration_detection"},
|
|
blocking=True,
|
|
)
|
|
assert device.enabled is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.shutter_contact_vibration_detection"},
|
|
blocking=True,
|
|
)
|
|
assert device.enabled is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"shutter_contacts2": [shutter_contact2_device()]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_shutter_contact2_no_vibration_enabled_support(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""No vibration switch is created for a plain (non-Plus) Door/Window Contact II."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert hass.states.get("switch.shutter_contact_vibration_detection") is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"motion_detectors2": [motion_detector2_device(pet_immunity_enabled=False)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_motion_detector2_pet_immunity(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Motion Detector 2's pet immunity setting is exposed and controllable."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.motion_detectors2[0]
|
|
|
|
state = hass.states.get("switch.motion_detector_pet_immunity")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.motion_detector_pet_immunity"},
|
|
blocking=True,
|
|
)
|
|
assert device.pet_immunity_enabled is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.motion_detector_pet_immunity"},
|
|
blocking=True,
|
|
)
|
|
assert device.pet_immunity_enabled is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"motion_detectors2": [motion_detector2_device(tamper_protection_enabled=False)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_motion_detector2_tamper_protection(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Motion Detector 2's tamper protection setting is exposed and controllable."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.motion_detectors2[0]
|
|
|
|
state = hass.states.get("switch.motion_detector_sabotage_detection")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.motion_detector_sabotage_detection"},
|
|
blocking=True,
|
|
)
|
|
assert device.tamper_protection_enabled is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.motion_detector_sabotage_detection"},
|
|
blocking=True,
|
|
)
|
|
assert device.tamper_protection_enabled is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"smoke_detectors": [smoke_detector_device(intrusion_alarm=False)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_smoke_detector_intrusion_alarm(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Smoke Detector II's intrusion alarm is exposed and controllable as a switch."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.smoke_detectors[0]
|
|
|
|
state = hass.states.get("switch.smoke_detector_intrusion_alarm")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.smoke_detector_intrusion_alarm"},
|
|
blocking=True,
|
|
)
|
|
assert device.intrusion_alarm is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.smoke_detector_intrusion_alarm"},
|
|
blocking=True,
|
|
)
|
|
assert device.intrusion_alarm is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"smoke_detectors": [smoke_detector_device(supports_intrusion_alarm=False)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_smoke_detector_no_intrusion_alarm_support(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""No switch is created for a gen-1 Smoke Detector without intrusion-alarm support."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert hass.states.get("switch.smoke_detector_intrusion_alarm") is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"twinguards": [
|
|
twinguard_device(
|
|
supports_nightly_promise=True, nightly_promise_enabled=False
|
|
)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_twinguard_nightly_promise(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Twinguard's nightly promise (Heartbeat) is exposed and controllable as a switch."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.twinguards[0]
|
|
|
|
state = hass.states.get("switch.twinguard_heartbeat")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.twinguard_heartbeat"},
|
|
blocking=True,
|
|
)
|
|
assert device.nightly_promise_enabled is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.twinguard_heartbeat"},
|
|
blocking=True,
|
|
)
|
|
assert device.nightly_promise_enabled is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"twinguards": [twinguard_device(supports_nightly_promise=False)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_twinguard_no_nightly_promise_support(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""No switch is created for a Twinguard without nightly-promise support."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert hass.states.get("switch.twinguard_heartbeat") is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"thermostats": [
|
|
thermostat_gen2_device(
|
|
supports_display_configuration=True,
|
|
humidity_warning_enabled=False,
|
|
)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_thermostat_gen2_humidity_warning(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Thermostat Gen2's humidity warning is exposed and controllable as a switch."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.thermostats[0]
|
|
|
|
state = hass.states.get("switch.thermostat_gen2_humidity_warning")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.thermostat_gen2_humidity_warning"},
|
|
blocking=True,
|
|
)
|
|
assert device.humidity_warning_enabled is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.thermostat_gen2_humidity_warning"},
|
|
blocking=True,
|
|
)
|
|
assert device.humidity_warning_enabled is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"thermostats": [thermostat_device()]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_thermostat_no_humidity_warning_support(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""No switch is created for a Gen-1 thermostat, which lacks display-configuration support."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert hass.states.get("switch.thermostat_gen2_humidity_warning") is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"roomthermostats": [
|
|
thermostat_gen2_device(
|
|
device_id="hdm:ZigBee:roomthermostatgen2_1",
|
|
name="Room Thermostat 2",
|
|
supports_display_configuration=True,
|
|
humidity_warning_enabled=False,
|
|
)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_roomthermostat_gen2_humidity_warning(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Room Thermostat 2's humidity warning is exposed and controllable as a switch."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.roomthermostats[0]
|
|
|
|
state = hass.states.get("switch.room_thermostat_2_humidity_warning")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.room_thermostat_2_humidity_warning"},
|
|
blocking=True,
|
|
)
|
|
assert device.humidity_warning_enabled is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.room_thermostat_2_humidity_warning"},
|
|
blocking=True,
|
|
)
|
|
assert device.humidity_warning_enabled is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"smart_plugs": [
|
|
smart_plug_device(
|
|
supports_energy_saving_mode=True, energy_saving_mode_enabled=False
|
|
)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_smart_plug_energy_saving_mode(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Smart Plug's energy saving mode is exposed and controllable as a switch."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.smart_plugs[0]
|
|
|
|
state = hass.states.get("switch.smart_plug_energy_saving_mode")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.smart_plug_energy_saving_mode"},
|
|
blocking=True,
|
|
)
|
|
assert device.energy_saving_mode_enabled is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.smart_plug_energy_saving_mode"},
|
|
blocking=True,
|
|
)
|
|
assert device.energy_saving_mode_enabled is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"smart_plugs": [smart_plug_device(supports_energy_saving_mode=False)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_smart_plug_no_energy_saving_mode_support(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""No switch is created for a Smart Plug without energy-saving-mode support."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert hass.states.get("switch.smart_plug_energy_saving_mode") is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"smart_plugs_compact": [
|
|
smart_plug_compact_device(
|
|
supports_energy_saving_mode=True, energy_saving_mode_enabled=False
|
|
)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_smart_plug_compact_energy_saving_mode(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Smart Plug Compact's energy saving mode is exposed and controllable as a switch."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.smart_plugs_compact[0]
|
|
|
|
state = hass.states.get("switch.smart_plug_compact_energy_saving_mode")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.smart_plug_compact_energy_saving_mode"},
|
|
blocking=True,
|
|
)
|
|
assert device.energy_saving_mode_enabled is True
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"motion_detectors2": [
|
|
motion_detector2_device(
|
|
supports_smart_sensitivity=True, smart_sensitivity_enabled=False
|
|
)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_motion_detector2_smart_sensitivity(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Motion Detector 2's automatic sensitivity is exposed and controllable."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.motion_detectors2[0]
|
|
|
|
state = hass.states.get("switch.motion_detector_automatic_sensitivity")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.motion_detector_automatic_sensitivity"},
|
|
blocking=True,
|
|
)
|
|
assert device.smart_sensitivity_enabled is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.motion_detector_automatic_sensitivity"},
|
|
blocking=True,
|
|
)
|
|
assert device.smart_sensitivity_enabled is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"motion_detectors2": [
|
|
motion_detector2_device(supports_smart_sensitivity=False)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_motion_detector2_no_smart_sensitivity_support(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""No switch is created for a Motion Detector 2 without smart-sensitivity support."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert hass.states.get("switch.motion_detector_automatic_sensitivity") is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"micromodule_relays": [
|
|
micromodule_relay_device(
|
|
supports_switch_configuration=True, swap_inputs=False
|
|
)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_micromodule_relay_swap_inputs(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Micromodule Relay's swap-inputs setting is exposed and controllable."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.micromodule_relays[0]
|
|
|
|
state = hass.states.get("switch.relay_swap_inputs")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.relay_swap_inputs"},
|
|
blocking=True,
|
|
)
|
|
assert device.swap_inputs is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.relay_swap_inputs"},
|
|
blocking=True,
|
|
)
|
|
assert device.swap_inputs is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"micromodule_relays": [
|
|
micromodule_relay_device(
|
|
supports_switch_configuration=True, swap_outputs=False
|
|
)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_micromodule_relay_swap_outputs(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Micromodule Relay's swap-outputs setting is exposed and controllable."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.micromodule_relays[0]
|
|
|
|
state = hass.states.get("switch.relay_swap_outputs")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.relay_swap_outputs"},
|
|
blocking=True,
|
|
)
|
|
assert device.swap_outputs is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.relay_swap_outputs"},
|
|
blocking=True,
|
|
)
|
|
assert device.swap_outputs is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[
|
|
{
|
|
"micromodule_relays": [
|
|
micromodule_relay_device(supports_switch_configuration=False)
|
|
]
|
|
}
|
|
],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_micromodule_relay_no_switch_configuration_support(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""No swap-inputs/outputs switches are created without switch-configuration support."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert hass.states.get("switch.relay_swap_inputs") is None
|
|
assert hass.states.get("switch.relay_swap_outputs") is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"device_buckets",
|
|
[{"camera_eyes": [camera_eyes_device(cameralight=CameraLightService.State.OFF)]}],
|
|
indirect=True,
|
|
)
|
|
@pytest.mark.usefixtures("mock_session")
|
|
async def test_camera_eyes_cameralight(
|
|
hass: HomeAssistant,
|
|
mock_session: MagicMock,
|
|
mock_config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""A Camera Eyes' camera light is exposed and controllable as a switch."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
device = mock_session.device_helper.camera_eyes[0]
|
|
|
|
state = hass.states.get("switch.camera_eyes_camera_light")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: "switch.camera_eyes_camera_light"},
|
|
blocking=True,
|
|
)
|
|
assert device.cameralight is True
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: "switch.camera_eyes_camera_light"},
|
|
blocking=True,
|
|
)
|
|
assert device.cameralight is False
|