From 584ea43d8eaeefeddf5f8dde8923a7c948fe2a60 Mon Sep 17 00:00:00 2001 From: Jeena Date: Sat, 29 Sep 2018 12:29:41 +0200 Subject: [PATCH] onvif: Add absolute and relative movements for PTZ Right now only continous movement is possible with the onvif component, which makes it not usable for many usecases. This patch adds the possibisity to set absolute and relative movements for PTZ but stays backwards compatible so we don't break the API. --- homeassistant/components/camera/onvif.py | 46 ++++++++++++++++-------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/camera/onvif.py b/homeassistant/components/camera/onvif.py index 9cf21dca9..9a4ff3676 100644 --- a/homeassistant/components/camera/onvif.py +++ b/homeassistant/components/camera/onvif.py @@ -39,6 +39,7 @@ CONF_PROFILE = "profile" ATTR_PAN = "pan" ATTR_TILT = "tilt" ATTR_ZOOM = "zoom" +ATTR_TYPE = "type" DIR_UP = "UP" DIR_DOWN = "DOWN" @@ -46,6 +47,9 @@ DIR_LEFT = "LEFT" DIR_RIGHT = "RIGHT" ZOOM_OUT = "ZOOM_OUT" ZOOM_IN = "ZOOM_IN" +TYPE_CONTINOUS = "CONTINOUS" +TYPE_ABSOLUTE = "ABSOLUTE" +TYPE_RELATIVE = "RELATIVE" SERVICE_PTZ = "onvif_ptz" @@ -65,9 +69,11 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ SERVICE_PTZ_SCHEMA = vol.Schema({ ATTR_ENTITY_ID: cv.entity_ids, - ATTR_PAN: vol.In([DIR_LEFT, DIR_RIGHT]), - ATTR_TILT: vol.In([DIR_UP, DIR_DOWN]), - ATTR_ZOOM: vol.In([ZOOM_OUT, ZOOM_IN]) + ATTR_PAN: vol.Or(vol.In([DIR_LEFT, DIR_RIGHT]), vol.Range(min=-1, max=1)), + ATTR_TILT: vol.Or(vol.In([DIR_UP, DIR_DOWN]), vol.Range(min=-1, max=1)), + ATTR_ZOOM: vol.Or(vol.In([ZOOM_OUT, ZOOM_IN]), vol.Range(min=-1, max=1)), + vol.Required(ATTR_TYPE, default=TYPE_CONTINOUS): + vol.In([TYPE_CONTINOUS, TYPE_ABSOLUTE, TYPE_RELATIVE]) }) @@ -81,6 +87,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): pan = service.data.get(ATTR_PAN, None) tilt = service.data.get(ATTR_TILT, None) zoom = service.data.get(ATTR_ZOOM, None) + type_ = service.data.get(ATTR_TYPE, None) all_cameras = hass.data[ONVIF_DATA][ENTITIES] entity_ids = extract_entity_ids(hass, service) target_cameras = [] @@ -90,7 +97,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): target_cameras = [camera for camera in all_cameras if camera.entity_id in entity_ids] for camera in target_cameras: - camera.perform_ptz(pan, tilt, zoom) + camera.perform_ptz(type_, pan, tilt, zoom) hass.services.async_register(DOMAIN, SERVICE_PTZ, handle_ptz, schema=SERVICE_PTZ_SCHEMA) @@ -162,23 +169,34 @@ class ONVIFHassCamera(Camera): self._name, err) return - def perform_ptz(self, pan, tilt, zoom): + def perform_ptz(self, type_, pan, tilt, zoom): """Perform a PTZ action on the camera.""" from onvif import exceptions + if self._ptz_service: - pan_val = 1 if pan == DIR_RIGHT else -1 if pan == DIR_LEFT else 0 - tilt_val = 1 if tilt == DIR_UP else -1 if tilt == DIR_DOWN else 0 - zoom_val = 1 if zoom == ZOOM_IN else -1 if zoom == ZOOM_OUT else 0 - req = {"Velocity": { - "PanTilt": {"_x": pan_val, "_y": tilt_val}, - "Zoom": {"_x": zoom_val}}} + type_val = "continous" if type_ == "" else type_ + pan_val = 1 if pan == DIR_RIGHT else -1 if pan == DIR_LEFT else (pan or 0) + tilt_val = 1 if tilt == DIR_UP else -1 if tilt == DIR_DOWN else (tilt or 0) + zoom_val = 1 if zoom == ZOOM_IN else -1 if zoom == ZOOM_OUT else (zoom or 0) + + xy = {"_x": pan_val, "_y": tilt_val} + z = {"_x": zoom_val} + try: - self._ptz_service.ContinuousMove(req) + if type_val == TYPE_ABSOLUTE: + req = {"Position": {"PanTilt": xy, "Zoom": z}} + self._ptz_service.AbsoluteMove(req) + elif type_val == TYPE_RELATIVE: + req = {"Position": {"Translation": xy, "Zoom": z}} + self._ptz_service.RelativeMove(req) + else: + req = {"Velocity": {"PanTilt": xy, "Zoom": z}} + self._ptz_service.ContinuousMove(req) except exceptions.ONVIFError as err: if "Bad Request" in err.reason: self._ptz_service = None - _LOGGER.debug("Camera '%s' doesn't support PTZ.", - self._name) + _LOGGER.debug("Camera '%s' doesn't support PTZ of type '%s.", + self._name, type_) else: _LOGGER.debug("Camera '%s' doesn't support PTZ.", self._name)