hass-core/tests/components/deconz/test_services.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

396 lines
12 KiB
Python
Raw Normal View History

"""deCONZ service tests."""
from unittest.mock import patch
import pytest
import voluptuous as vol
from homeassistant.components.deconz.const import (
CONF_BRIDGE_ID,
CONF_MASTER_GATEWAY,
DOMAIN as DECONZ_DOMAIN,
)
from homeassistant.components.deconz.deconz_event import CONF_DECONZ_EVENT
from homeassistant.components.deconz.services import (
SERVICE_CONFIGURE_DEVICE,
SERVICE_DATA,
SERVICE_DEVICE_REFRESH,
SERVICE_ENTITY,
SERVICE_FIELD,
SERVICE_REMOVE_ORPHANED_ENTRIES,
)
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from .test_gateway import (
BRIDGEID,
DECONZ_WEB_REQUEST,
mock_deconz_put_request,
mock_deconz_request,
setup_deconz_integration,
)
from tests.common import async_capture_events
from tests.test_util.aiohttp import AiohttpClientMocker
async def test_configure_service_with_field(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test that service invokes pydeconz with the correct path and data."""
config_entry = await setup_deconz_integration(hass, aioclient_mock)
data = {
SERVICE_FIELD: "/lights/2",
CONF_BRIDGE_ID: BRIDGEID,
SERVICE_DATA: {"on": True, "attr1": 10, "attr2": 20},
}
mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/2")
await hass.services.async_call(
DECONZ_DOMAIN, SERVICE_CONFIGURE_DEVICE, service_data=data, blocking=True
)
assert aioclient_mock.mock_calls[1][2] == {"on": True, "attr1": 10, "attr2": 20}
async def test_configure_service_with_entity(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test that service invokes pydeconz with the correct path and data."""
data = {
"lights": {
"1": {
"name": "Test",
"state": {"reachable": True},
"type": "Light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
}
}
with patch.dict(DECONZ_WEB_REQUEST, data):
config_entry = await setup_deconz_integration(hass, aioclient_mock)
data = {
SERVICE_ENTITY: "light.test",
SERVICE_DATA: {"on": True, "attr1": 10, "attr2": 20},
}
mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/1")
await hass.services.async_call(
DECONZ_DOMAIN, SERVICE_CONFIGURE_DEVICE, service_data=data, blocking=True
)
assert aioclient_mock.mock_calls[1][2] == {"on": True, "attr1": 10, "attr2": 20}
async def test_configure_service_with_entity_and_field(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test that service invokes pydeconz with the correct path and data."""
data = {
"lights": {
"1": {
"name": "Test",
"state": {"reachable": True},
"type": "Light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
}
}
with patch.dict(DECONZ_WEB_REQUEST, data):
config_entry = await setup_deconz_integration(hass, aioclient_mock)
data = {
SERVICE_ENTITY: "light.test",
SERVICE_FIELD: "/state",
SERVICE_DATA: {"on": True, "attr1": 10, "attr2": 20},
}
mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/1/state")
await hass.services.async_call(
DECONZ_DOMAIN, SERVICE_CONFIGURE_DEVICE, service_data=data, blocking=True
)
assert aioclient_mock.mock_calls[1][2] == {"on": True, "attr1": 10, "attr2": 20}
async def test_configure_service_with_faulty_bridgeid(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test that service fails on a bad bridge id."""
await setup_deconz_integration(hass, aioclient_mock)
aioclient_mock.clear_requests()
data = {
CONF_BRIDGE_ID: "Bad bridge id",
SERVICE_FIELD: "/lights/1",
SERVICE_DATA: {"on": True},
}
await hass.services.async_call(
DECONZ_DOMAIN, SERVICE_CONFIGURE_DEVICE, service_data=data
)
await hass.async_block_till_done()
assert len(aioclient_mock.mock_calls) == 0
async def test_configure_service_with_faulty_field(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test that service fails on a bad field."""
await setup_deconz_integration(hass, aioclient_mock)
data = {SERVICE_FIELD: "light/2", SERVICE_DATA: {}}
with pytest.raises(vol.Invalid):
await hass.services.async_call(
DECONZ_DOMAIN, SERVICE_CONFIGURE_DEVICE, service_data=data
)
async def test_configure_service_with_faulty_entity(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test that service on a non existing entity."""
await setup_deconz_integration(hass, aioclient_mock)
aioclient_mock.clear_requests()
data = {
SERVICE_ENTITY: "light.nonexisting",
SERVICE_DATA: {},
}
await hass.services.async_call(
DECONZ_DOMAIN, SERVICE_CONFIGURE_DEVICE, service_data=data
)
await hass.async_block_till_done()
assert len(aioclient_mock.mock_calls) == 0
async def test_calling_service_with_no_master_gateway_fails(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test that service call fails when no master gateway exist."""
await setup_deconz_integration(
hass, aioclient_mock, options={CONF_MASTER_GATEWAY: False}
)
aioclient_mock.clear_requests()
data = {
SERVICE_FIELD: "/lights/1",
SERVICE_DATA: {"on": True},
}
await hass.services.async_call(
DECONZ_DOMAIN, SERVICE_CONFIGURE_DEVICE, service_data=data
)
await hass.async_block_till_done()
assert len(aioclient_mock.mock_calls) == 0
async def test_service_refresh_devices(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test that service can refresh devices."""
config_entry = await setup_deconz_integration(hass, aioclient_mock)
assert len(hass.states.async_all()) == 0
aioclient_mock.clear_requests()
data = {
2022-04-05 22:44:04 +02:00
"config": {},
"groups": {
"1": {
"id": "Group 1 id",
"name": "Group 1 name",
"type": "LightGroup",
"state": {},
"action": {},
"scenes": [{"id": "1", "name": "Scene 1"}],
"lights": ["1"],
}
},
"lights": {
"1": {
"name": "Light 1 name",
"state": {"reachable": True},
"type": "Light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
},
"sensors": {
"1": {
"name": "Sensor 1 name",
"type": "ZHALightLevel",
"state": {"lightlevel": 30000, "dark": False},
"config": {"reachable": True},
"uniqueid": "00:00:00:00:00:00:00:02-00",
}
},
}
mock_deconz_request(aioclient_mock, config_entry.data, data)
await hass.services.async_call(
DECONZ_DOMAIN, SERVICE_DEVICE_REFRESH, service_data={CONF_BRIDGE_ID: BRIDGEID}
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 5
async def test_service_refresh_devices_trigger_no_state_update(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Verify that gateway.ignore_state_updates are honored."""
data = {
"sensors": {
"1": {
"name": "Switch 1",
"type": "ZHASwitch",
"state": {"buttonevent": 1000},
"config": {"battery": 100},
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
}
}
with patch.dict(DECONZ_WEB_REQUEST, data):
config_entry = await setup_deconz_integration(hass, aioclient_mock)
assert len(hass.states.async_all()) == 1
captured_events = async_capture_events(hass, CONF_DECONZ_EVENT)
aioclient_mock.clear_requests()
data = {
2022-04-05 22:44:04 +02:00
"config": {},
"groups": {
"1": {
"id": "Group 1 id",
"name": "Group 1 name",
"type": "LightGroup",
"state": {},
"action": {},
"scenes": [{"id": "1", "name": "Scene 1"}],
"lights": ["1"],
}
},
"lights": {
"1": {
"name": "Light 1 name",
"state": {"reachable": True},
"type": "Light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
},
"sensors": {
"1": {
"name": "Switch 1",
"type": "ZHASwitch",
"state": {"buttonevent": 1000},
"config": {"battery": 100},
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
},
}
mock_deconz_request(aioclient_mock, config_entry.data, data)
await hass.services.async_call(
DECONZ_DOMAIN, SERVICE_DEVICE_REFRESH, service_data={CONF_BRIDGE_ID: BRIDGEID}
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 5
assert len(captured_events) == 0
async def test_remove_orphaned_entries_service(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
aioclient_mock: AiohttpClientMocker,
) -> None:
"""Test service works and also don't remove more than expected."""
data = {
"lights": {
"1": {
"name": "Light 1 name",
"state": {"reachable": True},
"type": "Light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
}
},
"sensors": {
"1": {
"name": "Switch 1",
"type": "ZHASwitch",
"state": {"buttonevent": 1000, "gesture": 1},
"config": {"battery": 100},
"uniqueid": "00:00:00:00:00:00:00:03-00",
},
},
}
with patch.dict(DECONZ_WEB_REQUEST, data):
config_entry = await setup_deconz_integration(hass, aioclient_mock)
device = device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, "123")},
)
assert (
len(
[
entry
for entry in device_registry.devices.values()
if config_entry.entry_id in entry.config_entries
]
)
== 5 # Host, gateway, light, switch and orphan
)
entity_registry.async_get_or_create(
SENSOR_DOMAIN,
DECONZ_DOMAIN,
"12345",
suggested_object_id="Orphaned sensor",
config_entry=config_entry,
device_id=device.id,
)
assert (
len(er.async_entries_for_config_entry(entity_registry, config_entry.entry_id))
== 3 # Light, switch battery and orphan
)
await hass.services.async_call(
DECONZ_DOMAIN,
SERVICE_REMOVE_ORPHANED_ENTRIES,
service_data={CONF_BRIDGE_ID: BRIDGEID},
)
await hass.async_block_till_done()
assert (
len(
[
entry
for entry in device_registry.devices.values()
if config_entry.entry_id in entry.config_entries
]
)
== 4 # Host, gateway, light and switch
)
assert (
len(er.async_entries_for_config_entry(entity_registry, config_entry.entry_id))
== 2 # Light and switch battery
)