"""Support for Agent camera streaming.""" from datetime import timedelta import logging from agent import AgentError from homeassistant.components.camera import CameraEntityFeature from homeassistant.components.mjpeg import MjpegCamera, filter_urllib3_logging from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import ( AddEntitiesCallback, async_get_current_platform, ) from .const import ( ATTRIBUTION, CAMERA_SCAN_INTERVAL_SECS, CONNECTION, DOMAIN as AGENT_DOMAIN, ) SCAN_INTERVAL = timedelta(seconds=CAMERA_SCAN_INTERVAL_SECS) _LOGGER = logging.getLogger(__name__) _DEV_EN_ALT = "enable_alerts" _DEV_DS_ALT = "disable_alerts" _DEV_EN_REC = "start_recording" _DEV_DS_REC = "stop_recording" _DEV_SNAP = "snapshot" CAMERA_SERVICES = { _DEV_EN_ALT: "async_enable_alerts", _DEV_DS_ALT: "async_disable_alerts", _DEV_EN_REC: "async_start_recording", _DEV_DS_REC: "async_stop_recording", _DEV_SNAP: "async_snapshot", } async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up the Agent cameras.""" filter_urllib3_logging() cameras = [] server = hass.data[AGENT_DOMAIN][config_entry.entry_id][CONNECTION] if not server.devices: _LOGGER.warning("Could not fetch cameras from Agent server") return for device in server.devices: if device.typeID == 2: camera = AgentCamera(device) cameras.append(camera) async_add_entities(cameras) platform = async_get_current_platform() for service, method in CAMERA_SERVICES.items(): platform.async_register_entity_service(service, {}, method) class AgentCamera(MjpegCamera): """Representation of an Agent Device Stream.""" _attr_attribution = ATTRIBUTION _attr_should_poll = True # Cameras default to False _attr_supported_features = CameraEntityFeature.ON_OFF def __init__(self, device): """Initialize as a subclass of MjpegCamera.""" self.device = device self._removed = False self._attr_name = f"{device.client.name} {device.name}" self._attr_unique_id = f"{device._client.unique}_{device.typeID}_{device.id}" super().__init__( name=device.name, mjpeg_url=f"{device.client._server_url}{device.mjpeg_image_url}&size={device.mjpegStreamWidth}x{device.mjpegStreamHeight}", still_image_url=f"{device.client._server_url}{device.still_image_url}&size={device.mjpegStreamWidth}x{device.mjpegStreamHeight}", ) self._attr_device_info = DeviceInfo( identifiers={(AGENT_DOMAIN, self.unique_id)}, manufacturer="Agent", model="Camera", name=self.name, sw_version=device.client.version, ) async def async_update(self) -> None: """Update our state from the Agent API.""" try: await self.device.update() if self._removed: _LOGGER.debug("%s reacquired", self.name) self._removed = False except AgentError: # server still available - camera error if self.device.client.is_available and not self._removed: _LOGGER.error("%s lost", self.name) self._removed = True self._attr_icon = "mdi:camcorder-off" if self.is_on: self._attr_icon = "mdi:camcorder" self._attr_available = self.device.client.is_available self._attr_extra_state_attributes = { "editable": False, "enabled": self.is_on, "connected": self.connected, "detected": self.is_detected, "alerted": self.is_alerted, "has_ptz": self.device.has_ptz, "alerts_enabled": self.device.alerts_active, } @property def is_recording(self) -> bool: """Return whether the monitor is recording.""" return self.device.recording @property def is_alerted(self) -> bool: """Return whether the monitor has alerted.""" return self.device.alerted @property def is_detected(self) -> bool: """Return whether the monitor has alerted.""" return self.device.detected @property def connected(self) -> bool: """Return True if entity is connected.""" return self.device.connected @property def is_on(self) -> bool: """Return true if on.""" return self.device.online @property def motion_detection_enabled(self) -> bool: """Return the camera motion detection status.""" return self.device.detector_active async def async_enable_alerts(self): """Enable alerts.""" await self.device.alerts_on() async def async_disable_alerts(self): """Disable alerts.""" await self.device.alerts_off() async def async_enable_motion_detection(self) -> None: """Enable motion detection.""" await self.device.detector_on() async def async_disable_motion_detection(self) -> None: """Disable motion detection.""" await self.device.detector_off() async def async_start_recording(self): """Start recording.""" await self.device.record() async def async_stop_recording(self): """Stop recording.""" await self.device.record_stop() async def async_turn_on(self) -> None: """Enable the camera.""" await self.device.enable() async def async_snapshot(self): """Take a snapshot.""" await self.device.snapshot() async def async_turn_off(self) -> None: """Disable the camera.""" await self.device.disable()