Skip to content

Commit

Permalink
Solve Recorder component failing when using Axis component (#9293)
Browse files Browse the repository at this point in the history
* Bump Axis requirement to v10
Fix issues related to non JSON serializable items and recorder component (8297)
Add support to configure HTTP port (8403)

* Changed local port definition to CONF_PORT

* On request config is now sent to the camera platform as well, and in order better explain what is what the old internal config is now device_config and hass own config is the only one referenced as config

* Missed to add device_config to setup in discovered device

* Bump to V12 that has got a dependency fix

* Update requirements_all

* Add port configuration to automatically discovered devices
Allow setup to pass without Axis being configured in configuration.yaml
  • Loading branch information
Kane610 authored and pvizeli committed Sep 19, 2017
1 parent 8ea7e4b commit dcaa5fe
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 36 deletions.
66 changes: 41 additions & 25 deletions homeassistant/components/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
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,
CONF_PASSWORD, CONF_PORT, CONF_TRIGGER_TIME,
CONF_USERNAME, EVENT_HOMEASSISTANT_STOP)
from homeassistant.components.discovery import SERVICE_AXIS
from homeassistant.helpers import config_validation as cv
Expand All @@ -23,7 +23,7 @@
from homeassistant.helpers.entity import Entity


REQUIREMENTS = ['axis==8']
REQUIREMENTS = ['axis==12']

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -51,6 +51,7 @@
vol.Optional(CONF_USERNAME, default=AXIS_DEFAULT_USERNAME): cv.string,
vol.Optional(CONF_PASSWORD, default=AXIS_DEFAULT_PASSWORD): cv.string,
vol.Optional(CONF_TRIGGER_TIME, default=0): cv.positive_int,
vol.Optional(CONF_PORT, default=80): cv.positive_int,
vol.Optional(ATTR_LOCATION, default=''): cv.string,
})

Expand All @@ -76,7 +77,7 @@
})


def request_configuration(hass, name, host, serialnumber):
def request_configuration(hass, config, name, host, serialnumber):
"""Request configuration steps from the user."""
configurator = hass.components.configurator

Expand All @@ -91,15 +92,15 @@ def configuration_callback(callback_data):
if CONF_NAME not in callback_data:
callback_data[CONF_NAME] = name
try:
config = DEVICE_SCHEMA(callback_data)
device_config = DEVICE_SCHEMA(callback_data)
except vol.Invalid:
configurator.notify_errors(request_id,
"Bad input, please check spelling.")
return False

if setup_device(hass, config):
if setup_device(hass, config, device_config):
config_file = _read_config(hass)
config_file[serialnumber] = dict(config)
config_file[serialnumber] = dict(device_config)
del config_file[serialnumber]['hass']
_write_config(hass, config_file)
configurator.request_done(request_id)
Expand Down Expand Up @@ -132,14 +133,17 @@ def configuration_callback(callback_data):
{'id': ATTR_LOCATION,
'name': "Physical location of device (optional)",
'type': 'text'},
{'id': CONF_PORT,
'name': "HTTP port (default=80)",
'type': 'number'},
{'id': CONF_TRIGGER_TIME,
'name': "Sensor update interval (optional)",
'type': 'number'},
]
)


def setup(hass, base_config):
def setup(hass, config):
"""Common setup for Axis devices."""
def _shutdown(call): # pylint: disable=unused-argument
"""Stop the metadatastream on shutdown."""
Expand All @@ -160,16 +164,17 @@ def axis_device_discovered(service, discovery_info):
if serialnumber in config_file:
# Device config saved to file
try:
config = DEVICE_SCHEMA(config_file[serialnumber])
config[CONF_HOST] = host
device_config = DEVICE_SCHEMA(config_file[serialnumber])
device_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[CONF_NAME])
if not setup_device(hass, config, device_config):
_LOGGER.error("Couldn\'t set up %s",
device_config[CONF_NAME])
else:
# New device, create configuration request for UI
request_configuration(hass, name, host, serialnumber)
request_configuration(hass, config, name, host, serialnumber)
else:
# Device already registered, but on a different IP
device = AXIS_DEVICES[serialnumber]
Expand All @@ -181,13 +186,13 @@ def axis_device_discovered(service, discovery_info):
# Register discovery service
discovery.listen(hass, SERVICE_AXIS, axis_device_discovered)

