core/tests/components/hyperion/test_camera.py

212 lines
7.3 KiB
Python

"""Tests for the Hyperion integration."""
from __future__ import annotations
import asyncio
import base64
from collections.abc import Awaitable, Callable
from unittest.mock import AsyncMock, Mock, patch
from aiohttp import web
import pytest
from homeassistant.components.camera import (
DEFAULT_CONTENT_TYPE,
DOMAIN as CAMERA_DOMAIN,
async_get_image,
async_get_mjpeg_stream,
)
from homeassistant.components.hyperion import get_hyperion_device_id
from homeassistant.components.hyperion.const import (
DOMAIN,
HYPERION_MANUFACTURER_NAME,
HYPERION_MODEL_NAME,
TYPE_HYPERION_CAMERA,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr, entity_registry as er
from . import (
TEST_CONFIG_ENTRY_ID,
TEST_INSTANCE,
TEST_INSTANCE_1,
TEST_SYSINFO_ID,
async_call_registered_callback,
create_mock_client,
register_test_entity,
setup_test_config_entry,
)
TEST_CAMERA_ENTITY_ID = "camera.test_instance_1"
TEST_IMAGE_DATA = "TEST DATA"
TEST_IMAGE_UPDATE = {
"command": "ledcolors-imagestream-update",
"result": {
"image": "data:image/jpg;base64,"
+ base64.b64encode(TEST_IMAGE_DATA.encode()).decode("ascii"),
},
"success": True,
}
async def test_camera_setup(hass: HomeAssistant) -> None:
"""Test turning the light on."""
client = create_mock_client()
await setup_test_config_entry(hass, hyperion_client=client)
# Verify switch is on (as per TEST_COMPONENTS above).
entity_state = hass.states.get(TEST_CAMERA_ENTITY_ID)
assert entity_state
assert entity_state.state == "idle"
async def test_camera_image(hass: HomeAssistant) -> None:
"""Test retrieving a single camera image."""
client = create_mock_client()
client.async_send_image_stream_start = AsyncMock(return_value=True)
client.async_send_image_stream_stop = AsyncMock(return_value=True)
await setup_test_config_entry(hass, hyperion_client=client)
get_image_coro = async_get_image(hass, TEST_CAMERA_ENTITY_ID)
image_stream_update_coro = async_call_registered_callback(
client, "ledcolors-imagestream-update", TEST_IMAGE_UPDATE
)
result = await asyncio.gather(get_image_coro, image_stream_update_coro)
assert client.async_send_image_stream_start.called
assert client.async_send_image_stream_stop.called
assert result[0].content == TEST_IMAGE_DATA.encode()
async def test_camera_invalid_image(hass: HomeAssistant) -> None:
"""Test retrieving a single invalid camera image."""
client = create_mock_client()
client.async_send_image_stream_start = AsyncMock(return_value=True)
client.async_send_image_stream_stop = AsyncMock(return_value=True)
await setup_test_config_entry(hass, hyperion_client=client)
get_image_coro = async_get_image(hass, TEST_CAMERA_ENTITY_ID, timeout=0)
image_stream_update_coro = async_call_registered_callback(
client, "ledcolors-imagestream-update", None
)
with pytest.raises(HomeAssistantError):
await asyncio.gather(get_image_coro, image_stream_update_coro)
get_image_coro = async_get_image(hass, TEST_CAMERA_ENTITY_ID, timeout=0)
image_stream_update_coro = async_call_registered_callback(
client, "ledcolors-imagestream-update", {"garbage": 1}
)
with pytest.raises(HomeAssistantError):
await asyncio.gather(get_image_coro, image_stream_update_coro)
get_image_coro = async_get_image(hass, TEST_CAMERA_ENTITY_ID, timeout=0)
image_stream_update_coro = async_call_registered_callback(
client,
"ledcolors-imagestream-update",
{"result": {"image": "data:image/jpg;base64,FOO"}},
)
with pytest.raises(HomeAssistantError):
await asyncio.gather(get_image_coro, image_stream_update_coro)
async def test_camera_image_failed_start_stream_call(hass: HomeAssistant) -> None:
"""Test retrieving a single camera image with failed start stream call."""
client = create_mock_client()
client.async_send_image_stream_start = AsyncMock(return_value=False)
await setup_test_config_entry(hass, hyperion_client=client)
with pytest.raises(HomeAssistantError):
await async_get_image(hass, TEST_CAMERA_ENTITY_ID, timeout=0.01)
assert client.async_send_image_stream_start.called
assert not client.async_send_image_stream_stop.called
async def test_camera_stream(hass: HomeAssistant) -> None:
"""Test retrieving a camera stream."""
client = create_mock_client()
client.async_send_image_stream_start = AsyncMock(return_value=True)
client.async_send_image_stream_stop = AsyncMock(return_value=True)
request = Mock()
async def fake_get_still_stream(
in_request: web.Request,
callback: Callable[[], Awaitable[bytes | None]],
content_type: str,
interval: float,
) -> bytes | None:
assert request == in_request
assert content_type == DEFAULT_CONTENT_TYPE
assert interval == 0.0
return await callback()
await setup_test_config_entry(hass, hyperion_client=client)
with patch(
"homeassistant.components.hyperion.camera.async_get_still_stream",
) as fake:
fake.side_effect = fake_get_still_stream
get_stream_coro = async_get_mjpeg_stream(hass, request, TEST_CAMERA_ENTITY_ID)
image_stream_update_coro = async_call_registered_callback(
client, "ledcolors-imagestream-update", TEST_IMAGE_UPDATE
)
result = await asyncio.gather(get_stream_coro, image_stream_update_coro)
assert client.async_send_image_stream_start.called
assert client.async_send_image_stream_stop.called
assert result[0] == TEST_IMAGE_DATA.encode()
async def test_camera_stream_failed_start_stream_call(hass: HomeAssistant) -> None:
"""Test retrieving a camera stream with failed start stream call."""
client = create_mock_client()
client.async_send_image_stream_start = AsyncMock(return_value=False)
await setup_test_config_entry(hass, hyperion_client=client)
request = Mock()
assert not await async_get_mjpeg_stream(hass, request, TEST_CAMERA_ENTITY_ID)
assert client.async_send_image_stream_start.called
assert not client.async_send_image_stream_stop.called
async def test_device_info(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
) -> None:
"""Verify device information includes expected details."""
client = create_mock_client()
register_test_entity(
hass,
CAMERA_DOMAIN,
TYPE_HYPERION_CAMERA,
TEST_CAMERA_ENTITY_ID,
)
await setup_test_config_entry(hass, hyperion_client=client)
device_id = get_hyperion_device_id(TEST_SYSINFO_ID, TEST_INSTANCE)
device = device_registry.async_get_device(identifiers={(DOMAIN, device_id)})
assert device
assert device.config_entries == {TEST_CONFIG_ENTRY_ID}
assert device.identifiers == {(DOMAIN, device_id)}
assert device.manufacturer == HYPERION_MANUFACTURER_NAME
assert device.model == HYPERION_MODEL_NAME
assert device.name == TEST_INSTANCE_1["friendly_name"]
entities_from_device = [
entry.entity_id
for entry in er.async_entries_for_device(entity_registry, device.id)
]
assert TEST_CAMERA_ENTITY_ID in entities_from_device