108 lines
3.9 KiB
Python
108 lines
3.9 KiB
Python
"""Implement the Yandex Smart Home video_stream capabilities."""
|
|
|
|
# pyright: reportAttributeAccessIssue=information
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
from homeassistant.components import camera
|
|
from homeassistant.components.camera import CameraEntityFeature, StreamType
|
|
from homeassistant.components.stream import Stream
|
|
from homeassistant.core import Context
|
|
from homeassistant.helpers import network
|
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
|
|
|
from . import DOMAIN
|
|
from .capability import STATE_CAPABILITIES_REGISTRY, ActionOnlyCapabilityMixin, StateCapability
|
|
from .cloud_stream import CloudStreamManager
|
|
from .helpers import APIError
|
|
from .schema import (
|
|
CapabilityType,
|
|
GetStreamInstanceActionResultValue,
|
|
GetStreamInstanceActionState,
|
|
ResponseCode,
|
|
VideoStreamCapabilityInstance,
|
|
VideoStreamCapabilityParameters,
|
|
)
|
|
|
|
try:
|
|
from homeassistant.components.camera import get_camera_from_entity_id
|
|
except ImportError: # pragma: no cover
|
|
from homeassistant.components.camera import ( # type: ignore[no-redef]
|
|
_get_camera_from_entity_id as get_camera_from_entity_id,
|
|
)
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
from . import YandexSmartHome
|
|
|
|
|
|
class VideoStreamCapability(ActionOnlyCapabilityMixin, StateCapability[GetStreamInstanceActionState]):
|
|
"""Capability to stream from cameras."""
|
|
|
|
type = CapabilityType.VIDEO_STREAM
|
|
instance = VideoStreamCapabilityInstance.GET_STREAM
|
|
|
|
@property
|
|
def supported(self) -> bool:
|
|
"""Test if the capability is supported."""
|
|
return self.state.domain == camera.DOMAIN and bool(self._state_features & CameraEntityFeature.STREAM)
|
|
|
|
@property
|
|
def parameters(self) -> VideoStreamCapabilityParameters:
|
|
"""Return parameters for a devices request."""
|
|
return VideoStreamCapabilityParameters(protocols=["hls"])
|
|
|
|
async def set_instance_state(
|
|
self, context: Context, state: GetStreamInstanceActionState
|
|
) -> GetStreamInstanceActionResultValue:
|
|
"""Change capability instance state."""
|
|
component: YandexSmartHome = self._hass.data[DOMAIN]
|
|
entity_id = self.state.entity_id
|
|
stream = await self._async_request_stream(entity_id)
|
|
|
|
if self._entry_data.use_cloud_stream:
|
|
cloud_stream = component.cloud_streams.get(entity_id)
|
|
if not cloud_stream:
|
|
cloud_stream = CloudStreamManager(self._hass, stream, async_get_clientsession(self._hass))
|
|
component.cloud_streams[entity_id] = cloud_stream
|
|
|
|
await cloud_stream.async_start()
|
|
stream_url = cloud_stream.stream_url
|
|
if not stream_url:
|
|
raise APIError(ResponseCode.NOT_SUPPORTED_IN_CURRENT_MODE, "Failed to start stream")
|
|
else:
|
|
try:
|
|
external_url = network.get_url(self._hass, allow_internal=False)
|
|
except network.NoURLAvailableError:
|
|
raise APIError(
|
|
ResponseCode.NOT_SUPPORTED_IN_CURRENT_MODE,
|
|
"Missing 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 GetStreamInstanceActionResultValue(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 APIError(
|
|
ResponseCode.NOT_SUPPORTED_IN_CURRENT_MODE, f"{entity_id} does not support play stream service"
|
|
)
|
|
|
|
stream.add_provider(StreamType.HLS)
|
|
|
|
await stream.start()
|
|
|
|
stream.endpoint_url(StreamType.HLS)
|
|
|
|
return stream
|
|
|
|
|
|
STATE_CAPABILITIES_REGISTRY.register(VideoStreamCapability)
|