diff --git a/homeassistant/components/axis.py b/homeassistant/components/axis.py index 593eee2356e..bb1ec05496a 100644 --- a/homeassistant/components/axis.py +++ b/homeassistant/components/axis.py @@ -11,6 +11,7 @@ import os import voluptuous as vol +from homeassistant.config import load_yaml_config_file from homeassistant.const import (ATTR_LOCATION, ATTR_TRIPPED, CONF_HOST, CONF_INCLUDE, CONF_NAME, 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.helpers import config_validation as cv from homeassistant.helpers import discovery +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import Entity from homeassistant.loader import get_component -REQUIREMENTS = ['axis==7'] +REQUIREMENTS = ['axis==8'] _LOGGER = logging.getLogger(__name__) @@ -59,6 +61,21 @@ CONFIG_SCHEMA = vol.Schema({ }), }, 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): """Request configuration steps from the user.""" @@ -135,23 +152,34 @@ def setup(hass, base_config): def axis_device_discovered(service, discovery_info): """Called when axis devices has been found.""" - host = discovery_info['host'] + host = discovery_info[CONF_HOST] name = discovery_info['hostname'] serialnumber = discovery_info['properties']['macaddress'] if serialnumber not in AXIS_DEVICES: config_file = _read_config(hass) if serialnumber in config_file: + # Device config saved to file try: config = DEVICE_SCHEMA(config_file[serialnumber]) + config[CONF_HOST] = host except vol.Invalid as err: _LOGGER.error("Bad data from %s. %s", CONFIG_FILE, err) return False 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: + # New device, create configuration request for UI 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) if DOMAIN in base_config: @@ -160,7 +188,30 @@ def setup(hass, base_config): if CONF_NAME not in config: config[CONF_NAME] = device 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 @@ -190,8 +241,16 @@ def setup_device(hass, config): if enable_metadatastream: 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 + return True @@ -311,4 +370,4 @@ REMAP = [{'type': 'motion', 'class': 'input', 'topic': 'tns1:Device/tnsaxis:IO/Port', 'subscribe': 'onvif:Device/axis:IO/Port', - 'platform': 'sensor'}, ] + 'platform': 'binary_sensor'}, ] diff --git a/homeassistant/components/camera/axis.py b/homeassistant/components/camera/axis.py index 3de1c568745..b0295b9ee34 100644 --- a/homeassistant/components/camera/axis.py +++ b/homeassistant/components/camera/axis.py @@ -7,15 +7,16 @@ https://home-assistant.io/components/camera.axis/ import logging from homeassistant.const import ( - CONF_NAME, CONF_USERNAME, CONF_PASSWORD, + CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION) from homeassistant.components.camera.mjpeg import ( CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera) +from homeassistant.helpers.dispatcher import async_dispatcher_connect _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['axis'] DOMAIN = 'axis' +DEPENDENCIES = [DOMAIN] 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): """Setup Axis camera.""" - device_info = { - CONF_NAME: discovery_info['name'], - CONF_USERNAME: discovery_info['username'], - CONF_PASSWORD: discovery_info['password'], - CONF_MJPEG_URL: _get_image_url(discovery_info['host'], 'mjpeg'), - CONF_STILL_IMAGE_URL: _get_image_url(discovery_info['host'], 'single'), + config = { + CONF_NAME: discovery_info[CONF_NAME], + CONF_USERNAME: discovery_info[CONF_USERNAME], + CONF_PASSWORD: discovery_info[CONF_PASSWORD], + CONF_MJPEG_URL: _get_image_url(discovery_info[CONF_HOST], 'mjpeg'), + CONF_STILL_IMAGE_URL: _get_image_url(discovery_info[CONF_HOST], + 'single'), 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') diff --git a/homeassistant/components/services.yaml b/homeassistant/components/services.yaml index 94b7002093f..d81d14fc991 100644 --- a/homeassistant/components/services.yaml +++ b/homeassistant/components/services.yaml @@ -453,3 +453,20 @@ eight_sleep: duration: description: Duration to heat at the target level in seconds. 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' diff --git a/requirements_all.txt b/requirements_all.txt index c15ca949f6a..0dfbf9a9ae1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -74,7 +74,7 @@ apns2==0.1.1 # avion==0.6 # homeassistant.components.axis -axis==7 +axis==8 # homeassistant.components.sensor.modem_callerid basicmodem==0.7