Home Assistant Unofficial Reference 2024.12.1
camera.py
Go to the documentation of this file.
1 """Support for Verisure cameras."""
2 
3 from __future__ import annotations
4 
5 import errno
6 import os
7 
8 from verisure import Error as VerisureError
9 
10 from homeassistant.components.camera import Camera
11 from homeassistant.config_entries import ConfigEntry
12 from homeassistant.const import EVENT_HOMEASSISTANT_STOP
13 from homeassistant.core import HomeAssistant
14 from homeassistant.helpers.device_registry import DeviceInfo
16  AddEntitiesCallback,
17  async_get_current_platform,
18 )
19 from homeassistant.helpers.update_coordinator import CoordinatorEntity
20 
21 from .const import CONF_GIID, DOMAIN, LOGGER, SERVICE_CAPTURE_SMARTCAM
22 from .coordinator import VerisureDataUpdateCoordinator
23 
24 
26  hass: HomeAssistant,
27  entry: ConfigEntry,
28  async_add_entities: AddEntitiesCallback,
29 ) -> None:
30  """Set up Verisure sensors based on a config entry."""
31  coordinator: VerisureDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
32 
33  platform = async_get_current_platform()
34  platform.async_register_entity_service(
35  SERVICE_CAPTURE_SMARTCAM,
36  None,
37  VerisureSmartcam.capture_smartcam.__name__,
38  )
39 
41  VerisureSmartcam(coordinator, serial_number, hass.config.config_dir)
42  for serial_number in coordinator.data["cameras"]
43  )
44 
45 
46 class VerisureSmartcam(CoordinatorEntity[VerisureDataUpdateCoordinator], Camera):
47  """Representation of a Verisure camera."""
48 
49  _attr_has_entity_name = True
50  _attr_name = None
51 
52  def __init__(
53  self,
54  coordinator: VerisureDataUpdateCoordinator,
55  serial_number: str,
56  directory_path: str,
57  ) -> None:
58  """Initialize Verisure File Camera component."""
59  super().__init__(coordinator)
60  Camera.__init__(self)
61 
62  self._attr_unique_id_attr_unique_id = serial_number
63 
64  self.serial_numberserial_number = serial_number
65  self._directory_path_directory_path = directory_path
66  self._image_image: str | None = None
67  self._image_id_image_id: str | None = None
68 
69  @property
70  def device_info(self) -> DeviceInfo:
71  """Return device information about this entity."""
72  area = self.coordinator.data["cameras"][self.serial_numberserial_number]["device"]["area"]
73  return DeviceInfo(
74  name=area,
75  manufacturer="Verisure",
76  model="SmartCam",
77  identifiers={(DOMAIN, self.serial_numberserial_number)},
78  via_device=(DOMAIN, self.coordinator.entry.data[CONF_GIID]),
79  configuration_url="https://mypages.verisure.com",
80  )
81 
83  self, width: int | None = None, height: int | None = None
84  ) -> bytes | None:
85  """Return image response."""
86  self.check_imagelistcheck_imagelist()
87  if not self._image_image:
88  LOGGER.debug("No image to display")
89  return None
90  LOGGER.debug("Trying to open %s", self._image_image)
91  with open(self._image_image, "rb") as file:
92  return file.read()
93 
94  def check_imagelist(self) -> None:
95  """Check the contents of the image list."""
96  self.coordinator.update_smartcam_imageseries()
97 
98  new_image = None
99  for image in self.coordinator.imageseries:
100  if image["deviceLabel"] == self.serial_numberserial_number:
101  new_image = image
102  break
103 
104  if not new_image:
105  return
106 
107  new_image_id = new_image["mediaId"]
108  if new_image_id in ("-1", self._image_id_image_id):
109  LOGGER.debug("The image is the same, or loading image_id")
110  return
111 
112  LOGGER.debug("Download new image %s", new_image_id)
113  new_image_path = os.path.join(self._directory_path_directory_path, f"{new_image_id}.jpg")
114  new_image_url = new_image["contentUrl"]
115  self.coordinator.verisure.download_image(new_image_url, new_image_path)
116  LOGGER.debug("Old image_id=%s", self._image_id_image_id)
117  self.delete_imagedelete_image()
118 
119  self._image_id_image_id = new_image_id
120  self._image_image = new_image_path
121 
122  def delete_image(self, _=None) -> None:
123  """Delete an old image."""
124  remove_image = os.path.join(self._directory_path_directory_path, f"{self._image_id}.jpg")
125  try:
126  os.remove(remove_image)
127  LOGGER.debug("Deleting old image %s", remove_image)
128  except OSError as error:
129  if error.errno != errno.ENOENT:
130  raise
131 
132  def capture_smartcam(self) -> None:
133  """Capture a new picture from a smartcam."""
134  try:
135  self.coordinator.smartcam_capture(self.serial_numberserial_number)
136  LOGGER.debug("Capturing new image from %s", self.serial_numberserial_number)
137  except VerisureError as ex:
138  LOGGER.error("Could not capture image, %s", ex)
139 
140  async def async_added_to_hass(self) -> None:
141  """Entity added to Home Assistant."""
142  await super().async_added_to_hass()
143  self.hasshasshass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.delete_imagedelete_image)
None __init__(self, VerisureDataUpdateCoordinator coordinator, str serial_number, str directory_path)
Definition: camera.py:57
bytes|None camera_image(self, int|None width=None, int|None height=None)
Definition: camera.py:84
None open(self, **Any kwargs)
Definition: lock.py:86
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: camera.py:29