mirror of https://github.com/home-assistant/core
214 lines
6.9 KiB
Python
214 lines
6.9 KiB
Python
"""Tests for the Sighthound integration."""
|
|
|
|
from copy import deepcopy
|
|
import datetime
|
|
import os
|
|
from pathlib import Path
|
|
from unittest import mock
|
|
|
|
from PIL import UnidentifiedImageError
|
|
import pytest
|
|
import simplehound.core as hound
|
|
|
|
from homeassistant.components.image_processing import DOMAIN as IP_DOMAIN, SERVICE_SCAN
|
|
import homeassistant.components.sighthound.image_processing as sh
|
|
from homeassistant.const import (
|
|
ATTR_ENTITY_ID,
|
|
CONF_API_KEY,
|
|
CONF_ENTITY_ID,
|
|
CONF_SOURCE,
|
|
)
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
TEST_DIR = os.path.dirname(__file__)
|
|
|
|
VALID_CONFIG = {
|
|
IP_DOMAIN: {
|
|
"platform": "sighthound",
|
|
CONF_API_KEY: "abc123",
|
|
CONF_SOURCE: {CONF_ENTITY_ID: "camera.demo_camera"},
|
|
},
|
|
"camera": {"platform": "demo"},
|
|
}
|
|
|
|
VALID_ENTITY_ID = "image_processing.sighthound_demo_camera"
|
|
|
|
MOCK_DETECTIONS = {
|
|
"image": {"width": 960, "height": 480, "orientation": 1},
|
|
"objects": [
|
|
{
|
|
"type": "person",
|
|
"boundingBox": {"x": 227, "y": 133, "height": 245, "width": 125},
|
|
},
|
|
{
|
|
"type": "person",
|
|
"boundingBox": {"x": 833, "y": 137, "height": 268, "width": 93},
|
|
},
|
|
],
|
|
"requestId": "545cec700eac4d389743e2266264e84b",
|
|
}
|
|
|
|
MOCK_NOW = datetime.datetime(2020, 2, 20, 10, 5, 3)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
async def setup_homeassistant(hass: HomeAssistant):
|
|
"""Set up the homeassistant integration."""
|
|
await async_setup_component(hass, "homeassistant", {})
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_detections():
|
|
"""Return a mock detection."""
|
|
with mock.patch(
|
|
"simplehound.core.cloud.detect", return_value=MOCK_DETECTIONS
|
|
) as detection:
|
|
yield detection
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_image():
|
|
"""Return a mock camera image."""
|
|
with mock.patch(
|
|
"homeassistant.components.demo.camera.DemoCamera.camera_image",
|
|
return_value=b"Test",
|
|
) as image:
|
|
yield image
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_bad_image_data():
|
|
"""Mock bad image data."""
|
|
with mock.patch(
|
|
"homeassistant.components.sighthound.image_processing.Image.open",
|
|
side_effect=UnidentifiedImageError,
|
|
) as bad_data:
|
|
yield bad_data
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_now():
|
|
"""Return a mock now datetime."""
|
|
with mock.patch("homeassistant.util.dt.now", return_value=MOCK_NOW) as now_dt:
|
|
yield now_dt
|
|
|
|
|
|
async def test_bad_api_key(
|
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Catch bad api key."""
|
|
with mock.patch(
|
|
"simplehound.core.cloud.detect", side_effect=hound.SimplehoundException
|
|
):
|
|
await async_setup_component(hass, IP_DOMAIN, VALID_CONFIG)
|
|
await hass.async_block_till_done()
|
|
assert "Sighthound error" in caplog.text
|
|
assert not hass.states.get(VALID_ENTITY_ID)
|
|
|
|
|
|
async def test_setup_platform(hass: HomeAssistant, mock_detections) -> None:
|
|
"""Set up platform with one entity."""
|
|
await async_setup_component(hass, IP_DOMAIN, VALID_CONFIG)
|
|
await hass.async_block_till_done()
|
|
assert hass.states.get(VALID_ENTITY_ID)
|
|
|
|
|
|
async def test_process_image(hass: HomeAssistant, mock_image, mock_detections) -> None:
|
|
"""Process an image."""
|
|
await async_setup_component(hass, IP_DOMAIN, VALID_CONFIG)
|
|
await hass.async_block_till_done()
|
|
assert hass.states.get(VALID_ENTITY_ID)
|
|
|
|
person_events = []
|
|
|
|
@callback
|
|
def capture_person_event(event):
|
|
"""Mock event."""
|
|
person_events.append(event)
|
|
|
|
hass.bus.async_listen(sh.EVENT_PERSON_DETECTED, capture_person_event)
|
|
|
|
data = {ATTR_ENTITY_ID: VALID_ENTITY_ID}
|
|
await hass.services.async_call(IP_DOMAIN, SERVICE_SCAN, service_data=data)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get(VALID_ENTITY_ID)
|
|
assert state.state == "2"
|
|
assert len(person_events) == 2
|
|
|
|
|
|
async def test_catch_bad_image(
|
|
hass: HomeAssistant,
|
|
caplog: pytest.LogCaptureFixture,
|
|
mock_image,
|
|
mock_detections,
|
|
mock_bad_image_data,
|
|
) -> None:
|
|
"""Process an image."""
|
|
valid_config_save_file = deepcopy(VALID_CONFIG)
|
|
valid_config_save_file[IP_DOMAIN].update({sh.CONF_SAVE_FILE_FOLDER: TEST_DIR})
|
|
await async_setup_component(hass, IP_DOMAIN, valid_config_save_file)
|
|
await hass.async_block_till_done()
|
|
assert hass.states.get(VALID_ENTITY_ID)
|
|
|
|
data = {ATTR_ENTITY_ID: VALID_ENTITY_ID}
|
|
await hass.services.async_call(IP_DOMAIN, SERVICE_SCAN, service_data=data)
|
|
await hass.async_block_till_done()
|
|
assert "Sighthound unable to process image" in caplog.text
|
|
|
|
|
|
async def test_save_image(hass: HomeAssistant, mock_image, mock_detections) -> None:
|
|
"""Save a processed image."""
|
|
valid_config_save_file = deepcopy(VALID_CONFIG)
|
|
valid_config_save_file[IP_DOMAIN].update({sh.CONF_SAVE_FILE_FOLDER: TEST_DIR})
|
|
await async_setup_component(hass, IP_DOMAIN, valid_config_save_file)
|
|
await hass.async_block_till_done()
|
|
assert hass.states.get(VALID_ENTITY_ID)
|
|
|
|
with mock.patch(
|
|
"homeassistant.components.sighthound.image_processing.Image.open"
|
|
) as pil_img_open:
|
|
pil_img = pil_img_open.return_value
|
|
pil_img = pil_img.convert.return_value
|
|
data = {ATTR_ENTITY_ID: VALID_ENTITY_ID}
|
|
await hass.services.async_call(IP_DOMAIN, SERVICE_SCAN, service_data=data)
|
|
await hass.async_block_till_done()
|
|
state = hass.states.get(VALID_ENTITY_ID)
|
|
assert state.state == "2"
|
|
assert pil_img.save.call_count == 1
|
|
|
|
directory = Path(TEST_DIR)
|
|
latest_save_path = directory / "sighthound_demo_camera_latest.jpg"
|
|
assert pil_img.save.call_args_list[0] == mock.call(latest_save_path)
|
|
|
|
|
|
async def test_save_timestamped_image(
|
|
hass: HomeAssistant, mock_image, mock_detections, mock_now
|
|
) -> None:
|
|
"""Save a processed image."""
|
|
valid_config_save_ts_file = deepcopy(VALID_CONFIG)
|
|
valid_config_save_ts_file[IP_DOMAIN].update({sh.CONF_SAVE_FILE_FOLDER: TEST_DIR})
|
|
valid_config_save_ts_file[IP_DOMAIN].update({sh.CONF_SAVE_TIMESTAMPTED_FILE: True})
|
|
await async_setup_component(hass, IP_DOMAIN, valid_config_save_ts_file)
|
|
await hass.async_block_till_done()
|
|
assert hass.states.get(VALID_ENTITY_ID)
|
|
|
|
with mock.patch(
|
|
"homeassistant.components.sighthound.image_processing.Image.open"
|
|
) as pil_img_open:
|
|
pil_img = pil_img_open.return_value
|
|
pil_img = pil_img.convert.return_value
|
|
data = {ATTR_ENTITY_ID: VALID_ENTITY_ID}
|
|
await hass.services.async_call(IP_DOMAIN, SERVICE_SCAN, service_data=data)
|
|
await hass.async_block_till_done()
|
|
state = hass.states.get(VALID_ENTITY_ID)
|
|
assert state.state == "2"
|
|
assert pil_img.save.call_count == 2
|
|
|
|
directory = Path(TEST_DIR)
|
|
timestamp_save_path = (
|
|
directory / "sighthound_demo_camera_2020-02-20_10:05:03.jpg"
|
|
)
|
|
assert pil_img.save.call_args_list[1] == mock.call(timestamp_save_path)
|