1 """Support for the Netatmo cameras."""
3 from __future__
import annotations
6 from typing
import Any, cast
9 from pyatmo
import ApiError
as NetatmoApiError, modules
as NaModules
10 from pyatmo.event
import Event
as NaEvent
11 import voluptuous
as vol
22 ATTR_CAMERA_LIGHT_MODE,
30 EVENT_TYPE_LIGHT_MODE,
34 NETATMO_CREATE_CAMERA,
35 SERVICE_SET_CAMERA_LIGHT,
36 SERVICE_SET_PERSON_AWAY,
37 SERVICE_SET_PERSONS_HOME,
39 WEBHOOK_NACAMERA_CONNECTION,
42 from .data_handler
import EVENT, HOME, SIGNAL_NAME, NetatmoDevice
43 from .entity
import NetatmoModuleEntity
45 _LOGGER = logging.getLogger(__name__)
47 DEFAULT_QUALITY =
"high"
51 hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
53 """Set up the Netatmo camera platform."""
56 def _create_entity(netatmo_device: NetatmoDevice) ->
None:
60 entry.async_on_unload(
64 platform = entity_platform.async_get_current_platform()
66 platform.async_register_entity_service(
67 SERVICE_SET_PERSONS_HOME,
68 {vol.Required(ATTR_PERSONS): vol.All(cv.ensure_list, [cv.string])},
69 "_service_set_persons_home",
71 platform.async_register_entity_service(
72 SERVICE_SET_PERSON_AWAY,
73 {vol.Optional(ATTR_PERSON): cv.string},
74 "_service_set_person_away",
76 platform.async_register_entity_service(
77 SERVICE_SET_CAMERA_LIGHT,
78 {vol.Required(ATTR_CAMERA_LIGHT_MODE): vol.In(CAMERA_LIGHT_MODES)},
79 "_service_set_camera_light",
84 """Representation of a Netatmo camera."""
86 _attr_brand = MANUFACTURER
87 _attr_supported_features = CameraEntityFeature.STREAM
88 _attr_configuration_url = CONF_URL_SECURITY
89 device: NaModules.Camera
90 _quality = DEFAULT_QUALITY
91 _monitoring: bool |
None =
None
96 netatmo_device: NetatmoDevice,
98 """Set up for access to the Netatmo camera images."""
102 self.
_attr_unique_id_attr_unique_id = f
"{netatmo_device.device.entity_id}-{self.device_type}"
105 self._publishers.extend(
109 "home_id": self.
homehome.entity_id,
110 SIGNAL_NAME: f
"{HOME}-{self.home.entity_id}",
114 "home_id": self.
homehome.entity_id,
115 SIGNAL_NAME: f
"{EVENT}-{self.home.entity_id}",
121 """Entity created."""
124 for event_type
in (EVENT_TYPE_LIGHT_MODE, EVENT_TYPE_OFF, EVENT_TYPE_ON):
128 f
"signal-{DOMAIN}-webhook-{event_type}",
133 self.
hasshass.data[DOMAIN][DATA_CAMERAS][self.
devicedevice.entity_id] = self.
devicedevice.name
137 """Handle webhook events."""
140 if not data.get(
"camera_id"):
144 data[
"home_id"] == self.
homehome.entity_id
145 and data[
"camera_id"] == self.
devicedevice.entity_id
147 if data[WEBHOOK_PUSH_TYPE]
in (
"NACamera-off",
"NACamera-disconnection"):
150 elif data[WEBHOOK_PUSH_TYPE]
in (
152 WEBHOOK_NACAMERA_CONNECTION,
156 elif data[WEBHOOK_PUSH_TYPE] == WEBHOOK_LIGHT_MODE:
166 self, width: int |
None =
None, height: int |
None =
None
168 """Return a still image response from the camera."""
170 return cast(bytes, await self.
devicedevice.async_get_live_snapshot())
172 aiohttp.ClientPayloadError,
173 aiohttp.ContentTypeError,
174 aiohttp.ServerDisconnectedError,
175 aiohttp.ClientConnectorError,
178 _LOGGER.debug(
"Could not fetch live camera image (%s)", err)
183 """Return supported features."""
184 supported_features = CameraEntityFeature.ON_OFF
186 supported_features |= CameraEntityFeature.STREAM
187 return supported_features
190 """Turn off camera."""
191 await self.
devicedevice.async_monitoring_off()
194 """Turn on camera."""
195 await self.
devicedevice.async_monitoring_on()
198 """Return the stream source."""
199 if self.
devicedevice.is_local:
200 await self.
devicedevice.async_update_camera_urls()
202 if self.
devicedevice.local_url:
203 return f
"{self.device.local_url}/live/files/{self._quality}/index.m3u8"
204 return f
"{self.device.vpn_url}/live/files/{self._quality}/index.m3u8"
208 """Update the entity's state."""
212 if self.
devicedevice.monitoring
is not None:
216 self.
hasshass.data[DOMAIN][DATA_EVENTS][self.
devicedevice.entity_id] = (
222 "id": self.
devicedevice.entity_id,
224 "sd_status": self.
devicedevice.sd_status,
225 "alim_status": self.
devicedevice.alim_status,
226 "is_local": self.
devicedevice.is_local,
227 "vpn_url": self.
devicedevice.vpn_url,
228 "local_url": self.
devicedevice.local_url,
234 """Add meta data to events."""
236 for event
in event_list:
237 if not (video_id := event.video_id):
239 event_data = event.__dict__
240 event_data[
"subevents"] = [
242 for event
in event_data.get(
"subevents", [])
243 if not isinstance(event, dict)
245 event_data[
"media_url"] = self.
get_video_urlget_video_url(video_id)
246 events[event.event_time] = event_data
251 if self.
devicedevice.is_local:
252 return f
"{self.device.local_url}/vod/{video_id}/files/{self._quality}/index.m3u8"
253 return f
"{self.device.vpn_url}/vod/{video_id}/files/{self._quality}/index.m3u8"
256 """Fetch matching person ids for given list of persons."""
258 person_id_errors = []
260 for person
in persons:
262 for pid, data
in self.
homehome.persons.items():
263 if data.pseudo == person:
264 person_ids.append(pid)
268 if person_id
is None:
269 person_id_errors.append(person)
277 """Service to change current home schedule."""
278 persons = kwargs.get(ATTR_PERSONS, [])
281 await self.
homehome.async_set_persons_home(person_ids=person_ids)
282 _LOGGER.debug(
"Set %s as at home", persons)
285 """Service to mark a person as away or set the home as empty."""
286 person = kwargs.get(ATTR_PERSON)
287 person_ids = self.
fetch_person_idsfetch_person_ids([person]
if person
else [])
288 person_id = next(iter(person_ids),
None)
290 await self.
homehome.async_set_persons_away(
295 _LOGGER.debug(
"Set %s as away %s", person, person_id)
297 _LOGGER.debug(
"Set home as empty")
300 """Service to set light mode."""
301 if not isinstance(self.
devicedevice, NaModules.netatmo.NOC):
303 f
"{self.device_type} <{self.device.name}> does not have a floodlight"
306 mode =
str(kwargs.get(ATTR_CAMERA_LIGHT_MODE))
307 _LOGGER.debug(
"Turn %s camera light for '%s'", mode, self.
_attr_name_attr_name)
308 await self.
devicedevice.async_set_floodlight_state(mode)
None async_write_ha_state(self)
dict process_events(self, list[NaEvent] event_list)
None _service_set_persons_home(self, **Any kwargs)
None _service_set_person_away(self, **Any kwargs)
CameraEntityFeature supported_features(self)
list[str] fetch_person_ids(self, list[str|None] persons)
None __init__(self, NetatmoDevice netatmo_device)
None async_turn_off(self)
str get_video_url(self, str video_id)
bytes|None async_camera_image(self, int|None width=None, int|None height=None)
None _service_set_camera_light(self, **Any kwargs)
_attr_motion_detection_enabled
None handle_event(self, dict event)
None async_added_to_hass(self)
None async_update_callback(self)
_attr_extra_state_attributes
DeviceType device_type(self)
DeviceType device_type(self)
None async_write_ha_state(self)
None async_on_remove(self, CALLBACK_TYPE func)
IssData update(pyiss.ISS iss)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)