mobile_app: Camera Stream Webhook (#36839)

This commit is contained in:
Zac West 2020-06-15 19:09:53 -07:00 committed by GitHub
parent 87f236c05c
commit 8541ae0360
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 135 additions and 2 deletions

View file

@ -72,3 +72,5 @@ ATTR_SENSOR_UOM = "unit_of_measurement"
SIGNAL_SENSOR_UPDATE = f"{DOMAIN}_sensor_update"
SIGNAL_LOCATION_UPDATE = DOMAIN + "_location_update_{}"
ATTR_CAMERA_ENTITY_ID = "camera_entity_id"

View file

@ -5,7 +5,7 @@
"documentation": "https://www.home-assistant.io/integrations/mobile_app",
"requirements": ["PyNaCl==1.3.0"],
"dependencies": ["http", "webhook", "person"],
"after_dependencies": ["cloud"],
"after_dependencies": ["cloud", "camera"],
"codeowners": ["@robbiet480"],
"quality_scale": "internal"
}

View file

@ -11,6 +11,7 @@ import voluptuous as vol
from homeassistant.components.binary_sensor import (
DEVICE_CLASSES as BINARY_SENSOR_CLASSES,
)
from homeassistant.components.camera import SUPPORT_STREAM as CAMERA_SUPPORT_STREAM
from homeassistant.components.device_tracker import (
ATTR_BATTERY,
ATTR_GPS,
@ -29,7 +30,7 @@ from homeassistant.const import (
HTTP_CREATED,
)
from homeassistant.core import EventOrigin
from homeassistant.exceptions import ServiceNotFound, TemplateError
from homeassistant.exceptions import HomeAssistantError, ServiceNotFound, TemplateError
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.template import attach
@ -40,6 +41,7 @@ from .const import (
ATTR_ALTITUDE,
ATTR_APP_DATA,
ATTR_APP_VERSION,
ATTR_CAMERA_ENTITY_ID,
ATTR_COURSE,
ATTR_DEVICE_ID,
ATTR_DEVICE_NAME,
@ -240,6 +242,32 @@ async def webhook_fire_event(hass, config_entry, data):
return empty_okay_response()
@WEBHOOK_COMMANDS.register("stream_camera")
@validate_schema({vol.Required(ATTR_CAMERA_ENTITY_ID): cv.string})
async def webhook_stream_camera(hass, config_entry, data):
"""Handle a request to HLS-stream a camera."""
camera = hass.states.get(data[ATTR_CAMERA_ENTITY_ID])
if camera is None:
return webhook_response(
{"success": False}, registration=config_entry.data, status=HTTP_BAD_REQUEST,
)
resp = {"mjpeg_path": "/api/camera_proxy_stream/%s" % (camera.entity_id)}
if camera.attributes["supported_features"] & CAMERA_SUPPORT_STREAM:
try:
resp["hls_path"] = await hass.components.camera.async_request_stream(
camera.entity_id, "hls"
)
except HomeAssistantError:
resp["hls_path"] = None
else:
resp["hls_path"] = None
return webhook_response(resp, registration=config_entry.data)
@WEBHOOK_COMMANDS.register("render_template")
@validate_schema(
{

View file

@ -3,14 +3,17 @@ import logging
import pytest
from homeassistant.components.camera import SUPPORT_STREAM as CAMERA_SUPPORT_STREAM
from homeassistant.components.mobile_app.const import CONF_SECRET
from homeassistant.components.zone import DOMAIN as ZONE_DOMAIN
from homeassistant.const import CONF_WEBHOOK_ID
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.setup import async_setup_component
from .const import CALL_SERVICE, FIRE_EVENT, REGISTER_CLEARTEXT, RENDER_TEMPLATE, UPDATE
from tests.async_mock import patch
from tests.common import async_mock_service
_LOGGER = logging.getLogger(__name__)
@ -303,3 +306,103 @@ async def test_webhook_enable_encryption(hass, webhook_client, create_registrati
decrypted_data = decrypt_payload(key, enc_json["encrypted_data"])
assert decrypted_data == {"one": "Hello world"}
async def test_webhook_camera_stream_non_existent(
hass, create_registrations, webhook_client
):
"""Test fetching camera stream URLs for a non-existent camera."""
webhook_id = create_registrations[1]["webhook_id"]
resp = await webhook_client.post(
f"/api/webhook/{webhook_id}",
json={
"type": "stream_camera",
"data": {"camera_entity_id": "camera.doesnt_exist"},
},
)
assert resp.status == 400
webhook_json = await resp.json()
assert webhook_json["success"] is False
async def test_webhook_camera_stream_non_hls(
hass, create_registrations, webhook_client
):
"""Test fetching camera stream URLs for a non-HLS/stream-supporting camera."""
hass.states.async_set("camera.non_stream_camera", "idle", {"supported_features": 0})
webhook_id = create_registrations[1]["webhook_id"]
resp = await webhook_client.post(
f"/api/webhook/{webhook_id}",
json={
"type": "stream_camera",
"data": {"camera_entity_id": "camera.non_stream_camera"},
},
)
assert resp.status == 200
webhook_json = await resp.json()
assert webhook_json["hls_path"] is None
assert (
webhook_json["mjpeg_path"]
== "/api/camera_proxy_stream/camera.non_stream_camera"
)
async def test_webhook_camera_stream_stream_available(
hass, create_registrations, webhook_client
):
"""Test fetching camera stream URLs for an HLS/stream-supporting camera."""
hass.states.async_set(
"camera.stream_camera", "idle", {"supported_features": CAMERA_SUPPORT_STREAM}
)
webhook_id = create_registrations[1]["webhook_id"]
with patch(
"homeassistant.components.camera.async_request_stream",
return_value="/api/streams/some_hls_stream",
):
resp = await webhook_client.post(
f"/api/webhook/{webhook_id}",
json={
"type": "stream_camera",
"data": {"camera_entity_id": "camera.stream_camera"},
},
)
assert resp.status == 200
webhook_json = await resp.json()
assert webhook_json["hls_path"] == "/api/streams/some_hls_stream"
assert webhook_json["mjpeg_path"] == "/api/camera_proxy_stream/camera.stream_camera"
async def test_webhook_camera_stream_stream_available_but_errors(
hass, create_registrations, webhook_client
):
"""Test fetching camera stream URLs for an HLS/stream-supporting camera but that streaming errors."""
hass.states.async_set(
"camera.stream_camera", "idle", {"supported_features": CAMERA_SUPPORT_STREAM}
)
webhook_id = create_registrations[1]["webhook_id"]
with patch(
"homeassistant.components.camera.async_request_stream",
side_effect=HomeAssistantError(),
):
resp = await webhook_client.post(
f"/api/webhook/{webhook_id}",
json={
"type": "stream_camera",
"data": {"camera_entity_id": "camera.stream_camera"},
},
)
assert resp.status == 200
webhook_json = await resp.json()
assert webhook_json["hls_path"] is None
assert webhook_json["mjpeg_path"] == "/api/camera_proxy_stream/camera.stream_camera"