core/tests/components/husqvarna_automower/test_switch.py

282 lines
9.1 KiB
Python

"""Tests for switch platform."""
from datetime import timedelta
from unittest.mock import AsyncMock, patch
import zoneinfo
from aioautomower.exceptions import ApiException
from aioautomower.model import MowerAttributes, MowerModes, Zone
from aioautomower.utils import mower_list_to_dictionary_dataclass
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.husqvarna_automower.const import (
DOMAIN,
EXECUTION_TIME_DELAY,
)
from homeassistant.components.husqvarna_automower.coordinator import SCAN_INTERVAL
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from .const import TEST_MOWER_ID
from tests.common import (
MockConfigEntry,
async_fire_time_changed,
load_json_value_fixture,
snapshot_platform,
)
TEST_AREA_ID = 0
TEST_VARIABLE_ZONE_ID = "203F6359-AB56-4D57-A6DC-703095BB695D"
TEST_ZONE_ID = "AAAAAAAA-BBBB-CCCC-DDDD-123456789101"
async def test_switch_states(
hass: HomeAssistant,
mock_automower_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
values: dict[str, MowerAttributes],
) -> None:
"""Test switch state."""
await setup_integration(hass, mock_config_entry)
for mode, expected_state in (
(MowerModes.HOME, "off"),
(MowerModes.MAIN_AREA, "on"),
):
values[TEST_MOWER_ID].mower.mode = mode
mock_automower_client.get_status.return_value = values
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get("switch.test_mower_1_enable_schedule")
assert state.state == expected_state
@pytest.mark.parametrize(
("service", "aioautomower_command"),
[
(SERVICE_TURN_OFF, "park_until_further_notice"),
(SERVICE_TURN_ON, "resume_schedule"),
(SERVICE_TOGGLE, "park_until_further_notice"),
],
)
async def test_switch_commands(
hass: HomeAssistant,
aioautomower_command: str,
service: str,
mock_automower_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test switch commands."""
await setup_integration(hass, mock_config_entry)
await hass.services.async_call(
domain=SWITCH_DOMAIN,
service=service,
service_data={ATTR_ENTITY_ID: "switch.test_mower_1_enable_schedule"},
blocking=True,
)
mocked_method = getattr(mock_automower_client.commands, aioautomower_command)
mocked_method.assert_called_once_with(TEST_MOWER_ID)
mocked_method.side_effect = ApiException("Test error")
with pytest.raises(
HomeAssistantError,
match="Failed to send command: Test error",
):
await hass.services.async_call(
domain=SWITCH_DOMAIN,
service=service,
service_data={ATTR_ENTITY_ID: "switch.test_mower_1_enable_schedule"},
blocking=True,
)
assert len(mocked_method.mock_calls) == 2
@pytest.mark.parametrize(
("service", "boolean", "excepted_state"),
[
(SERVICE_TURN_OFF, False, "off"),
(SERVICE_TURN_ON, True, "on"),
(SERVICE_TOGGLE, True, "on"),
],
)
async def test_stay_out_zone_switch_commands(
hass: HomeAssistant,
service: str,
boolean: bool,
excepted_state: str,
mock_automower_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
mower_time_zone: zoneinfo.ZoneInfo,
) -> None:
"""Test switch commands."""
entity_id = "switch.test_mower_1_avoid_danger_zone"
await setup_integration(hass, mock_config_entry)
values = mower_list_to_dictionary_dataclass(
load_json_value_fixture("mower.json", DOMAIN),
mower_time_zone,
)
values[TEST_MOWER_ID].stay_out_zones.zones[TEST_ZONE_ID].enabled = boolean
mock_automower_client.get_status.return_value = values
mocked_method = AsyncMock()
setattr(mock_automower_client.commands, "switch_stay_out_zone", mocked_method)
await hass.services.async_call(
domain=SWITCH_DOMAIN,
service=service,
service_data={ATTR_ENTITY_ID: entity_id},
blocking=False,
)
freezer.tick(timedelta(seconds=EXECUTION_TIME_DELAY))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mocked_method.assert_called_once_with(TEST_MOWER_ID, TEST_ZONE_ID, boolean)
state = hass.states.get(entity_id)
assert state is not None
assert state.state == excepted_state
mocked_method.side_effect = ApiException("Test error")
with pytest.raises(
HomeAssistantError,
match="Failed to send command: Test error",
):
await hass.services.async_call(
domain=SWITCH_DOMAIN,
service=service,
service_data={ATTR_ENTITY_ID: entity_id},
blocking=True,
)
assert len(mocked_method.mock_calls) == 2
@pytest.mark.parametrize(
("service", "boolean", "excepted_state"),
[
(SERVICE_TURN_OFF, False, "off"),
(SERVICE_TURN_ON, True, "on"),
(SERVICE_TOGGLE, True, "on"),
],
)
async def test_work_area_switch_commands(
hass: HomeAssistant,
service: str,
boolean: bool,
excepted_state: str,
mock_automower_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
mower_time_zone: zoneinfo.ZoneInfo,
values: dict[str, MowerAttributes],
) -> None:
"""Test switch commands."""
entity_id = "switch.test_mower_1_my_lawn"
await setup_integration(hass, mock_config_entry)
values = mower_list_to_dictionary_dataclass(
load_json_value_fixture("mower.json", DOMAIN),
mower_time_zone,
)
values[TEST_MOWER_ID].work_areas[TEST_AREA_ID].enabled = boolean
mock_automower_client.get_status.return_value = values
mocked_method = AsyncMock()
setattr(mock_automower_client.commands, "workarea_settings", mocked_method)
await hass.services.async_call(
domain=SWITCH_DOMAIN,
service=service,
service_data={ATTR_ENTITY_ID: entity_id},
blocking=False,
)
freezer.tick(timedelta(seconds=EXECUTION_TIME_DELAY))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mocked_method.assert_called_once_with(TEST_MOWER_ID, TEST_AREA_ID, enabled=boolean)
state = hass.states.get(entity_id)
assert state is not None
assert state.state == excepted_state
mocked_method.side_effect = ApiException("Test error")
with pytest.raises(
HomeAssistantError,
match="Failed to send command: Test error",
):
await hass.services.async_call(
domain=SWITCH_DOMAIN,
service=service,
service_data={ATTR_ENTITY_ID: entity_id},
blocking=True,
)
assert len(mocked_method.mock_calls) == 2
async def test_add_stay_out_zone(
hass: HomeAssistant,
mock_automower_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
entity_registry: er.EntityRegistry,
values: dict[str, MowerAttributes],
) -> None:
"""Test adding a stay out zone in runtime."""
await setup_integration(hass, mock_config_entry)
entry = hass.config_entries.async_entries(DOMAIN)[0]
current_entites = len(
er.async_entries_for_config_entry(entity_registry, entry.entry_id)
)
values[TEST_MOWER_ID].stay_out_zones.zones.update(
{
TEST_VARIABLE_ZONE_ID: Zone(
name="future_zone",
enabled=True,
)
}
)
mock_automower_client.get_status.return_value = values
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
current_entites_after_addition = len(
er.async_entries_for_config_entry(entity_registry, entry.entry_id)
)
assert current_entites_after_addition == current_entites + 1
values[TEST_MOWER_ID].stay_out_zones.zones.pop(TEST_VARIABLE_ZONE_ID)
values[TEST_MOWER_ID].stay_out_zones.zones.pop(TEST_ZONE_ID)
mock_automower_client.get_status.return_value = values
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
current_entites_after_deletion = len(
er.async_entries_for_config_entry(entity_registry, entry.entry_id)
)
assert current_entites_after_deletion == current_entites - 1
async def test_switch_snapshot(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
mock_automower_client: AsyncMock,
mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
) -> None:
"""Snapshot tests of the switches."""
with patch(
"homeassistant.components.husqvarna_automower.PLATFORMS",
[Platform.SWITCH],
):
await setup_integration(hass, mock_config_entry)
await snapshot_platform(
hass, entity_registry, snapshot, mock_config_entry.entry_id
)