1 """Support for Ubiquiti's UVC cameras."""
3 from __future__
import annotations
5 from datetime
import datetime
8 from typing
import Any, cast
10 from uvcclient
import camera
as uvc_camera, nvr
11 from uvcclient.camera
import UVCCameraClient
12 from uvcclient.nvr
import UVCRemote
13 import voluptuous
as vol
16 PLATFORM_SCHEMA
as CAMERA_PLATFORM_SCHEMA,
28 _LOGGER = logging.getLogger(__name__)
33 DEFAULT_PASSWORD =
"ubnt"
37 PLATFORM_SCHEMA = CAMERA_PLATFORM_SCHEMA.extend(
39 vol.Required(CONF_NVR): cv.string,
40 vol.Required(CONF_KEY): cv.string,
41 vol.Optional(CONF_PASSWORD, default=DEFAULT_PASSWORD): cv.string,
42 vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
43 vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean,
51 add_entities: AddEntitiesCallback,
52 discovery_info: DiscoveryInfoType |
None =
None,
54 """Discover cameras on a Unifi NVR."""
55 addr = config[CONF_NVR]
56 key = config[CONF_KEY]
57 password = config[CONF_PASSWORD]
58 port = config[CONF_PORT]
59 ssl = config[CONF_SSL]
62 nvrconn = nvr.UVCRemote(addr, port, key, ssl=ssl)
64 cameras = nvrconn.index()
66 identifier = nvrconn.camera_identifier
72 if "airCam" not in nvrconn.get_camera(camera[identifier])[
"model"]
74 except nvr.NotAuthorized:
75 _LOGGER.error(
"Authorization failure while connecting to NVR")
77 except nvr.NvrError
as ex:
78 _LOGGER.error(
"NVR refuses to talk to me: %s",
str(ex))
79 raise PlatformNotReady
from ex
91 """A Ubiquiti Unifi Video Camera."""
93 _attr_should_poll =
True
94 _attr_brand =
"Ubiquiti"
95 _attr_is_streaming =
False
96 _caminfo: dict[str, Any]
98 def __init__(self, camera: UVCRemote, uuid: str, name: str, password: str) ->
None:
99 """Initialize an Unifi camera."""
106 self.
_camera_camera: UVCCameraClient |
None =
None
110 """Return supported features."""
111 channels = self.
_caminfo_caminfo[
"channels"]
112 for channel
in channels:
113 if channel[
"isRtspEnabled"]:
114 return CameraEntityFeature.STREAM
120 """Return the camera state attributes."""
124 self.
_caminfo_caminfo[
"lastRecordingStartTime"]
130 """Return true if the camera is recording."""
131 recording_state =
"DISABLED"
132 if "recordingIndicator" in self.
_caminfo_caminfo:
133 recording_state = self.
_caminfo_caminfo[
"recordingIndicator"]
135 return self.
_caminfo_caminfo[
"recordingSettings"][
136 "fullTimeRecordEnabled"
137 ]
or recording_state
in (
"MOTION_INPROGRESS",
"MOTION_FINISHED")
141 """Camera Motion Detection Status."""
142 return bool(self.
_caminfo_caminfo[
"recordingSettings"][
"motionRecordEnabled"])
146 """Return the model of this camera."""
147 return cast(str, self.
_caminfo_caminfo[
"model"])
150 """Login to the camera."""
155 addrs = [caminfo[
"host"], caminfo[
"internalHost"]]
157 client_cls: type[uvc_camera.UVCCameraClient]
158 if self.
_nvr_nvr.server_version >= (3, 2, 0):
159 client_cls = uvc_camera.UVCCameraClientV320
161 client_cls = uvc_camera.UVCCameraClient
163 if caminfo[
"username"]
is None:
164 caminfo[
"username"] =
"ubnt"
166 assert isinstance(caminfo[
"username"], str)
171 camera = client_cls(addr, caminfo[
"username"], self.
_password_password)
173 _LOGGER.debug(
"Logged into UVC camera %s via %s", self.
_attr_name_attr_name, addr)
178 except uvc_camera.CameraConnectError:
180 except uvc_camera.CameraAuthError:
183 _LOGGER.error(
"Unable to login to camera")
191 self, width: int |
None =
None, height: int |
None =
None
193 """Return the image of this camera."""
197 def _get_image(retry: bool =
True) -> bytes |
None:
198 assert self.
_camera_camera
is not None
200 return self.
_camera_camera.get_snapshot()
201 except uvc_camera.CameraConnectError:
202 _LOGGER.error(
"Unable to contact camera")
204 except uvc_camera.CameraAuthError:
207 return _get_image(retry=
False)
208 _LOGGER.error(
"Unable to log into camera, unable to get snapshot")
214 """Set motion detection on or off."""
215 set_mode =
"motion" if mode
is True else "none"
218 self.
_nvr_nvr.set_recordmode(self.
_uuid_uuid, set_mode)
219 except nvr.NvrError
as err:
220 _LOGGER.error(
"Unable to set recordmode to %s", set_mode)
224 """Enable motion detection in camera."""
228 """Disable motion detection in camera."""
232 """Return the source of the stream."""
233 for channel
in self.
_caminfo_caminfo[
"channels"]:
234 if channel[
"isRtspEnabled"]:
240 for i, uri
in enumerate(channel[
"rtspUris"])
241 if re.search(self.
_nvr_nvr._host, uri)
249 """Update the info."""
254 """Convert millisecond timestamp to datetime."""
bool motion_detection_enabled(self)
None enable_motion_detection(self)
CameraEntityFeature supported_features(self)
str|None stream_source(self)
None disable_motion_detection(self)
None set_motion_detection(self, bool mode)
None __init__(self, UVCRemote camera, str uuid, str name, str password)
bool motion_detection_enabled(self)
dict[str, Any] extra_state_attributes(self)
bytes|None camera_image(self, int|None width=None, int|None height=None)
None add_entities(HomeAssistant hass, FreeboxRouter router, AddEntitiesCallback async_add_entities, set[str] tracked)
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
datetime|None timestamp_ms_to_date(int epoch_ms)