mirror of
https://github.com/home-assistant/core.git
synced 2025-07-26 22:57:17 +00:00
Fix unifiprotect supported features being set too late (#129850)
This commit is contained in:
parent
0b56ef5699
commit
3584c710b9
@ -156,7 +156,8 @@ async def async_setup_entry(
|
|||||||
async_add_entities(_async_camera_entities(hass, entry, data))
|
async_add_entities(_async_camera_entities(hass, entry, data))
|
||||||
|
|
||||||
|
|
||||||
_EMPTY_CAMERA_FEATURES = CameraEntityFeature(0)
|
_DISABLE_FEATURE = CameraEntityFeature(0)
|
||||||
|
_ENABLE_FEATURE = CameraEntityFeature.STREAM
|
||||||
|
|
||||||
|
|
||||||
class ProtectCamera(ProtectDeviceEntity, Camera):
|
class ProtectCamera(ProtectDeviceEntity, Camera):
|
||||||
@ -195,24 +196,20 @@ class ProtectCamera(ProtectDeviceEntity, Camera):
|
|||||||
self._attr_name = f"{camera_name} (insecure)"
|
self._attr_name = f"{camera_name} (insecure)"
|
||||||
# only the default (first) channel is enabled by default
|
# only the default (first) channel is enabled by default
|
||||||
self._attr_entity_registry_enabled_default = is_default and secure
|
self._attr_entity_registry_enabled_default = is_default and secure
|
||||||
|
# Set the stream source before finishing the init
|
||||||
|
# because async_added_to_hass is too late and camera
|
||||||
|
# integration uses async_internal_added_to_hass to access
|
||||||
|
# the stream source which is called before async_added_to_hass
|
||||||
|
self._async_set_stream_source()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_set_stream_source(self) -> None:
|
def _async_set_stream_source(self) -> None:
|
||||||
disable_stream = self._disable_stream
|
|
||||||
channel = self.channel
|
channel = self.channel
|
||||||
|
enable_stream = not self._disable_stream and channel.is_rtsp_enabled
|
||||||
if not channel.is_rtsp_enabled:
|
|
||||||
disable_stream = False
|
|
||||||
|
|
||||||
rtsp_url = channel.rtsps_url if self._secure else channel.rtsp_url
|
rtsp_url = channel.rtsps_url if self._secure else channel.rtsp_url
|
||||||
|
source = rtsp_url if enable_stream else None
|
||||||
# _async_set_stream_source called by __init__
|
self._attr_supported_features = _ENABLE_FEATURE if source else _DISABLE_FEATURE
|
||||||
# pylint: disable-next=attribute-defined-outside-init
|
self._stream_source = source
|
||||||
self._stream_source = None if disable_stream else rtsp_url
|
|
||||||
if self._stream_source:
|
|
||||||
self._attr_supported_features = CameraEntityFeature.STREAM
|
|
||||||
else:
|
|
||||||
self._attr_supported_features = _EMPTY_CAMERA_FEATURES
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_device_from_protect(self, device: ProtectDeviceType) -> None:
|
def _async_update_device_from_protect(self, device: ProtectDeviceType) -> None:
|
||||||
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from unittest.mock import AsyncMock, Mock
|
from unittest.mock import AsyncMock, Mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
from uiprotect.api import DEVICE_UPDATE_INTERVAL
|
from uiprotect.api import DEVICE_UPDATE_INTERVAL
|
||||||
from uiprotect.data import Camera as ProtectCamera, CameraChannel, StateType
|
from uiprotect.data import Camera as ProtectCamera, CameraChannel, StateType
|
||||||
from uiprotect.exceptions import NvrError
|
from uiprotect.exceptions import NvrError
|
||||||
@ -12,8 +13,13 @@ from uiprotect.websocket import WebsocketState
|
|||||||
from homeassistant.components.camera import (
|
from homeassistant.components.camera import (
|
||||||
CameraEntityFeature,
|
CameraEntityFeature,
|
||||||
CameraState,
|
CameraState,
|
||||||
|
CameraWebRTCProvider,
|
||||||
|
RTCIceCandidate,
|
||||||
|
StreamType,
|
||||||
|
WebRTCSendMessage,
|
||||||
async_get_image,
|
async_get_image,
|
||||||
async_get_stream_source,
|
async_get_stream_source,
|
||||||
|
async_register_webrtc_provider,
|
||||||
)
|
)
|
||||||
from homeassistant.components.unifiprotect.const import (
|
from homeassistant.components.unifiprotect.const import (
|
||||||
ATTR_BITRATE,
|
ATTR_BITRATE,
|
||||||
@ -22,6 +28,7 @@ from homeassistant.components.unifiprotect.const import (
|
|||||||
ATTR_HEIGHT,
|
ATTR_HEIGHT,
|
||||||
ATTR_WIDTH,
|
ATTR_WIDTH,
|
||||||
DEFAULT_ATTRIBUTION,
|
DEFAULT_ATTRIBUTION,
|
||||||
|
DOMAIN,
|
||||||
)
|
)
|
||||||
from homeassistant.components.unifiprotect.utils import get_camera_base_name
|
from homeassistant.components.unifiprotect.utils import get_camera_base_name
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@ -31,11 +38,12 @@ from homeassistant.const import (
|
|||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
Platform,
|
Platform,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from .utils import (
|
from .utils import (
|
||||||
|
Camera,
|
||||||
MockUFPFixture,
|
MockUFPFixture,
|
||||||
adopt_devices,
|
adopt_devices,
|
||||||
assert_entity_counts,
|
assert_entity_counts,
|
||||||
@ -46,6 +54,45 @@ from .utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MockWebRTCProvider(CameraWebRTCProvider):
|
||||||
|
"""WebRTC provider."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def domain(self) -> str:
|
||||||
|
"""Return the integration domain of the provider."""
|
||||||
|
return DOMAIN
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_is_supported(self, stream_source: str) -> bool:
|
||||||
|
"""Return if this provider is supports the Camera as source."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def async_handle_async_webrtc_offer(
|
||||||
|
self,
|
||||||
|
camera: Camera,
|
||||||
|
offer_sdp: str,
|
||||||
|
session_id: str,
|
||||||
|
send_message: WebRTCSendMessage,
|
||||||
|
) -> None:
|
||||||
|
"""Handle the WebRTC offer and return the answer via the provided callback."""
|
||||||
|
|
||||||
|
async def async_on_webrtc_candidate(
|
||||||
|
self, session_id: str, candidate: RTCIceCandidate
|
||||||
|
) -> None:
|
||||||
|
"""Handle the WebRTC candidate."""
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_close_session(self, session_id: str) -> None:
|
||||||
|
"""Close the session."""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def web_rtc_provider(hass: HomeAssistant) -> None:
|
||||||
|
"""Fixture to enable WebRTC provider for camera entities."""
|
||||||
|
await async_setup_component(hass, "camera", {})
|
||||||
|
async_register_webrtc_provider(hass, MockWebRTCProvider())
|
||||||
|
|
||||||
|
|
||||||
def validate_default_camera_entity(
|
def validate_default_camera_entity(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
camera_obj: ProtectCamera,
|
camera_obj: ProtectCamera,
|
||||||
@ -283,6 +330,26 @@ async def test_basic_setup(
|
|||||||
await validate_no_stream_camera_state(hass, doorbell, 3, entity_id, features=0)
|
await validate_no_stream_camera_state(hass, doorbell, 3, entity_id, features=0)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("web_rtc_provider")
|
||||||
|
async def test_webrtc_support(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
ufp: MockUFPFixture,
|
||||||
|
camera_all: ProtectCamera,
|
||||||
|
) -> None:
|
||||||
|
"""Test webrtc support is available."""
|
||||||
|
camera_high_only = camera_all.copy()
|
||||||
|
camera_high_only.channels = [c.copy() for c in camera_all.channels]
|
||||||
|
camera_high_only.name = "Test Camera 1"
|
||||||
|
camera_high_only.channels[0].is_rtsp_enabled = True
|
||||||
|
camera_high_only.channels[1].is_rtsp_enabled = False
|
||||||
|
camera_high_only.channels[2].is_rtsp_enabled = False
|
||||||
|
await init_entry(hass, ufp, [camera_high_only])
|
||||||
|
entity_id = validate_default_camera_entity(hass, camera_high_only, 0)
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert StreamType.WEB_RTC in state.attributes["frontend_stream_type"]
|
||||||
|
|
||||||
|
|
||||||
async def test_adopt(
|
async def test_adopt(
|
||||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
||||||
) -> None:
|
) -> None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user