mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Allow generic camera conf without still_image_url (#62611)
* Allow generic config with no CONF_STILL_IMAGE_URL * Use Stream.async_get_image when no CONF_STILL_IMAGE_URL * Remove GenericCamera.camera_image
This commit is contained in:
parent
3e3fb52dfa
commit
08a3140e6c
@ -1,7 +1,6 @@
|
||||
"""Support for IP Cameras."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import httpx
|
||||
@ -45,8 +44,8 @@ GET_IMAGE_TIMEOUT = 10
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_STILL_IMAGE_URL): cv.template,
|
||||
vol.Optional(CONF_STREAM_SOURCE): cv.template,
|
||||
vol.Required(vol.Any(CONF_STILL_IMAGE_URL, CONF_STREAM_SOURCE)): cv.template,
|
||||
vol.Optional(vol.Any(CONF_STILL_IMAGE_URL, CONF_STREAM_SOURCE)): cv.template,
|
||||
vol.Optional(CONF_AUTHENTICATION, default=HTTP_BASIC_AUTHENTICATION): vol.In(
|
||||
[HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION]
|
||||
),
|
||||
@ -81,9 +80,10 @@ class GenericCamera(Camera):
|
||||
self.hass = hass
|
||||
self._authentication = device_info.get(CONF_AUTHENTICATION)
|
||||
self._name = device_info.get(CONF_NAME)
|
||||
self._still_image_url = device_info[CONF_STILL_IMAGE_URL]
|
||||
self._still_image_url = device_info.get(CONF_STILL_IMAGE_URL)
|
||||
if self._still_image_url:
|
||||
self._still_image_url.hass = hass
|
||||
self._stream_source = device_info.get(CONF_STREAM_SOURCE)
|
||||
self._still_image_url.hass = hass
|
||||
if self._stream_source is not None:
|
||||
self._stream_source.hass = hass
|
||||
self._limit_refetch = device_info[CONF_LIMIT_REFETCH_TO_URL_CHANGE]
|
||||
@ -120,18 +120,16 @@ class GenericCamera(Camera):
|
||||
"""Return the interval between frames of the mjpeg stream."""
|
||||
return self._frame_interval
|
||||
|
||||
def camera_image(
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Return bytes of camera image."""
|
||||
return asyncio.run_coroutine_threadsafe(
|
||||
self.async_camera_image(), self.hass.loop
|
||||
).result()
|
||||
|
||||
async def async_camera_image(
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Return a still image response from the camera."""
|
||||
if not self._still_image_url:
|
||||
if not self.stream:
|
||||
await self.async_create_stream()
|
||||
if self.stream:
|
||||
return await self.stream.async_get_image(width, height)
|
||||
return None
|
||||
try:
|
||||
url = self._still_image_url.async_render(parse_result=False)
|
||||
except TemplateError as err:
|
||||
|
@ -437,6 +437,8 @@ class Stream:
|
||||
hass.add_executor_job underneath the hood.
|
||||
"""
|
||||
|
||||
self.add_provider(HLS_PROVIDER)
|
||||
self.start()
|
||||
return await self._keyframe_converter.async_get_image(
|
||||
width=width, height=height
|
||||
)
|
||||
|
@ -14,7 +14,7 @@ from homeassistant.components.websocket_api.const import TYPE_RESULT
|
||||
from homeassistant.const import SERVICE_RELOAD
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import get_fixture_path
|
||||
from tests.common import AsyncMock, Mock, get_fixture_path
|
||||
|
||||
|
||||
@respx.mock
|
||||
@ -459,3 +459,48 @@ async def test_timeout_cancelled(hass, hass_client):
|
||||
assert respx.calls.call_count == total_calls
|
||||
assert resp.status == HTTPStatus.OK
|
||||
assert await resp.text() == "hello world"
|
||||
|
||||
|
||||
async def test_no_still_image_url(hass, hass_client):
|
||||
"""Test that the component can grab images from stream with no still_image_url."""
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"camera",
|
||||
{
|
||||
"camera": {
|
||||
"name": "config_test",
|
||||
"platform": "generic",
|
||||
"stream_source": "rtsp://example.com:554/rtsp/",
|
||||
},
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
client = await hass_client()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.generic.camera.GenericCamera.stream_source",
|
||||
return_value=None,
|
||||
) as mock_stream_source:
|
||||
|
||||
# First test when there is no stream_source should fail
|
||||
resp = await client.get("/api/camera_proxy/camera.config_test")
|
||||
await hass.async_block_till_done()
|
||||
mock_stream_source.assert_called_once()
|
||||
assert resp.status == HTTPStatus.INTERNAL_SERVER_ERROR
|
||||
|
||||
with patch("homeassistant.components.camera.create_stream") as mock_create_stream:
|
||||
|
||||
# Now test when creating the stream succeeds
|
||||
mock_stream = Mock()
|
||||
mock_stream.async_get_image = AsyncMock()
|
||||
mock_stream.async_get_image.return_value = b"stream_keyframe_image"
|
||||
mock_create_stream.return_value = mock_stream
|
||||
|
||||
# should start the stream and get the image
|
||||
resp = await client.get("/api/camera_proxy/camera.config_test")
|
||||
await hass.async_block_till_done()
|
||||
mock_create_stream.assert_called_once()
|
||||
mock_stream.async_get_image.assert_called_once()
|
||||
assert resp.status == HTTPStatus.OK
|
||||
assert await resp.read() == b"stream_keyframe_image"
|
||||
|
Loading…
x
Reference in New Issue
Block a user