mobile_app: Camera Stream Webhook (#36839)
This commit is contained in:
parent
87f236c05c
commit
8541ae0360
4 changed files with 135 additions and 2 deletions
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
{
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue