97 lines
3.4 KiB
Python
97 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from homeassistant.components import camera
|
|
from homeassistant.components.camera import StreamType, _get_camera_from_entity_id
|
|
from homeassistant.components.stream import Stream
|
|
from homeassistant.const import ATTR_SUPPORTED_FEATURES
|
|
from homeassistant.core import HomeAssistant, State
|
|
from homeassistant.helpers import network
|
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
|
|
|
from .capability import PREFIX_CAPABILITIES, AbstractCapability, register_capability
|
|
from .cloud_stream import CloudStream
|
|
from .const import CLOUD_STREAMS, DOMAIN, ERR_NOT_SUPPORTED_IN_CURRENT_MODE, VIDEO_STREAM_INSTANCE_GET_STREAM
|
|
from .error import SmartHomeError
|
|
from .helpers import Config, RequestData
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
CAPABILITIES_VIDEO_STREAM = PREFIX_CAPABILITIES + 'video_stream'
|
|
|
|
|
|
@register_capability
|
|
class VideoStreamCapability(AbstractCapability):
|
|
|
|
type = CAPABILITIES_VIDEO_STREAM
|
|
instance = VIDEO_STREAM_INSTANCE_GET_STREAM
|
|
retrievable = False
|
|
|
|
def __init__(self, hass: HomeAssistant, config: Config, state: State):
|
|
super().__init__(hass, config, state)
|
|
|
|
self._config = config
|
|
self.reportable = False
|
|
|
|
def parameters(self) -> dict[str, Any]:
|
|
return {
|
|
'protocol': StreamType.HLS
|
|
}
|
|
|
|
def supported(self) -> bool:
|
|
if self.state.domain != camera.DOMAIN:
|
|
return False
|
|
|
|
features = self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
|
return features & camera.CameraEntityFeature.STREAM
|
|
|
|
def get_value(self) -> float | str | bool | None:
|
|
return None
|
|
|
|
async def set_state(self, data: RequestData, state: dict[str, Any]) -> dict[str, Any] | None:
|
|
entity_id = self.state.entity_id
|
|
stream = await self._async_request_stream(entity_id)
|
|
|
|
if self._config.use_cloud_stream:
|
|
cloud_stream = self.hass.data[DOMAIN][CLOUD_STREAMS].get(entity_id)
|
|
if not cloud_stream:
|
|
cloud_stream = CloudStream(self.hass, stream, async_get_clientsession(self.hass))
|
|
self.hass.data[DOMAIN][CLOUD_STREAMS][entity_id] = cloud_stream
|
|
|
|
await cloud_stream.start()
|
|
stream_url = cloud_stream.stream_url
|
|
else:
|
|
try:
|
|
external_url = network.get_url(self.hass, allow_internal=False)
|
|
except network.NoURLAvailableError:
|
|
raise SmartHomeError(
|
|
ERR_NOT_SUPPORTED_IN_CURRENT_MODE,
|
|
'Unable to get Home Assistant external URL. Have you set external URLs in Configuration -> General?'
|
|
)
|
|
|
|
endpoint_url = stream.endpoint_url(StreamType.HLS)
|
|
stream_url = f'{external_url}{endpoint_url}'
|
|
|
|
return {
|
|
'stream_url': stream_url,
|
|
'protocol': 'hls'
|
|
}
|
|
|
|
async def _async_request_stream(self, entity_id: str) -> Stream:
|
|
camera_entity = _get_camera_from_entity_id(self.hass, self.state.entity_id)
|
|
stream = await camera_entity.async_create_stream()
|
|
|
|
if not stream:
|
|
raise SmartHomeError(
|
|
ERR_NOT_SUPPORTED_IN_CURRENT_MODE,
|
|
f'{entity_id} does not support play stream service'
|
|
)
|
|
|
|
stream.add_provider(StreamType.HLS)
|
|
stream.start()
|
|
stream.endpoint_url(StreamType.HLS)
|
|
|
|
return stream
|