Home Assistant Unofficial Reference 2024.12.1
camera.py
Go to the documentation of this file.
1 """Component providing support for Reolink IP cameras."""
2 
3 from __future__ import annotations
4 
5 from dataclasses import dataclass
6 import logging
7 
8 from reolink_aio.api import DUAL_LENS_MODELS
9 from reolink_aio.exceptions import ReolinkError
10 
12  Camera,
13  CameraEntityDescription,
14  CameraEntityFeature,
15 )
16 from homeassistant.core import HomeAssistant
17 from homeassistant.exceptions import HomeAssistantError
18 from homeassistant.helpers.entity_platform import AddEntitiesCallback
19 
20 from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription
21 from .util import ReolinkConfigEntry, ReolinkData
22 
23 _LOGGER = logging.getLogger(__name__)
24 PARALLEL_UPDATES = 0
25 
26 
27 @dataclass(frozen=True, kw_only=True)
29  CameraEntityDescription,
30  ReolinkChannelEntityDescription,
31 ):
32  """A class that describes camera entities for a camera channel."""
33 
34  stream: str
35 
36 
37 CAMERA_ENTITIES = (
39  key="sub",
40  stream="sub",
41  translation_key="sub",
42  ),
44  key="main",
45  stream="main",
46  translation_key="main",
47  entity_registry_enabled_default=False,
48  ),
50  key="snapshots_sub",
51  stream="snapshots_sub",
52  translation_key="snapshots_sub",
53  entity_registry_enabled_default=False,
54  ),
56  key="snapshots",
57  stream="snapshots_main",
58  translation_key="snapshots_main",
59  entity_registry_enabled_default=False,
60  ),
62  key="ext",
63  stream="ext",
64  translation_key="ext",
65  supported=lambda api, ch: api.protocol in ["rtmp", "flv"],
66  entity_registry_enabled_default=False,
67  ),
69  key="autotrack_sub",
70  stream="autotrack_sub",
71  translation_key="autotrack_sub",
72  supported=lambda api, ch: api.supported(ch, "autotrack_stream"),
73  ),
75  key="autotrack_snapshots_sub",
76  stream="autotrack_snapshots_sub",
77  translation_key="autotrack_snapshots_sub",
78  supported=lambda api, ch: api.supported(ch, "autotrack_stream"),
79  entity_registry_enabled_default=False,
80  ),
82  key="autotrack_snapshots_main",
83  stream="autotrack_snapshots_main",
84  translation_key="autotrack_snapshots_main",
85  supported=lambda api, ch: api.supported(ch, "autotrack_stream"),
86  entity_registry_enabled_default=False,
87  ),
88 )
89 
90 
92  hass: HomeAssistant,
93  config_entry: ReolinkConfigEntry,
94  async_add_entities: AddEntitiesCallback,
95 ) -> None:
96  """Set up a Reolink IP Camera."""
97  reolink_data: ReolinkData = config_entry.runtime_data
98 
99  entities: list[ReolinkCamera] = []
100  for entity_description in CAMERA_ENTITIES:
101  for channel in reolink_data.host.api.stream_channels:
102  if not entity_description.supported(reolink_data.host.api, channel):
103  continue
104  stream_url = await reolink_data.host.api.get_stream_source(
105  channel, entity_description.stream
106  )
107  if stream_url is None and "snapshots" not in entity_description.stream:
108  continue
109 
110  entities.append(ReolinkCamera(reolink_data, channel, entity_description))
111 
112  async_add_entities(entities)
113 
114 
116  """An implementation of a Reolink IP camera."""
117 
118  entity_description: ReolinkCameraEntityDescription
119 
120  def __init__(
121  self,
122  reolink_data: ReolinkData,
123  channel: int,
124  entity_description: ReolinkCameraEntityDescription,
125  ) -> None:
126  """Initialize Reolink camera stream."""
127  self.entity_descriptionentity_description = entity_description
128  ReolinkChannelCoordinatorEntity.__init__(self, reolink_data, channel)
129  Camera.__init__(self)
130 
131  if "snapshots" not in entity_description.stream:
132  self._attr_supported_features_attr_supported_features = CameraEntityFeature.STREAM
133 
134  if self._host_host.api.model in DUAL_LENS_MODELS:
135  self._attr_translation_key_attr_translation_key = (
136  f"{entity_description.translation_key}_lens_{self._channel}"
137  )
138 
139  async def stream_source(self) -> str | None:
140  """Return the source of the stream."""
141  return await self._host_host.api.get_stream_source(
142  self._channel_channel, self.entity_descriptionentity_description.stream
143  )
144 
146  self, width: int | None = None, height: int | None = None
147  ) -> bytes | None:
148  """Return a still image response from the camera."""
149  try:
150  return await self._host_host.api.get_snapshot(
151  self._channel_channel, self.entity_descriptionentity_description.stream
152  )
153  except ReolinkError as err:
154  raise HomeAssistantError(err) from err