mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Add motionEye services (#53411)
This commit is contained in:
parent
855e0fc2eb
commit
bbbbcfbb93
@ -9,10 +9,20 @@ from jinja2 import Template
|
|||||||
from motioneye_client.client import MotionEyeClient, MotionEyeClientURLParseError
|
from motioneye_client.client import MotionEyeClient, MotionEyeClientURLParseError
|
||||||
from motioneye_client.const import (
|
from motioneye_client.const import (
|
||||||
DEFAULT_SURVEILLANCE_USERNAME,
|
DEFAULT_SURVEILLANCE_USERNAME,
|
||||||
|
KEY_ACTION_SNAPSHOT,
|
||||||
KEY_MOTION_DETECTION,
|
KEY_MOTION_DETECTION,
|
||||||
KEY_NAME,
|
KEY_NAME,
|
||||||
KEY_STREAMING_AUTH_MODE,
|
KEY_STREAMING_AUTH_MODE,
|
||||||
|
KEY_TEXT_OVERLAY_CAMERA_NAME,
|
||||||
|
KEY_TEXT_OVERLAY_CUSTOM_TEXT,
|
||||||
|
KEY_TEXT_OVERLAY_CUSTOM_TEXT_LEFT,
|
||||||
|
KEY_TEXT_OVERLAY_CUSTOM_TEXT_RIGHT,
|
||||||
|
KEY_TEXT_OVERLAY_DISABLED,
|
||||||
|
KEY_TEXT_OVERLAY_LEFT,
|
||||||
|
KEY_TEXT_OVERLAY_RIGHT,
|
||||||
|
KEY_TEXT_OVERLAY_TIMESTAMP,
|
||||||
)
|
)
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.mjpeg.camera import (
|
from homeassistant.components.mjpeg.camera import (
|
||||||
CONF_MJPEG_URL,
|
CONF_MJPEG_URL,
|
||||||
@ -30,6 +40,7 @@ from homeassistant.const import (
|
|||||||
HTTP_DIGEST_AUTHENTICATION,
|
HTTP_DIGEST_AUTHENTICATION,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
|
||||||
@ -40,6 +51,7 @@ from . import (
|
|||||||
listen_for_new_cameras,
|
listen_for_new_cameras,
|
||||||
)
|
)
|
||||||
from .const import (
|
from .const import (
|
||||||
|
CONF_ACTION,
|
||||||
CONF_CLIENT,
|
CONF_CLIENT,
|
||||||
CONF_COORDINATOR,
|
CONF_COORDINATOR,
|
||||||
CONF_STREAM_URL_TEMPLATE,
|
CONF_STREAM_URL_TEMPLATE,
|
||||||
@ -47,11 +59,40 @@ from .const import (
|
|||||||
CONF_SURVEILLANCE_USERNAME,
|
CONF_SURVEILLANCE_USERNAME,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
MOTIONEYE_MANUFACTURER,
|
MOTIONEYE_MANUFACTURER,
|
||||||
|
SERVICE_ACTION,
|
||||||
|
SERVICE_SET_TEXT_OVERLAY,
|
||||||
|
SERVICE_SNAPSHOT,
|
||||||
TYPE_MOTIONEYE_MJPEG_CAMERA,
|
TYPE_MOTIONEYE_MJPEG_CAMERA,
|
||||||
)
|
)
|
||||||
|
|
||||||
PLATFORMS = ["camera"]
|
PLATFORMS = ["camera"]
|
||||||
|
|
||||||
|
SCHEMA_TEXT_OVERLAY = vol.In(
|
||||||
|
[
|
||||||
|
KEY_TEXT_OVERLAY_DISABLED,
|
||||||
|
KEY_TEXT_OVERLAY_TIMESTAMP,
|
||||||
|
KEY_TEXT_OVERLAY_CUSTOM_TEXT,
|
||||||
|
KEY_TEXT_OVERLAY_CAMERA_NAME,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
SCHEMA_SERVICE_SET_TEXT = vol.Schema(
|
||||||
|
vol.All(
|
||||||
|
{
|
||||||
|
vol.Optional(KEY_TEXT_OVERLAY_LEFT): SCHEMA_TEXT_OVERLAY,
|
||||||
|
vol.Optional(KEY_TEXT_OVERLAY_CUSTOM_TEXT_LEFT): cv.string,
|
||||||
|
vol.Optional(KEY_TEXT_OVERLAY_RIGHT): SCHEMA_TEXT_OVERLAY,
|
||||||
|
vol.Optional(KEY_TEXT_OVERLAY_CUSTOM_TEXT_RIGHT): cv.string,
|
||||||
|
},
|
||||||
|
cv.has_at_least_one_key(
|
||||||
|
KEY_TEXT_OVERLAY_LEFT,
|
||||||
|
KEY_TEXT_OVERLAY_CUSTOM_TEXT_LEFT,
|
||||||
|
KEY_TEXT_OVERLAY_RIGHT,
|
||||||
|
KEY_TEXT_OVERLAY_CUSTOM_TEXT_RIGHT,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
extra=vol.ALLOW_EXTRA,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
@ -80,6 +121,23 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
listen_for_new_cameras(hass, entry, camera_add)
|
listen_for_new_cameras(hass, entry, camera_add)
|
||||||
|
|
||||||
|
platform = entity_platform.async_get_current_platform()
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
SERVICE_SET_TEXT_OVERLAY,
|
||||||
|
SCHEMA_SERVICE_SET_TEXT,
|
||||||
|
"async_set_text_overlay",
|
||||||
|
)
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
SERVICE_ACTION,
|
||||||
|
{vol.Required(CONF_ACTION): cv.string},
|
||||||
|
"async_request_action",
|
||||||
|
)
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
SERVICE_SNAPSHOT,
|
||||||
|
{},
|
||||||
|
"async_request_snapshot",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MotionEyeMjpegCamera(MotionEyeEntity, MjpegCamera):
|
class MotionEyeMjpegCamera(MotionEyeEntity, MjpegCamera):
|
||||||
"""motionEye mjpeg camera."""
|
"""motionEye mjpeg camera."""
|
||||||
@ -201,3 +259,38 @@ class MotionEyeMjpegCamera(MotionEyeEntity, MjpegCamera):
|
|||||||
def motion_detection_enabled(self) -> bool:
|
def motion_detection_enabled(self) -> bool:
|
||||||
"""Return the camera motion detection status."""
|
"""Return the camera motion detection status."""
|
||||||
return self._motion_detection_enabled
|
return self._motion_detection_enabled
|
||||||
|
|
||||||
|
async def async_set_text_overlay(
|
||||||
|
self,
|
||||||
|
left_text: str = None,
|
||||||
|
right_text: str = None,
|
||||||
|
custom_left_text: str = None,
|
||||||
|
custom_right_text: str = None,
|
||||||
|
) -> None:
|
||||||
|
"""Set text overlay for a camera."""
|
||||||
|
# Fetch the very latest camera config to reduce the risk of updating with a
|
||||||
|
# stale configuration.
|
||||||
|
camera = await self._client.async_get_camera(self._camera_id)
|
||||||
|
if not camera:
|
||||||
|
return
|
||||||
|
if left_text is not None:
|
||||||
|
camera[KEY_TEXT_OVERLAY_LEFT] = left_text
|
||||||
|
if right_text is not None:
|
||||||
|
camera[KEY_TEXT_OVERLAY_RIGHT] = right_text
|
||||||
|
if custom_left_text is not None:
|
||||||
|
camera[KEY_TEXT_OVERLAY_CUSTOM_TEXT_LEFT] = custom_left_text.encode(
|
||||||
|
"unicode_escape"
|
||||||
|
).decode("UTF-8")
|
||||||
|
if custom_right_text is not None:
|
||||||
|
camera[KEY_TEXT_OVERLAY_CUSTOM_TEXT_RIGHT] = custom_right_text.encode(
|
||||||
|
"unicode_escape"
|
||||||
|
).decode("UTF-8")
|
||||||
|
await self._client.async_set_camera(self._camera_id, camera)
|
||||||
|
|
||||||
|
async def async_request_action(self, action: str) -> None:
|
||||||
|
"""Call a motionEye action on a camera."""
|
||||||
|
await self._client.async_action(self._camera_id, action)
|
||||||
|
|
||||||
|
async def async_request_snapshot(self) -> None:
|
||||||
|
"""Request a motionEye snapshot be saved."""
|
||||||
|
await self.async_request_action(KEY_ACTION_SNAPSHOT)
|
||||||
|
@ -28,6 +28,7 @@ DOMAIN: Final = "motioneye"
|
|||||||
ATTR_EVENT_TYPE: Final = "event_type"
|
ATTR_EVENT_TYPE: Final = "event_type"
|
||||||
ATTR_WEBHOOK_ID: Final = "webhook_id"
|
ATTR_WEBHOOK_ID: Final = "webhook_id"
|
||||||
|
|
||||||
|
CONF_ACTION: Final = "action"
|
||||||
CONF_CLIENT: Final = "client"
|
CONF_CLIENT: Final = "client"
|
||||||
CONF_COORDINATOR: Final = "coordinator"
|
CONF_COORDINATOR: Final = "coordinator"
|
||||||
CONF_ADMIN_PASSWORD: Final = "admin_password"
|
CONF_ADMIN_PASSWORD: Final = "admin_password"
|
||||||
@ -81,6 +82,10 @@ EVENT_FILE_STORED_KEYS: Final = [
|
|||||||
|
|
||||||
MOTIONEYE_MANUFACTURER: Final = "motionEye"
|
MOTIONEYE_MANUFACTURER: Final = "motionEye"
|
||||||
|
|
||||||
|
SERVICE_SET_TEXT_OVERLAY: Final = "set_text_overlay"
|
||||||
|
SERVICE_ACTION: Final = "action"
|
||||||
|
SERVICE_SNAPSHOT: Final = "snapshot"
|
||||||
|
|
||||||
SIGNAL_CAMERA_ADD: Final = f"{DOMAIN}_camera_add_signal." "{}"
|
SIGNAL_CAMERA_ADD: Final = f"{DOMAIN}_camera_add_signal." "{}"
|
||||||
SIGNAL_CAMERA_REMOVE: Final = f"{DOMAIN}_camera_remove_signal." "{}"
|
SIGNAL_CAMERA_REMOVE: Final = f"{DOMAIN}_camera_remove_signal." "{}"
|
||||||
|
|
||||||
|
110
homeassistant/components/motioneye/services.yaml
Normal file
110
homeassistant/components/motioneye/services.yaml
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
set_text_overlay:
|
||||||
|
name: Set Text Overlay
|
||||||
|
description: Sets the text overlay for a camera.
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: motioneye
|
||||||
|
entity:
|
||||||
|
integration: motioneye
|
||||||
|
fields:
|
||||||
|
left_text:
|
||||||
|
name: Left Text Overlay
|
||||||
|
description: Text to display on the left
|
||||||
|
required: false
|
||||||
|
advanced: false
|
||||||
|
example: "timestamp"
|
||||||
|
default: ""
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
options:
|
||||||
|
- "disabled"
|
||||||
|
- "camera-name"
|
||||||
|
- "timestamp"
|
||||||
|
- "custom-text"
|
||||||
|
custom_left_text:
|
||||||
|
name: Left Custom Text
|
||||||
|
description: Custom text to display on the left
|
||||||
|
required: false
|
||||||
|
advanced: false
|
||||||
|
example: "Hello on the left!"
|
||||||
|
default: ""
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
multiline: true
|
||||||
|
right_text:
|
||||||
|
name: Right Text Overlay
|
||||||
|
description: Text to display on the right
|
||||||
|
required: false
|
||||||
|
advanced: false
|
||||||
|
example: "timestamp"
|
||||||
|
default: ""
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
options:
|
||||||
|
- "disabled"
|
||||||
|
- "camera-name"
|
||||||
|
- "timestamp"
|
||||||
|
- "custom-text"
|
||||||
|
custom_right_text:
|
||||||
|
name: Right Custom Text
|
||||||
|
description: Custom text to display on the right
|
||||||
|
required: false
|
||||||
|
advanced: false
|
||||||
|
example: "Hello on the right!"
|
||||||
|
default: ""
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
multiline: true
|
||||||
|
|
||||||
|
action:
|
||||||
|
name: Action
|
||||||
|
description: Trigger a motionEye action
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: motioneye
|
||||||
|
entity:
|
||||||
|
integration: motioneye
|
||||||
|
fields:
|
||||||
|
action:
|
||||||
|
name: Action
|
||||||
|
description: Action to trigger
|
||||||
|
required: true
|
||||||
|
advanced: false
|
||||||
|
example: "snapshot"
|
||||||
|
default: ""
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
options:
|
||||||
|
- "snapshot"
|
||||||
|
- "record_start"
|
||||||
|
- "record_stop"
|
||||||
|
- "lock"
|
||||||
|
- "unlock"
|
||||||
|
- "light_on"
|
||||||
|
- "light_off"
|
||||||
|
- "alarm_on"
|
||||||
|
- "alarm_off"
|
||||||
|
- "up"
|
||||||
|
- "right"
|
||||||
|
- "down"
|
||||||
|
- "left"
|
||||||
|
- "zoom_in"
|
||||||
|
- "zoom_out"
|
||||||
|
- "preset1"
|
||||||
|
- "preset2"
|
||||||
|
- "preset3"
|
||||||
|
- "preset4"
|
||||||
|
- "preset5"
|
||||||
|
- "preset6"
|
||||||
|
- "preset7"
|
||||||
|
- "preset8"
|
||||||
|
- "preset9"
|
||||||
|
|
||||||
|
snapshot:
|
||||||
|
name: Snapshot
|
||||||
|
description: Trigger a motionEye still snapshot
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: motioneye
|
||||||
|
entity:
|
||||||
|
integration: motioneye
|
@ -1,7 +1,7 @@
|
|||||||
"""Test the motionEye camera."""
|
"""Test the motionEye camera."""
|
||||||
import copy
|
import copy
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
from unittest.mock import AsyncMock, Mock
|
from unittest.mock import AsyncMock, Mock, call
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from aiohttp.web_exceptions import HTTPBadGateway
|
from aiohttp.web_exceptions import HTTPBadGateway
|
||||||
@ -14,20 +14,31 @@ from motioneye_client.const import (
|
|||||||
KEY_CAMERAS,
|
KEY_CAMERAS,
|
||||||
KEY_MOTION_DETECTION,
|
KEY_MOTION_DETECTION,
|
||||||
KEY_NAME,
|
KEY_NAME,
|
||||||
|
KEY_TEXT_OVERLAY_CUSTOM_TEXT,
|
||||||
|
KEY_TEXT_OVERLAY_CUSTOM_TEXT_LEFT,
|
||||||
|
KEY_TEXT_OVERLAY_CUSTOM_TEXT_RIGHT,
|
||||||
|
KEY_TEXT_OVERLAY_LEFT,
|
||||||
|
KEY_TEXT_OVERLAY_RIGHT,
|
||||||
|
KEY_TEXT_OVERLAY_TIMESTAMP,
|
||||||
KEY_VIDEO_STREAMING,
|
KEY_VIDEO_STREAMING,
|
||||||
)
|
)
|
||||||
import pytest
|
import pytest
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.camera import async_get_image, async_get_mjpeg_stream
|
from homeassistant.components.camera import async_get_image, async_get_mjpeg_stream
|
||||||
from homeassistant.components.motioneye import get_motioneye_device_identifier
|
from homeassistant.components.motioneye import get_motioneye_device_identifier
|
||||||
from homeassistant.components.motioneye.const import (
|
from homeassistant.components.motioneye.const import (
|
||||||
|
CONF_ACTION,
|
||||||
CONF_STREAM_URL_TEMPLATE,
|
CONF_STREAM_URL_TEMPLATE,
|
||||||
CONF_SURVEILLANCE_USERNAME,
|
CONF_SURVEILLANCE_USERNAME,
|
||||||
DEFAULT_SCAN_INTERVAL,
|
DEFAULT_SCAN_INTERVAL,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
MOTIONEYE_MANUFACTURER,
|
MOTIONEYE_MANUFACTURER,
|
||||||
|
SERVICE_ACTION,
|
||||||
|
SERVICE_SET_TEXT_OVERLAY,
|
||||||
|
SERVICE_SNAPSHOT,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CONF_URL
|
from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID, CONF_URL
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
@ -35,6 +46,7 @@ from homeassistant.helpers.device_registry import async_get_registry
|
|||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
|
TEST_CAMERA,
|
||||||
TEST_CAMERA_DEVICE_IDENTIFIER,
|
TEST_CAMERA_DEVICE_IDENTIFIER,
|
||||||
TEST_CAMERA_ENTITY_ID,
|
TEST_CAMERA_ENTITY_ID,
|
||||||
TEST_CAMERA_ID,
|
TEST_CAMERA_ID,
|
||||||
@ -379,3 +391,155 @@ async def test_get_stream_from_camera_with_broken_host(
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
with pytest.raises(HTTPBadGateway):
|
with pytest.raises(HTTPBadGateway):
|
||||||
await async_get_mjpeg_stream(hass, Mock(), TEST_CAMERA_ENTITY_ID)
|
await async_get_mjpeg_stream(hass, Mock(), TEST_CAMERA_ENTITY_ID)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_text_overlay_bad_extra_key(hass: HomeAssistant) -> None:
|
||||||
|
"""Test text overlay with incorrect input data."""
|
||||||
|
client = create_mock_motioneye_client()
|
||||||
|
await setup_mock_motioneye_config_entry(hass, client=client)
|
||||||
|
|
||||||
|
data = {ATTR_ENTITY_ID: TEST_CAMERA_ENTITY_ID, "extra_key": "foo"}
|
||||||
|
with pytest.raises(vol.error.MultipleInvalid):
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_SET_TEXT_OVERLAY, data)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_text_overlay_bad_entity_identifier(hass: HomeAssistant) -> None:
|
||||||
|
"""Test text overlay with bad entity identifier."""
|
||||||
|
client = create_mock_motioneye_client()
|
||||||
|
await setup_mock_motioneye_config_entry(hass, client=client)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
ATTR_ENTITY_ID: "some random string",
|
||||||
|
KEY_TEXT_OVERLAY_LEFT: KEY_TEXT_OVERLAY_TIMESTAMP,
|
||||||
|
}
|
||||||
|
|
||||||
|
client.reset_mock()
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_SET_TEXT_OVERLAY, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert not client.async_set_camera.called
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_text_overlay_bad_empty(hass: HomeAssistant) -> None:
|
||||||
|
"""Test text overlay with incorrect input data."""
|
||||||
|
client = create_mock_motioneye_client()
|
||||||
|
await setup_mock_motioneye_config_entry(hass, client=client)
|
||||||
|
with pytest.raises(vol.error.MultipleInvalid):
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_SET_TEXT_OVERLAY, {})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_text_overlay_bad_no_left_or_right(hass: HomeAssistant) -> None:
|
||||||
|
"""Test text overlay with incorrect input data."""
|
||||||
|
client = create_mock_motioneye_client()
|
||||||
|
await setup_mock_motioneye_config_entry(hass, client=client)
|
||||||
|
|
||||||
|
data = {ATTR_ENTITY_ID: TEST_CAMERA_ENTITY_ID}
|
||||||
|
with pytest.raises(vol.error.MultipleInvalid):
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_SET_TEXT_OVERLAY, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_text_overlay_good(hass: HomeAssistant) -> None:
|
||||||
|
"""Test a working text overlay."""
|
||||||
|
client = create_mock_motioneye_client()
|
||||||
|
await setup_mock_motioneye_config_entry(hass, client=client)
|
||||||
|
|
||||||
|
custom_left_text = "one\ntwo\nthree"
|
||||||
|
custom_right_text = "four\nfive\nsix"
|
||||||
|
data = {
|
||||||
|
ATTR_ENTITY_ID: TEST_CAMERA_ENTITY_ID,
|
||||||
|
KEY_TEXT_OVERLAY_LEFT: KEY_TEXT_OVERLAY_CUSTOM_TEXT,
|
||||||
|
KEY_TEXT_OVERLAY_RIGHT: KEY_TEXT_OVERLAY_CUSTOM_TEXT,
|
||||||
|
KEY_TEXT_OVERLAY_CUSTOM_TEXT_LEFT: custom_left_text,
|
||||||
|
KEY_TEXT_OVERLAY_CUSTOM_TEXT_RIGHT: custom_right_text,
|
||||||
|
}
|
||||||
|
client.async_get_camera = AsyncMock(return_value=copy.deepcopy(TEST_CAMERA))
|
||||||
|
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_SET_TEXT_OVERLAY, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert client.async_get_camera.called
|
||||||
|
|
||||||
|
expected_camera = copy.deepcopy(TEST_CAMERA)
|
||||||
|
expected_camera[KEY_TEXT_OVERLAY_LEFT] = KEY_TEXT_OVERLAY_CUSTOM_TEXT
|
||||||
|
expected_camera[KEY_TEXT_OVERLAY_RIGHT] = KEY_TEXT_OVERLAY_CUSTOM_TEXT
|
||||||
|
expected_camera[KEY_TEXT_OVERLAY_CUSTOM_TEXT_LEFT] = "one\\ntwo\\nthree"
|
||||||
|
expected_camera[KEY_TEXT_OVERLAY_CUSTOM_TEXT_RIGHT] = "four\\nfive\\nsix"
|
||||||
|
assert client.async_set_camera.call_args == call(TEST_CAMERA_ID, expected_camera)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_text_overlay_good_entity_id(hass: HomeAssistant) -> None:
|
||||||
|
"""Test a working text overlay with entity_id."""
|
||||||
|
client = create_mock_motioneye_client()
|
||||||
|
await setup_mock_motioneye_config_entry(hass, client=client)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
ATTR_ENTITY_ID: TEST_CAMERA_ENTITY_ID,
|
||||||
|
KEY_TEXT_OVERLAY_LEFT: KEY_TEXT_OVERLAY_TIMESTAMP,
|
||||||
|
}
|
||||||
|
client.async_get_camera = AsyncMock(return_value=copy.deepcopy(TEST_CAMERA))
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_SET_TEXT_OVERLAY, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert client.async_get_camera.called
|
||||||
|
|
||||||
|
expected_camera = copy.deepcopy(TEST_CAMERA)
|
||||||
|
expected_camera[KEY_TEXT_OVERLAY_LEFT] = KEY_TEXT_OVERLAY_TIMESTAMP
|
||||||
|
assert client.async_set_camera.call_args == call(TEST_CAMERA_ID, expected_camera)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_text_overlay_bad_device(hass: HomeAssistant) -> None:
|
||||||
|
"""Test a working text overlay."""
|
||||||
|
client = create_mock_motioneye_client()
|
||||||
|
await setup_mock_motioneye_config_entry(hass, client=client)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
ATTR_DEVICE_ID: "not a device",
|
||||||
|
KEY_TEXT_OVERLAY_LEFT: KEY_TEXT_OVERLAY_TIMESTAMP,
|
||||||
|
}
|
||||||
|
client.reset_mock()
|
||||||
|
client.async_get_camera = AsyncMock(return_value=copy.deepcopy(TEST_CAMERA))
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_SET_TEXT_OVERLAY, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert not client.async_get_camera.called
|
||||||
|
assert not client.async_set_camera.called
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_text_overlay_no_such_camera(hass: HomeAssistant) -> None:
|
||||||
|
"""Test a working text overlay."""
|
||||||
|
client = create_mock_motioneye_client()
|
||||||
|
await setup_mock_motioneye_config_entry(hass, client=client)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
ATTR_ENTITY_ID: TEST_CAMERA_ENTITY_ID,
|
||||||
|
KEY_TEXT_OVERLAY_LEFT: KEY_TEXT_OVERLAY_TIMESTAMP,
|
||||||
|
}
|
||||||
|
client.reset_mock()
|
||||||
|
client.async_get_camera = AsyncMock(return_value={})
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_SET_TEXT_OVERLAY, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert not client.async_set_camera.called
|
||||||
|
|
||||||
|
|
||||||
|
async def test_request_action(hass: HomeAssistant) -> None:
|
||||||
|
"""Test requesting an action."""
|
||||||
|
client = create_mock_motioneye_client()
|
||||||
|
await setup_mock_motioneye_config_entry(hass, client=client)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
ATTR_ENTITY_ID: TEST_CAMERA_ENTITY_ID,
|
||||||
|
CONF_ACTION: "foo",
|
||||||
|
}
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_ACTION, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert client.async_action.call_args == call(TEST_CAMERA_ID, data[CONF_ACTION])
|
||||||
|
|
||||||
|
|
||||||
|
async def test_request_snapshot(hass: HomeAssistant) -> None:
|
||||||
|
"""Test requesting a snapshot."""
|
||||||
|
client = create_mock_motioneye_client()
|
||||||
|
await setup_mock_motioneye_config_entry(hass, client=client)
|
||||||
|
|
||||||
|
data = {ATTR_ENTITY_ID: TEST_CAMERA_ENTITY_ID}
|
||||||
|
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_SNAPSHOT, data)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert client.async_action.call_args == call(TEST_CAMERA_ID, "snapshot")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user