Home Assistant Unofficial Reference 2024.12.1
camera.py
Go to the documentation of this file.
1 """Support ezviz camera devices."""
2 
3 from __future__ import annotations
4 
5 import logging
6 
7 from pyezviz.exceptions import HTTPError, InvalidHost, PyEzvizError
8 
9 from homeassistant.components import ffmpeg
10 from homeassistant.components.camera import Camera, CameraEntityFeature
11 from homeassistant.components.ffmpeg import get_ffmpeg_manager
12 from homeassistant.components.stream import CONF_USE_WALLCLOCK_AS_TIMESTAMPS
13 from homeassistant.config_entries import (
14  SOURCE_IGNORE,
15  SOURCE_INTEGRATION_DISCOVERY,
16  ConfigEntry,
17 )
18 from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_USERNAME
19 from homeassistant.core import HomeAssistant
20 from homeassistant.helpers import discovery_flow
22  AddEntitiesCallback,
23  async_get_current_platform,
24 )
25 
26 from .const import (
27  ATTR_SERIAL,
28  CONF_FFMPEG_ARGUMENTS,
29  DATA_COORDINATOR,
30  DEFAULT_CAMERA_USERNAME,
31  DEFAULT_FFMPEG_ARGUMENTS,
32  DOMAIN,
33  SERVICE_WAKE_DEVICE,
34 )
35 from .coordinator import EzvizDataUpdateCoordinator
36 from .entity import EzvizEntity
37 
38 _LOGGER = logging.getLogger(__name__)
39 
40 
42  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
43 ) -> None:
44  """Set up EZVIZ cameras based on a config entry."""
45 
46  coordinator: EzvizDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][
47  DATA_COORDINATOR
48  ]
49 
50  camera_entities = []
51 
52  for camera, value in coordinator.data.items():
53  camera_rtsp_entry = [
54  item
55  for item in hass.config_entries.async_entries(DOMAIN)
56  if item.unique_id == camera and item.source != SOURCE_IGNORE
57  ]
58 
59  if camera_rtsp_entry:
60  ffmpeg_arguments = camera_rtsp_entry[0].options[CONF_FFMPEG_ARGUMENTS]
61  camera_username = camera_rtsp_entry[0].data[CONF_USERNAME]
62  camera_password = camera_rtsp_entry[0].data[CONF_PASSWORD]
63 
64  camera_rtsp_stream = f"rtsp://{camera_username}:{camera_password}@{value['local_ip']}:{value['local_rtsp_port']}{ffmpeg_arguments}"
65  _LOGGER.debug(
66  "Configuring Camera %s with ip: %s rtsp port: %s ffmpeg arguments: %s",
67  camera,
68  value["local_ip"],
69  value["local_rtsp_port"],
70  ffmpeg_arguments,
71  )
72 
73  else:
74  discovery_flow.async_create_flow(
75  hass,
76  DOMAIN,
77  context={"source": SOURCE_INTEGRATION_DISCOVERY},
78  data={
79  ATTR_SERIAL: camera,
80  CONF_IP_ADDRESS: value["local_ip"],
81  },
82  )
83 
84  _LOGGER.warning(
85  (
86  "Found camera with serial %s without configuration. Please go to"
87  " integration to complete setup"
88  ),
89  camera,
90  )
91 
92  ffmpeg_arguments = DEFAULT_FFMPEG_ARGUMENTS
93  camera_username = DEFAULT_CAMERA_USERNAME
94  camera_password = None
95  camera_rtsp_stream = ""
96 
97  camera_entities.append(
99  hass,
100  coordinator,
101  camera,
102  camera_username,
103  camera_password,
104  camera_rtsp_stream,
105  value["local_rtsp_port"],
106  ffmpeg_arguments,
107  )
108  )
109 
110  async_add_entities(camera_entities)
111 
112  platform = async_get_current_platform()
113 
114  platform.async_register_entity_service(
115  SERVICE_WAKE_DEVICE, None, "perform_wake_device"
116  )
117 
118 
120  """An implementation of a EZVIZ security camera."""
121 
122  _attr_name = None
123 
124  def __init__(
125  self,
126  hass: HomeAssistant,
127  coordinator: EzvizDataUpdateCoordinator,
128  serial: str,
129  camera_username: str,
130  camera_password: str | None,
131  camera_rtsp_stream: str | None,
132  local_rtsp_port: int,
133  ffmpeg_arguments: str | None,
134  ) -> None:
135  """Initialize a EZVIZ security camera."""
136  super().__init__(coordinator, serial)
137  Camera.__init__(self)
138  self.stream_options[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] = True
139  self._username_username = camera_username
140  self._password_password = camera_password
141  self._rtsp_stream_rtsp_stream = camera_rtsp_stream
142  self._local_rtsp_port_local_rtsp_port = local_rtsp_port
143  self._ffmpeg_arguments_ffmpeg_arguments = ffmpeg_arguments
144  self._ffmpeg_ffmpeg = get_ffmpeg_manager(hass)
145  self._attr_unique_id_attr_unique_id = serial
146  if camera_password:
147  self._attr_supported_features_attr_supported_features = CameraEntityFeature.STREAM
148 
149  @property
150  def available(self) -> bool:
151  """Return True if entity is available."""
152  return self.datadatadata["status"] != 2
153 
154  @property
155  def is_on(self) -> bool:
156  """Return true if on."""
157  return bool(self.datadatadata["status"])
158 
159  @property
160  def is_recording(self) -> bool:
161  """Return true if the device is recording."""
162  return self.datadatadata["alarm_notify"]
163 
164  @property
165  def motion_detection_enabled(self) -> bool:
166  """Camera Motion Detection Status."""
167  return self.datadatadata["alarm_notify"]
168 
169  def enable_motion_detection(self) -> None:
170  """Enable motion detection in camera."""
171  try:
172  self.coordinator.ezviz_client.set_camera_defence(self._serial_serial, 1)
173 
174  except InvalidHost as err:
175  raise InvalidHost("Error enabling motion detection") from err
176 
177  def disable_motion_detection(self) -> None:
178  """Disable motion detection."""
179  try:
180  self.coordinator.ezviz_client.set_camera_defence(self._serial_serial, 0)
181 
182  except InvalidHost as err:
183  raise InvalidHost("Error disabling motion detection") from err
184 
186  self, width: int | None = None, height: int | None = None
187  ) -> bytes | None:
188  """Return a frame from the camera stream."""
189  if self._rtsp_stream_rtsp_stream is None:
190  return None
191  return await ffmpeg.async_get_image(
192  self.hasshasshass, self._rtsp_stream_rtsp_stream, width=width, height=height
193  )
194 
195  async def stream_source(self) -> str | None:
196  """Return the stream source."""
197  if self._password_password is None:
198  return None
199  local_ip = self.datadatadata["local_ip"]
200  self._rtsp_stream_rtsp_stream = (
201  f"rtsp://{self._username}:{self._password}@"
202  f"{local_ip}:{self._local_rtsp_port}{self._ffmpeg_arguments}"
203  )
204  _LOGGER.debug(
205  "Configuring Camera %s with ip: %s rtsp port: %s ffmpeg arguments: %s",
206  self._serial_serial,
207  local_ip,
208  self._local_rtsp_port_local_rtsp_port,
209  self._ffmpeg_arguments_ffmpeg_arguments,
210  )
211 
212  return self._rtsp_stream_rtsp_stream
213 
214  def perform_wake_device(self) -> None:
215  """Basically wakes the camera by querying the device."""
216  try:
217  self.coordinator.ezviz_client.get_detection_sensibility(self._serial_serial)
218  except (HTTPError, PyEzvizError) as err:
219  raise PyEzvizError("Cannot wake device") from err
bytes|None async_camera_image(self, int|None width=None, int|None height=None)
Definition: camera.py:187
None __init__(self, HomeAssistant hass, EzvizDataUpdateCoordinator coordinator, str serial, str camera_username, str|None camera_password, str|None camera_rtsp_stream, int local_rtsp_port, str|None ffmpeg_arguments)
Definition: camera.py:134
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: camera.py:43
FFmpegManager get_ffmpeg_manager(HomeAssistant hass)
Definition: __init__.py:106