2022-01-10 21:04:53 +00:00
|
|
|
"""Test the UniFi Protect global services."""
|
|
|
|
# pylint: disable=protected-access
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
from unittest.mock import AsyncMock, Mock
|
|
|
|
|
|
|
|
import pytest
|
2022-05-20 20:16:01 +00:00
|
|
|
from pyunifiprotect.data import Camera, Light, ModelType
|
|
|
|
from pyunifiprotect.data.devices import Chime
|
2022-01-10 21:04:53 +00:00
|
|
|
from pyunifiprotect.exceptions import BadRequest
|
|
|
|
|
2022-01-11 22:37:47 +00:00
|
|
|
from homeassistant.components.unifiprotect.const import ATTR_MESSAGE, DOMAIN
|
|
|
|
from homeassistant.components.unifiprotect.services import (
|
2022-01-10 21:04:53 +00:00
|
|
|
SERVICE_ADD_DOORBELL_TEXT,
|
|
|
|
SERVICE_REMOVE_DOORBELL_TEXT,
|
2022-05-20 20:16:01 +00:00
|
|
|
SERVICE_SET_CHIME_PAIRED,
|
2022-01-10 21:04:53 +00:00
|
|
|
SERVICE_SET_DEFAULT_DOORBELL_TEXT,
|
|
|
|
)
|
2022-05-20 20:16:01 +00:00
|
|
|
from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID
|
2022-01-10 21:04:53 +00:00
|
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
from homeassistant.exceptions import HomeAssistantError
|
2022-05-20 20:16:01 +00:00
|
|
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
2022-01-10 21:04:53 +00:00
|
|
|
|
2022-06-19 14:22:33 +00:00
|
|
|
from .conftest import MockEntityFixture, regenerate_device_ids
|
2022-01-10 21:04:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(name="device")
|
|
|
|
async def device_fixture(hass: HomeAssistant, mock_entry: MockEntityFixture):
|
|
|
|
"""Fixture with entry setup to call services with."""
|
|
|
|
|
|
|
|
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2022-05-18 11:12:38 +00:00
|
|
|
device_registry = dr.async_get(hass)
|
2022-01-10 21:04:53 +00:00
|
|
|
|
|
|
|
return list(device_registry.devices.values())[0]
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(name="subdevice")
|
|
|
|
async def subdevice_fixture(
|
|
|
|
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light
|
|
|
|
):
|
|
|
|
"""Fixture with entry setup to call services with."""
|
|
|
|
|
|
|
|
mock_light._api = mock_entry.api
|
|
|
|
mock_entry.api.bootstrap.lights = {
|
|
|
|
mock_light.id: mock_light,
|
|
|
|
}
|
|
|
|
|
|
|
|
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2022-05-18 11:12:38 +00:00
|
|
|
device_registry = dr.async_get(hass)
|
2022-01-10 21:04:53 +00:00
|
|
|
|
|
|
|
return [d for d in device_registry.devices.values() if d.name != "UnifiProtect"][0]
|
|
|
|
|
|
|
|
|
|
|
|
async def test_global_service_bad_device(
|
|
|
|
hass: HomeAssistant, device: dr.DeviceEntry, mock_entry: MockEntityFixture
|
|
|
|
):
|
|
|
|
"""Test global service, invalid device ID."""
|
|
|
|
|
|
|
|
nvr = mock_entry.api.bootstrap.nvr
|
|
|
|
nvr.__fields__["add_custom_doorbell_message"] = Mock()
|
|
|
|
nvr.add_custom_doorbell_message = AsyncMock()
|
|
|
|
|
|
|
|
with pytest.raises(HomeAssistantError):
|
|
|
|
await hass.services.async_call(
|
|
|
|
DOMAIN,
|
|
|
|
SERVICE_ADD_DOORBELL_TEXT,
|
|
|
|
{ATTR_DEVICE_ID: "bad_device_id", ATTR_MESSAGE: "Test Message"},
|
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
assert not nvr.add_custom_doorbell_message.called
|
|
|
|
|
|
|
|
|
|
|
|
async def test_global_service_exception(
|
|
|
|
hass: HomeAssistant, device: dr.DeviceEntry, mock_entry: MockEntityFixture
|
|
|
|
):
|
|
|
|
"""Test global service, unexpected error."""
|
|
|
|
|
|
|
|
nvr = mock_entry.api.bootstrap.nvr
|
|
|
|
nvr.__fields__["add_custom_doorbell_message"] = Mock()
|
|
|
|
nvr.add_custom_doorbell_message = AsyncMock(side_effect=BadRequest)
|
|
|
|
|
|
|
|
with pytest.raises(HomeAssistantError):
|
|
|
|
await hass.services.async_call(
|
|
|
|
DOMAIN,
|
|
|
|
SERVICE_ADD_DOORBELL_TEXT,
|
|
|
|
{ATTR_DEVICE_ID: device.id, ATTR_MESSAGE: "Test Message"},
|
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
assert nvr.add_custom_doorbell_message.called
|
|
|
|
|
|
|
|
|
|
|
|
async def test_add_doorbell_text(
|
|
|
|
hass: HomeAssistant, device: dr.DeviceEntry, mock_entry: MockEntityFixture
|
|
|
|
):
|
|
|
|
"""Test add_doorbell_text service."""
|
|
|
|
|
|
|
|
nvr = mock_entry.api.bootstrap.nvr
|
|
|
|
nvr.__fields__["add_custom_doorbell_message"] = Mock()
|
|
|
|
nvr.add_custom_doorbell_message = AsyncMock()
|
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
DOMAIN,
|
|
|
|
SERVICE_ADD_DOORBELL_TEXT,
|
|
|
|
{ATTR_DEVICE_ID: device.id, ATTR_MESSAGE: "Test Message"},
|
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
nvr.add_custom_doorbell_message.assert_called_once_with("Test Message")
|
|
|
|
|
|
|
|
|
|
|
|
async def test_remove_doorbell_text(
|
|
|
|
hass: HomeAssistant, subdevice: dr.DeviceEntry, mock_entry: MockEntityFixture
|
|
|
|
):
|
|
|
|
"""Test remove_doorbell_text service."""
|
|
|
|
|
|
|
|
nvr = mock_entry.api.bootstrap.nvr
|
|
|
|
nvr.__fields__["remove_custom_doorbell_message"] = Mock()
|
|
|
|
nvr.remove_custom_doorbell_message = AsyncMock()
|
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
DOMAIN,
|
|
|
|
SERVICE_REMOVE_DOORBELL_TEXT,
|
|
|
|
{ATTR_DEVICE_ID: subdevice.id, ATTR_MESSAGE: "Test Message"},
|
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
nvr.remove_custom_doorbell_message.assert_called_once_with("Test Message")
|
|
|
|
|
|
|
|
|
|
|
|
async def test_set_default_doorbell_text(
|
|
|
|
hass: HomeAssistant, device: dr.DeviceEntry, mock_entry: MockEntityFixture
|
|
|
|
):
|
|
|
|
"""Test set_default_doorbell_text service."""
|
|
|
|
|
|
|
|
nvr = mock_entry.api.bootstrap.nvr
|
|
|
|
nvr.__fields__["set_default_doorbell_message"] = Mock()
|
|
|
|
nvr.set_default_doorbell_message = AsyncMock()
|
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
DOMAIN,
|
|
|
|
SERVICE_SET_DEFAULT_DOORBELL_TEXT,
|
|
|
|
{ATTR_DEVICE_ID: device.id, ATTR_MESSAGE: "Test Message"},
|
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
nvr.set_default_doorbell_message.assert_called_once_with("Test Message")
|
2022-05-20 20:16:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_set_chime_paired_doorbells(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
mock_entry: MockEntityFixture,
|
|
|
|
mock_chime: Chime,
|
|
|
|
mock_camera: Camera,
|
|
|
|
):
|
|
|
|
"""Test set_chime_paired_doorbells."""
|
|
|
|
|
|
|
|
mock_entry.api.update_device = AsyncMock()
|
|
|
|
|
|
|
|
mock_chime._api = mock_entry.api
|
|
|
|
mock_chime.name = "Test Chime"
|
|
|
|
mock_chime._initial_data = mock_chime.dict()
|
|
|
|
mock_entry.api.bootstrap.chimes = {
|
|
|
|
mock_chime.id: mock_chime,
|
|
|
|
}
|
|
|
|
|
|
|
|
camera1 = mock_camera.copy()
|
|
|
|
camera1.name = "Test Camera 1"
|
|
|
|
camera1._api = mock_entry.api
|
|
|
|
camera1.channels[0]._api = mock_entry.api
|
|
|
|
camera1.channels[1]._api = mock_entry.api
|
|
|
|
camera1.channels[2]._api = mock_entry.api
|
|
|
|
camera1.feature_flags.has_chime = True
|
2022-06-19 14:22:33 +00:00
|
|
|
regenerate_device_ids(camera1)
|
2022-05-20 20:16:01 +00:00
|
|
|
|
|
|
|
camera2 = mock_camera.copy()
|
|
|
|
camera2.name = "Test Camera 2"
|
|
|
|
camera2._api = mock_entry.api
|
|
|
|
camera2.channels[0]._api = mock_entry.api
|
|
|
|
camera2.channels[1]._api = mock_entry.api
|
|
|
|
camera2.channels[2]._api = mock_entry.api
|
|
|
|
camera2.feature_flags.has_chime = True
|
2022-06-19 14:22:33 +00:00
|
|
|
regenerate_device_ids(camera2)
|
2022-05-20 20:16:01 +00:00
|
|
|
|
|
|
|
mock_entry.api.bootstrap.cameras = {
|
|
|
|
camera1.id: camera1,
|
|
|
|
camera2.id: camera2,
|
|
|
|
}
|
|
|
|
|
|
|
|
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
registry = er.async_get(hass)
|
|
|
|
chime_entry = registry.async_get("button.test_chime_play_chime")
|
|
|
|
camera_entry = registry.async_get("binary_sensor.test_camera_2_doorbell")
|
|
|
|
assert chime_entry is not None
|
|
|
|
assert camera_entry is not None
|
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
DOMAIN,
|
|
|
|
SERVICE_SET_CHIME_PAIRED,
|
|
|
|
{
|
|
|
|
ATTR_DEVICE_ID: chime_entry.device_id,
|
|
|
|
"doorbells": {
|
|
|
|
ATTR_ENTITY_ID: ["binary_sensor.test_camera_1_doorbell"],
|
|
|
|
ATTR_DEVICE_ID: [camera_entry.device_id],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
mock_entry.api.update_device.assert_called_once_with(
|
2022-06-19 14:22:33 +00:00
|
|
|
ModelType.CHIME, mock_chime.id, {"cameraIds": sorted([camera1.id, camera2.id])}
|
2022-05-20 20:16:01 +00:00
|
|
|
)
|