Axis service vapix call (#7794)

* Initial commit for an Axis service to do Vapix calls to device

* Added check to see if metadatastream initiated properly

* Make sure to configure the correct IP address when setting up registered devices on system start

* Manage reconnection when device is discovered with a different IP

* Cleaned up setting new IP

* Better naming of event for new IP

* New version of dependency axis

* Fix flake8 failing

* Break out service default strings to constants

* Use the dispatcher and not the core event bus for internal communication
This commit is contained in:
Kane610 2017-06-24 09:14:57 +02:00 committed by Paulus Schoutsen
parent 6234f2d73f
commit 2a1f8af10a
4 changed files with 110 additions and 16 deletions

View File

@ -11,6 +11,7 @@ import os
import voluptuous as vol import voluptuous as vol
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (ATTR_LOCATION, ATTR_TRIPPED, from homeassistant.const import (ATTR_LOCATION, ATTR_TRIPPED,
CONF_HOST, CONF_INCLUDE, CONF_NAME, CONF_HOST, CONF_INCLUDE, CONF_NAME,
CONF_PASSWORD, CONF_TRIGGER_TIME, CONF_PASSWORD, CONF_TRIGGER_TIME,
@ -18,11 +19,12 @@ from homeassistant.const import (ATTR_LOCATION, ATTR_TRIPPED,
from homeassistant.components.discovery import SERVICE_AXIS from homeassistant.components.discovery import SERVICE_AXIS
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import discovery from homeassistant.helpers import discovery
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.loader import get_component from homeassistant.loader import get_component
REQUIREMENTS = ['axis==7'] REQUIREMENTS = ['axis==8']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -59,6 +61,21 @@ CONFIG_SCHEMA = vol.Schema({
}), }),
}, extra=vol.ALLOW_EXTRA) }, extra=vol.ALLOW_EXTRA)
SERVICE_VAPIX_CALL = 'vapix_call'
SERVICE_VAPIX_CALL_RESPONSE = 'vapix_call_response'
SERVICE_CGI = 'cgi'
SERVICE_ACTION = 'action'
SERVICE_PARAM = 'param'
SERVICE_DEFAULT_CGI = 'param.cgi'
SERVICE_DEFAULT_ACTION = 'update'
SERVICE_SCHEMA = vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Required(SERVICE_PARAM): cv.string,
vol.Optional(SERVICE_CGI, default=SERVICE_DEFAULT_CGI): cv.string,
vol.Optional(SERVICE_ACTION, default=SERVICE_DEFAULT_ACTION): cv.string,
})
def request_configuration(hass, name, host, serialnumber): def request_configuration(hass, name, host, serialnumber):
"""Request configuration steps from the user.""" """Request configuration steps from the user."""
@ -135,23 +152,34 @@ def setup(hass, base_config):
def axis_device_discovered(service, discovery_info): def axis_device_discovered(service, discovery_info):
"""Called when axis devices has been found.""" """Called when axis devices has been found."""
host = discovery_info['host'] host = discovery_info[CONF_HOST]
name = discovery_info['hostname'] name = discovery_info['hostname']
serialnumber = discovery_info['properties']['macaddress'] serialnumber = discovery_info['properties']['macaddress']
if serialnumber not in AXIS_DEVICES: if serialnumber not in AXIS_DEVICES:
config_file = _read_config(hass) config_file = _read_config(hass)
if serialnumber in config_file: if serialnumber in config_file:
# Device config saved to file
try: try:
config = DEVICE_SCHEMA(config_file[serialnumber]) config = DEVICE_SCHEMA(config_file[serialnumber])
config[CONF_HOST] = host
except vol.Invalid as err: except vol.Invalid as err:
_LOGGER.error("Bad data from %s. %s", CONFIG_FILE, err) _LOGGER.error("Bad data from %s. %s", CONFIG_FILE, err)
return False return False
if not setup_device(hass, config): if not setup_device(hass, config):
_LOGGER.error("Couldn\'t set up %s", config['name']) _LOGGER.error("Couldn\'t set up %s", config[CONF_NAME])
else: else:
# New device, create configuration request for UI
request_configuration(hass, name, host, serialnumber) request_configuration(hass, name, host, serialnumber)
else:
# Device already registered, but on a different IP
device = AXIS_DEVICES[serialnumber]
device.url = host
async_dispatcher_send(hass,
DOMAIN + '_' + device.name + '_new_ip',
host)
# Register discovery service
discovery.listen(hass, SERVICE_AXIS, axis_device_discovered) discovery.listen(hass, SERVICE_AXIS, axis_device_discovered)
if DOMAIN in base_config: if DOMAIN in base_config:
@ -160,7 +188,30 @@ def setup(hass, base_config):
if CONF_NAME not in config: if CONF_NAME not in config:
config[CONF_NAME] = device config[CONF_NAME] = device
if not setup_device(hass, config): if not setup_device(hass, config):
_LOGGER.error("Couldn\'t set up %s", config['name']) _LOGGER.error("Couldn\'t set up %s", config[CONF_NAME])
# Services to communicate with device.
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
def vapix_service(call):
"""Service to send a message."""
for _, device in AXIS_DEVICES.items():
if device.name == call.data[CONF_NAME]:
response = device.do_request(call.data[SERVICE_CGI],
call.data[SERVICE_ACTION],
call.data[SERVICE_PARAM])
hass.bus.async_fire(SERVICE_VAPIX_CALL_RESPONSE, response)
return True
_LOGGER.info("Couldn\'t find device %s", call.data[CONF_NAME])
return False
# Register service with Home Assistant.
hass.services.register(DOMAIN,
SERVICE_VAPIX_CALL,
vapix_service,
descriptions[DOMAIN][SERVICE_VAPIX_CALL],
schema=SERVICE_SCHEMA)
return True return True
@ -190,8 +241,16 @@ def setup_device(hass, config):
if enable_metadatastream: if enable_metadatastream:
device.initialize_new_event = event_initialized device.initialize_new_event = event_initialized
device.initiate_metadatastream() if not device.initiate_metadatastream():
notification = get_component('persistent_notification')
notification.create(hass,
'Dependency missing for sensors, '
'please check documentation',
title=DOMAIN,
notification_id='axis_notification')
AXIS_DEVICES[device.serial_number] = device AXIS_DEVICES[device.serial_number] = device
return True return True
@ -311,4 +370,4 @@ REMAP = [{'type': 'motion',
'class': 'input', 'class': 'input',
'topic': 'tns1:Device/tnsaxis:IO/Port', 'topic': 'tns1:Device/tnsaxis:IO/Port',
'subscribe': 'onvif:Device/axis:IO/Port', 'subscribe': 'onvif:Device/axis:IO/Port',
'platform': 'sensor'}, ] 'platform': 'binary_sensor'}, ]

