1 """Support for Ubiquiti's UniFi Protect NVR."""
3 from __future__
import annotations
5 from collections.abc
import Generator
8 from uiprotect.data
import (
11 ProtectAdoptableDeviceModel,
30 from .data
import ProtectData, ProtectDeviceType, UFPConfigEntry
31 from .entity
import ProtectDeviceEntity
32 from .utils
import get_camera_base_name
34 _LOGGER = logging.getLogger(__name__)
39 hass: HomeAssistant, entry: UFPConfigEntry, data: ProtectData, camera: UFPCamera
42 if camera.can_write(data.api.bootstrap.auth_user):
45 translation_key = f
"rtsp_disabled_{edit_key}"
46 issue_key = f
"rtsp_disabled_{camera.id}"
48 ir.async_create_issue(
54 learn_more_url=
"https://www.home-assistant.io/integrations/unifiprotect/#camera-streams",
55 severity=IssueSeverity.WARNING,
56 translation_key=translation_key,
57 translation_placeholders={
"camera": camera.display_name},
58 data={
"entry_id": entry.entry_id,
"camera_id": camera.id},
65 entry: UFPConfigEntry,
67 ufp_device: UFPCamera |
None =
None,
68 ) -> Generator[tuple[UFPCamera, CameraChannel, bool]]:
69 """Get all the camera channels."""
71 cameras = data.get_cameras()
if ufp_device
is None else [ufp_device]
72 for camera
in cameras:
73 if not camera.channels:
74 if ufp_device
is None:
77 "Camera does not have any channels: %s (id: %s)",
81 data.async_add_pending_camera_id(camera.id)
85 for channel
in camera.channels:
86 if channel.is_package:
87 yield camera, channel,
True
88 elif channel.is_rtsp_enabled:
89 yield camera, channel, is_default
93 if is_default
and not camera.is_third_party_camera:
95 yield camera, camera.channels[0],
True
97 ir.async_delete_issue(hass, DOMAIN, f
"rtsp_disabled_{camera.id}")
102 entry: UFPConfigEntry,
104 ufp_device: UFPCamera |
None =
None,
105 ) -> list[ProtectDeviceEntity]:
106 disable_stream = data.disable_stream
107 entities: list[ProtectDeviceEntity] = []
109 hass, entry, data, ufp_device
120 disable_stream
or channel.is_package,
124 if channel.is_rtsp_enabled
and not channel.is_package:
140 entry: UFPConfigEntry,
141 async_add_entities: AddEntitiesCallback,
143 """Discover cameras on a UniFi Protect NVR."""
144 data = entry.runtime_data
147 def _add_new_device(device: ProtectAdoptableDeviceModel) ->
None:
148 if not isinstance(device, UFPCamera):
152 data.async_subscribe_adopt(_add_new_device)
153 entry.async_on_unload(
160 _ENABLE_FEATURE = CameraEntityFeature.STREAM
164 """A Ubiquiti UniFi Protect Camera."""
169 "_attr_is_recording",
170 "_attr_motion_detection_enabled",
177 channel: CameraChannel,
180 disable_stream: bool,
182 """Initialize an UniFi camera."""
188 device = self.
devicedevice
208 enable_stream =
not self.
_disable_stream_disable_stream
and channel.is_rtsp_enabled
211 rtsp_url = channel.rtsps_no_srtp_url
if self.
_secure_secure
else channel.rtsp_url
212 source = rtsp_url
if enable_stream
else None
219 updated_device = self.
devicedevice
220 channel = updated_device.channels[self.
channelchannel.id]
222 motion_enabled = updated_device.recording_settings.enable_motion_detection
224 motion_enabled
if motion_enabled
is not None else True
226 state_type_is_connected = updated_device.state
is StateType.CONNECTED
228 state_type_is_connected
and updated_device.is_recording
230 is_connected = self.
datadata.last_update_success
and state_type_is_connected
236 ATTR_WIDTH: channel.width,
237 ATTR_HEIGHT: channel.height,
238 ATTR_FPS: channel.fps,
239 ATTR_BITRATE: channel.bitrate,
240 ATTR_CHANNEL_ID: channel.id,
244 self, width: int |
None =
None, height: int |
None =
None
246 """Return the Camera Image."""
247 if self.
channelchannel.is_package:
248 last_image = await self.
devicedevice.get_package_snapshot(width, height)
250 last_image = await self.
devicedevice.get_snapshot(width, height)
255 """Return the Stream Source."""
259 """Call the job and enable motion detection."""
260 await self.
devicedevice.set_motion_detection(
True)
263 """Call the job and disable motion detection."""
264 await self.
devicedevice.set_motion_detection(
False)
None __init__(self, ProtectData data, UFPCamera camera, CameraChannel channel, bool is_default, bool secure, bool disable_stream)
None async_disable_motion_detection(self)
_attr_motion_detection_enabled
_attr_extra_state_attributes
None _async_set_stream_source(self)
None async_enable_motion_detection(self)
_attr_entity_registry_enabled_default
bytes|None async_camera_image(self, int|None width=None, int|None height=None)
None _async_update_device_from_protect(self, ProtectDeviceType device)
str|None stream_source(self)
None _create_rtsp_repair(HomeAssistant hass, UFPConfigEntry entry, ProtectData data, UFPCamera camera)
None async_setup_entry(HomeAssistant hass, UFPConfigEntry entry, AddEntitiesCallback async_add_entities)
Generator[tuple[UFPCamera, CameraChannel, bool]] _get_camera_channels(HomeAssistant hass, UFPConfigEntry entry, ProtectData data, UFPCamera|None ufp_device=None)
list[ProtectDeviceEntity] _async_camera_entities(HomeAssistant hass, UFPConfigEntry entry, ProtectData data, UFPCamera|None ufp_device=None)
str get_camera_base_name(CameraChannel channel)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)