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.
This commit is contained in:
Jeena 2018-09-29 12:29:41 +02:00
parent 909c3bdd63
commit 584ea43d8e

View file

@ -39,6 +39,7 @@ CONF_PROFILE = "profile"
ATTR_PAN = "pan" ATTR_PAN = "pan"
ATTR_TILT = "tilt" ATTR_TILT = "tilt"
ATTR_ZOOM = "zoom" ATTR_ZOOM = "zoom"
ATTR_TYPE = "type"
DIR_UP = "UP" DIR_UP = "UP"
DIR_DOWN = "DOWN" DIR_DOWN = "DOWN"
@ -46,6 +47,9 @@ DIR_LEFT = "LEFT"
DIR_RIGHT = "RIGHT" DIR_RIGHT = "RIGHT"
ZOOM_OUT = "ZOOM_OUT" ZOOM_OUT = "ZOOM_OUT"
ZOOM_IN = "ZOOM_IN" ZOOM_IN = "ZOOM_IN"
TYPE_CONTINOUS = "CONTINOUS"
TYPE_ABSOLUTE = "ABSOLUTE"
TYPE_RELATIVE = "RELATIVE"
SERVICE_PTZ = "onvif_ptz" SERVICE_PTZ = "onvif_ptz"
@ -65,9 +69,11 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
SERVICE_PTZ_SCHEMA = vol.Schema({ SERVICE_PTZ_SCHEMA = vol.Schema({
ATTR_ENTITY_ID: cv.entity_ids, ATTR_ENTITY_ID: cv.entity_ids,
ATTR_PAN: vol.In([DIR_LEFT, DIR_RIGHT]), ATTR_PAN: vol.Or(vol.In([DIR_LEFT, DIR_RIGHT]), vol.Range(min=-1, max=1)),
ATTR_TILT: vol.In([DIR_UP, DIR_DOWN]), ATTR_TILT: vol.Or(vol.In([DIR_UP, DIR_DOWN]), vol.Range(min=-1, max=1)),
ATTR_ZOOM: vol.In([ZOOM_OUT, ZOOM_IN]) 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) pan = service.data.get(ATTR_PAN, None)
tilt = service.data.get(ATTR_TILT, None) tilt = service.data.get(ATTR_TILT, None)
zoom = service.data.get(ATTR_ZOOM, None) zoom = service.data.get(ATTR_ZOOM, None)
type_ = service.data.get(ATTR_TYPE, None)
all_cameras = hass.data[ONVIF_DATA][ENTITIES] all_cameras = hass.data[ONVIF_DATA][ENTITIES]
entity_ids = extract_entity_ids(hass, service) entity_ids = extract_entity_ids(hass, service)
target_cameras = [] target_cameras = []
@ -90,7 +97,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
target_cameras = [camera for camera in all_cameras target_cameras = [camera for camera in all_cameras
if camera.entity_id in entity_ids] if camera.entity_id in entity_ids]
for camera in target_cameras: 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, hass.services.async_register(DOMAIN, SERVICE_PTZ, handle_ptz,
schema=SERVICE_PTZ_SCHEMA) schema=SERVICE_PTZ_SCHEMA)
@ -162,23 +169,34 @@ class ONVIFHassCamera(Camera):
self._name, err) self._name, err)
return return
def perform_ptz(self, pan, tilt, zoom): def perform_ptz(self, type_, pan, tilt, zoom):
"""Perform a PTZ action on the camera.""" """Perform a PTZ action on the camera."""
from onvif import exceptions from onvif import exceptions
if self._ptz_service: if self._ptz_service:
pan_val = 1 if pan == DIR_RIGHT else -1 if pan == DIR_LEFT else 0 type_val = "continous" if type_ == "" else type_
tilt_val = 1 if tilt == DIR_UP else -1 if tilt == DIR_DOWN else 0 pan_val = 1 if pan == DIR_RIGHT else -1 if pan == DIR_LEFT else (pan or 0)
zoom_val = 1 if zoom == ZOOM_IN else -1 if zoom == ZOOM_OUT else 0 tilt_val = 1 if tilt == DIR_UP else -1 if tilt == DIR_DOWN else (tilt or 0)
req = {"Velocity": { zoom_val = 1 if zoom == ZOOM_IN else -1 if zoom == ZOOM_OUT else (zoom or 0)
"PanTilt": {"_x": pan_val, "_y": tilt_val},
"Zoom": {"_x": zoom_val}}} xy = {"_x": pan_val, "_y": tilt_val}
z = {"_x": zoom_val}
try: try:
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) self._ptz_service.ContinuousMove(req)
except exceptions.ONVIFError as err: except exceptions.ONVIFError as err:
if "Bad Request" in err.reason: if "Bad Request" in err.reason:
self._ptz_service = None self._ptz_service = None
_LOGGER.debug("Camera '%s' doesn't support PTZ.", _LOGGER.debug("Camera '%s' doesn't support PTZ of type '%s.",
self._name) self._name, type_)
else: else:
_LOGGER.debug("Camera '%s' doesn't support PTZ.", self._name) _LOGGER.debug("Camera '%s' doesn't support PTZ.", self._name)