core/tests/components/wemo/test_wemo_device.py

264 lines
8.9 KiB
Python

"""Tests for wemo_device.py."""
import asyncio
from dataclasses import asdict
from datetime import timedelta
from unittest.mock import call, patch
import pytest
from pywemo.exceptions import ActionException, PyWeMoException
from pywemo.subscribe import EVENT_TYPE_LONG_PRESS
from homeassistant import runner
from homeassistant.components.wemo import CONF_DISCOVERY, CONF_STATIC, wemo_device
from homeassistant.components.wemo.const import DOMAIN, WEMO_SUBSCRIPTION_EVENT
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.update_coordinator import UpdateFailed
from homeassistant.setup import async_setup_component
from homeassistant.util.dt import utcnow
from .conftest import MOCK_FIRMWARE_VERSION, MOCK_HOST, MOCK_SERIAL_NUMBER
from tests.common import async_fire_time_changed
asyncio.set_event_loop_policy(runner.HassEventLoopPolicy(True))
@pytest.fixture
def pywemo_model():
"""Pywemo LightSwitch models use the switch platform."""
return "LightSwitchLongPress"
async def test_async_register_device_longpress_fails(
hass: HomeAssistant, pywemo_device, device_registry: dr.DeviceRegistry
) -> None:
"""Device is still registered if ensure_long_press_virtual_device fails."""
with patch.object(pywemo_device, "ensure_long_press_virtual_device") as elp:
elp.side_effect = PyWeMoException
assert await async_setup_component(
hass,
DOMAIN,
{
DOMAIN: {
CONF_DISCOVERY: False,
CONF_STATIC: [MOCK_HOST],
},
},
)
await hass.async_block_till_done()
device_entries = list(device_registry.devices.values())
assert len(device_entries) == 1
device = wemo_device.async_get_coordinator(hass, device_entries[0].id)
assert device.supports_long_press is False
async def test_long_press_event(
hass: HomeAssistant, pywemo_registry, wemo_entity
) -> None:
"""Device fires a long press event."""
device = wemo_device.async_get_coordinator(hass, wemo_entity.device_id)
got_event = asyncio.Event()
event_data = {}
@callback
def async_event_received(event):
nonlocal event_data
event_data = event.data
got_event.set()
hass.bus.async_listen_once(WEMO_SUBSCRIPTION_EVENT, async_event_received)
await hass.async_add_executor_job(
pywemo_registry.callbacks[device.wemo.name],
device.wemo,
EVENT_TYPE_LONG_PRESS,
"testing_params",
)
async with asyncio.timeout(8):
await got_event.wait()
assert event_data == {
"device_id": wemo_entity.device_id,
"name": device.wemo.name,
"params": "testing_params",
"type": EVENT_TYPE_LONG_PRESS,
"unique_id": device.wemo.serial_number,
}
async def test_subscription_callback(
hass: HomeAssistant, pywemo_registry, wemo_entity
) -> None:
"""Device processes a registry subscription callback."""
device = wemo_device.async_get_coordinator(hass, wemo_entity.device_id)
device.last_update_success = False
got_callback = asyncio.Event()
@callback
def async_received_callback():
got_callback.set()
device.async_add_listener(async_received_callback)
await hass.async_add_executor_job(
pywemo_registry.callbacks[device.wemo.name], device.wemo, "", ""
)
async with asyncio.timeout(8):
await got_callback.wait()
assert device.last_update_success
async def test_subscription_update_action_exception(
hass: HomeAssistant, pywemo_device, wemo_entity
) -> None:
"""Device handles ActionException on get_state properly."""
device = wemo_device.async_get_coordinator(hass, wemo_entity.device_id)
device.last_update_success = True
pywemo_device.subscription_update.return_value = False
pywemo_device.get_state.reset_mock()
pywemo_device.get_state.side_effect = ActionException
await hass.async_add_executor_job(
device.subscription_callback, pywemo_device, "", ""
)
await hass.async_block_till_done()
pywemo_device.get_state.assert_called_once_with(True)
assert device.last_update_success is False
assert isinstance(device.last_exception, UpdateFailed)
async def test_subscription_update_exception(
hass: HomeAssistant, pywemo_device, wemo_entity
) -> None:
"""Device handles Exception on get_state properly."""
device = wemo_device.async_get_coordinator(hass, wemo_entity.device_id)
device.last_update_success = True
pywemo_device.subscription_update.return_value = False
pywemo_device.get_state.reset_mock()
pywemo_device.get_state.side_effect = Exception
await hass.async_add_executor_job(
device.subscription_callback, pywemo_device, "", ""
)
await hass.async_block_till_done()
pywemo_device.get_state.assert_called_once_with(True)
assert device.last_update_success is False
assert isinstance(device.last_exception, Exception)
async def test_async_update_data_subscribed(
hass: HomeAssistant, pywemo_registry, pywemo_device, wemo_entity
) -> None:
"""No update happens when the device is subscribed."""
device = wemo_device.async_get_coordinator(hass, wemo_entity.device_id)
pywemo_registry.is_subscribed.return_value = True
pywemo_device.get_state.reset_mock()
await device._async_update_data()
pywemo_device.get_state.assert_not_called()
async def test_device_info(
hass: HomeAssistant, wemo_entity, device_registry: dr.DeviceRegistry
) -> None:
"""Verify the DeviceInfo data is set properly."""
device_entries = list(device_registry.devices.values())
assert len(device_entries) == 1
assert device_entries[0].connections == {
("upnp", f"uuid:LightSwitch-1_0-{MOCK_SERIAL_NUMBER}")
}
assert device_entries[0].manufacturer == "Belkin"
assert device_entries[0].model == "LightSwitch"
assert device_entries[0].sw_version == MOCK_FIRMWARE_VERSION
async def test_dli_device_info(
hass: HomeAssistant, wemo_dli_entity, device_registry: dr.DeviceRegistry
) -> None:
"""Verify the DeviceInfo data for Digital Loggers emulated wemo device."""
device_entries = list(device_registry.devices.values())
assert device_entries[0].configuration_url == "http://127.0.0.1"
assert device_entries[0].identifiers == {(DOMAIN, "123456789")}
async def test_options_enable_subscription_false(
hass, pywemo_registry, pywemo_device, wemo_entity
):
"""Test setting Options.enable_subscription = False."""
config_entry = hass.config_entries.async_get_entry(wemo_entity.config_entry_id)
assert hass.config_entries.async_update_entry(
config_entry,
options=asdict(
wemo_device.Options(enable_subscription=False, enable_long_press=False)
),
)
await hass.async_block_till_done()
pywemo_registry.unregister.assert_called_once_with(pywemo_device)
async def test_options_enable_long_press_false(hass, pywemo_device, wemo_entity):
"""Test setting Options.enable_long_press = False."""
config_entry = hass.config_entries.async_get_entry(wemo_entity.config_entry_id)
assert hass.config_entries.async_update_entry(
config_entry, options=asdict(wemo_device.Options(enable_long_press=False))
)
await hass.async_block_till_done()
pywemo_device.remove_long_press_virtual_device.assert_called_once_with()
class TestInsight:
"""Tests specific to the WeMo Insight device."""
@pytest.fixture
def pywemo_model(self):
"""Pywemo Dimmer models use the light platform (WemoDimmer class)."""
return "Insight"
@pytest.fixture(name="pywemo_device")
def pywemo_device_fixture(self, pywemo_device):
"""Fixture for WeMoDevice instances."""
pywemo_device.insight_params = {
"currentpower": 1.0,
"todaymw": 200000000.0,
"state": 0,
"onfor": 0,
"ontoday": 0,
"ontotal": 0,
"powerthreshold": 0,
}
return pywemo_device
@pytest.mark.parametrize(
("subscribed", "state", "expected_calls"),
[
(False, 0, [call(), call(True), call(), call()]),
(False, 1, [call(), call(True), call(), call()]),
(True, 0, [call(), call(True), call(), call()]),
(True, 1, [call(), call(), call()]),
],
)
async def test_should_poll(
self,
hass,
subscribed,
state,
expected_calls,
wemo_entity,
pywemo_device,
pywemo_registry,
):
"""Validate the should_poll returns the correct value."""
pywemo_registry.is_subscribed.return_value = subscribed
pywemo_device.get_state.reset_mock()
pywemo_device.get_state.return_value = state
async_fire_time_changed(hass, utcnow() + timedelta(seconds=31))
await hass.async_block_till_done()
pywemo_device.get_state.assert_has_calls(expected_calls)