View File

@ -7,15 +7,16 @@ https://home-assistant.io/components/camera.axis/
import logging import logging
from homeassistant.const import ( from homeassistant.const import (
CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
CONF_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION) CONF_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION)
from homeassistant.components.camera.mjpeg import ( from homeassistant.components.camera.mjpeg import (
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera) CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera)
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['axis']
DOMAIN = 'axis' DOMAIN = 'axis'
DEPENDENCIES = [DOMAIN]
def _get_image_url(host, mode): def _get_image_url(host, mode):
@ -27,12 +28,29 @@ def _get_image_url(host, mode):
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Axis camera.""" """Setup Axis camera."""
device_info = { config = {
CONF_NAME: discovery_info['name'], CONF_NAME: discovery_info[CONF_NAME],
CONF_USERNAME: discovery_info['username'], CONF_USERNAME: discovery_info[CONF_USERNAME],
CONF_PASSWORD: discovery_info['password'], CONF_PASSWORD: discovery_info[CONF_PASSWORD],
CONF_MJPEG_URL: _get_image_url(discovery_info['host'], 'mjpeg'), CONF_MJPEG_URL: _get_image_url(discovery_info[CONF_HOST], 'mjpeg'),
CONF_STILL_IMAGE_URL: _get_image_url(discovery_info['host'], 'single'), CONF_STILL_IMAGE_URL: _get_image_url(discovery_info[CONF_HOST],
'single'),
CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION, CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION,
} }
add_devices([MjpegCamera(hass, device_info)]) add_devices([AxisCamera(hass, config)])
class AxisCamera(MjpegCamera):
"""AxisCamera class."""
def __init__(self, hass, config):
"""Initialize Axis Communications camera component."""
super().__init__(hass, config)
async_dispatcher_connect(hass,
DOMAIN + '_' + config[CONF_NAME] + '_new_ip',
self._new_ip)
def _new_ip(self, host):
"""Set new IP for video stream."""
self._mjpeg_url = _get_image_url(host, 'mjpeg')
self._still_image_url = _get_image_url(host, 'mjpeg')

View File

@ -453,3 +453,20 @@ eight_sleep:
duration: duration:
description: Duration to heat at the target level in seconds. description: Duration to heat at the target level in seconds.
example: 3600 example: 3600
axis:
vapix_call:
description: Configure device using Vapix parameter management.
fields:
name:
description: Name of device to Configure. [Required]
example: M1065-W
cgi:
description: Which cgi to call on device. [Optional] Default is 'param.cgi'
example: 'applications/control.cgi'
action:
description: What type of call. [Optional] Default is 'update'
example: 'start'
param:
description: What parameter to operate on. [Required]
example: 'package=VideoMotionDetection'

View File

@ -74,7 +74,7 @@ apns2==0.1.1
# avion==0.6 # avion==0.6
# homeassistant.components.axis # homeassistant.components.axis
axis==7 axis==8
# homeassistant.components.sensor.modem_callerid # homeassistant.components.sensor.modem_callerid
basicmodem==0.7 basicmodem==0.7