if DOMAIN in base_config:
for device in base_config[DOMAIN]:
config = base_config[DOMAIN][device]
if CONF_NAME not in config:
config[CONF_NAME] = device
if not setup_device(hass, config):
_LOGGER.error("Couldn\'t set up %s", config[CONF_NAME])
if DOMAIN in config:
for device in config[DOMAIN]:
device_config = config[DOMAIN][device]
if CONF_NAME not in device_config:
device_config[CONF_NAME] = device
if not setup_device(hass, config, device_config):
_LOGGER.error("Couldn\'t set up %s", device_config[CONF_NAME])

# Services to communicate with device.
descriptions = load_yaml_config_file(
Expand Down Expand Up @@ -215,28 +220,39 @@ def vapix_service(call):
return True


def setup_device(hass, config):
def setup_device(hass, config, device_config):
"""Set up device."""
from axis import AxisDevice

config['hass'] = hass
device = AxisDevice(config) # Initialize device
device_config['hass'] = hass
device = AxisDevice(device_config) # Initialize device
enable_metadatastream = False

if device.serial_number is None:
# If there is no serial number a connection could not be made
_LOGGER.error("Couldn\'t connect to %s", config[CONF_HOST])
_LOGGER.error("Couldn\'t connect to %s", device_config[CONF_HOST])
return False

for component in config[CONF_INCLUDE]:
for component in device_config[CONF_INCLUDE]:
if component in EVENT_TYPES:
# Sensors are created by device calling event_initialized
# when receiving initialize messages on metadatastream
device.add_event_topic(convert(component, 'type', 'subscribe'))
if not enable_metadatastream:
enable_metadatastream = True
else:
discovery.load_platform(hass, component, DOMAIN, config)
camera_config = {
CONF_HOST: device_config[CONF_HOST],
CONF_NAME: device_config[CONF_NAME],
CONF_PORT: device_config[CONF_PORT],
CONF_USERNAME: device_config[CONF_USERNAME],
CONF_PASSWORD: device_config[CONF_PASSWORD]
}
discovery.load_platform(hass,
component,
DOMAIN,
camera_config,
config)

if enable_metadatastream:
device.initialize_new_event = event_initialized
Expand Down
26 changes: 16 additions & 10 deletions homeassistant/components/camera/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import logging

from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_PORT,
CONF_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION)
from homeassistant.components.camera.mjpeg import (
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera)
Expand All @@ -19,38 +19,44 @@
DEPENDENCIES = [DOMAIN]


def _get_image_url(host, mode):
def _get_image_url(host, port, mode):
if mode == 'mjpeg':
return 'http://{}/axis-cgi/mjpg/video.cgi'.format(host)
return 'http://{}:{}/axis-cgi/mjpg/video.cgi'.format(host, port)
elif mode == 'single':
return 'http://{}/axis-cgi/jpg/image.cgi'.format(host)
return 'http://{}:{}/axis-cgi/jpg/image.cgi'.format(host, port)


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Axis camera."""
config = {
camera_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_MJPEG_URL: _get_image_url(discovery_info[CONF_HOST],
str(discovery_info[CONF_PORT]),
'mjpeg'),
CONF_STILL_IMAGE_URL: _get_image_url(discovery_info[CONF_HOST],
str(discovery_info[CONF_PORT]),
'single'),
CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION,
}
add_devices([AxisCamera(hass, config)])
add_devices([AxisCamera(hass,
camera_config,
str(discovery_info[CONF_PORT]))])


class AxisCamera(MjpegCamera):
"""AxisCamera class."""

def __init__(self, hass, config):
def __init__(self, hass, config, port):
"""Initialize Axis Communications camera component."""
super().__init__(hass, config)
self.port = port
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')
self._mjpeg_url = _get_image_url(host, self.port, 'mjpeg')
self._still_image_url = _get_image_url(host, self.port, 'single')
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ asterisk_mbox==0.4.0
# avion==0.7

# homeassistant.components.axis
axis==8
axis==12

# homeassistant.components.sensor.modem_callerid
basicmodem==0.7
Expand Down

0 comments on commit dcaa5fe

Please sign in to comment.