From bc30491dc0a54de3f9b3b7faddab883f7ce34098 Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Wed, 16 Jan 2019 01:15:13 -0800 Subject: [PATCH] Add support for connecting to multiple zoneminder instances (#19955) * Add support for connecting to multiple zoneminder instances * Lint * Lint * Clean up and better error handling * Fix config validation * Lint --- homeassistant/components/camera/zoneminder.py | 21 +++--- homeassistant/components/sensor/zoneminder.py | 22 +++--- homeassistant/components/switch/zoneminder.py | 17 +++-- .../components/zoneminder/__init__.py | 72 ++++++++++++------- 4 files changed, 78 insertions(+), 54 deletions(-) diff --git a/homeassistant/components/camera/zoneminder.py b/homeassistant/components/camera/zoneminder.py index 9bde4ac099b..8556fbf1e35 100644 --- a/homeassistant/components/camera/zoneminder.py +++ b/homeassistant/components/camera/zoneminder.py @@ -19,17 +19,18 @@ DEPENDENCIES = ['zoneminder'] def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ZoneMinder cameras.""" filter_urllib3_logging() - zm_client = hass.data[ZONEMINDER_DOMAIN] - - monitors = zm_client.get_monitors() - if not monitors: - _LOGGER.warning("Could not fetch monitors from ZoneMinder") - return - cameras = [] - for monitor in monitors: - _LOGGER.info("Initializing camera %s", monitor.id) - cameras.append(ZoneMinderCamera(monitor, zm_client.verify_ssl)) + for zm_client in hass.data[ZONEMINDER_DOMAIN].values(): + monitors = zm_client.get_monitors() + if not monitors: + _LOGGER.warning( + "Could not fetch monitors from ZoneMinder host: %s" + ) + return + + for monitor in monitors: + _LOGGER.info("Initializing camera %s", monitor.id) + cameras.append(ZoneMinderCamera(monitor, zm_client.verify_ssl)) add_entities(cameras) diff --git a/homeassistant/components/sensor/zoneminder.py b/homeassistant/components/sensor/zoneminder.py index 16513dc58de..abecc99e278 100644 --- a/homeassistant/components/sensor/zoneminder.py +++ b/homeassistant/components/sensor/zoneminder.py @@ -42,19 +42,21 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ZoneMinder sensor platform.""" include_archived = config.get(CONF_INCLUDE_ARCHIVED) - zm_client = hass.data[ZONEMINDER_DOMAIN] - monitors = zm_client.get_monitors() - if not monitors: - _LOGGER.warning('Could not fetch any monitors from ZoneMinder') - sensors = [] - for monitor in monitors: - sensors.append(ZMSensorMonitors(monitor)) + for zm_client in hass.data[ZONEMINDER_DOMAIN].values(): + monitors = zm_client.get_monitors() + if not monitors: + _LOGGER.warning('Could not fetch any monitors from ZoneMinder') - for sensor in config[CONF_MONITORED_CONDITIONS]: - sensors.append(ZMSensorEvents(monitor, include_archived, sensor)) + for monitor in monitors: + sensors.append(ZMSensorMonitors(monitor)) - sensors.append(ZMSensorRunState(zm_client)) + for sensor in config[CONF_MONITORED_CONDITIONS]: + sensors.append( + ZMSensorEvents(monitor, include_archived, sensor) + ) + + sensors.append(ZMSensorRunState(zm_client)) add_entities(sensors) diff --git a/homeassistant/components/switch/zoneminder.py b/homeassistant/components/switch/zoneminder.py index c28fe843b90..b039d2e3ce9 100644 --- a/homeassistant/components/switch/zoneminder.py +++ b/homeassistant/components/switch/zoneminder.py @@ -29,16 +29,15 @@ def setup_platform(hass, config, add_entities, discovery_info=None): on_state = MonitorState(config.get(CONF_COMMAND_ON)) off_state = MonitorState(config.get(CONF_COMMAND_OFF)) - zm_client = hass.data[ZONEMINDER_DOMAIN] - - monitors = zm_client.get_monitors() - if not monitors: - _LOGGER.warning('Could not fetch monitors from ZoneMinder') - return - switches = [] - for monitor in monitors: - switches.append(ZMSwitchMonitors(monitor, on_state, off_state)) + for zm_client in hass.data[ZONEMINDER_DOMAIN].values(): + monitors = zm_client.get_monitors() + if not monitors: + _LOGGER.warning('Could not fetch monitors from ZoneMinder') + return + + for monitor in monitors: + switches.append(ZMSwitchMonitors(monitor, on_state, off_state)) add_entities(switches) diff --git a/homeassistant/components/zoneminder/__init__.py b/homeassistant/components/zoneminder/__init__.py index 8d603d04f59..258841e20d0 100644 --- a/homeassistant/components/zoneminder/__init__.py +++ b/homeassistant/components/zoneminder/__init__.py @@ -8,10 +8,10 @@ import logging import voluptuous as vol +import homeassistant.helpers.config_validation as cv from homeassistant.const import ( CONF_HOST, CONF_PASSWORD, CONF_PATH, CONF_SSL, CONF_USERNAME, - CONF_VERIFY_SSL, ATTR_NAME) -import homeassistant.helpers.config_validation as cv + CONF_VERIFY_SSL, ATTR_NAME, ATTR_ID) _LOGGER = logging.getLogger(__name__) @@ -26,20 +26,23 @@ DEFAULT_TIMEOUT = 10 DEFAULT_VERIFY_SSL = True DOMAIN = 'zoneminder' +HOST_CONFIG_SCHEMA = vol.Schema({ + vol.Required(CONF_HOST): cv.string, + vol.Optional(CONF_PASSWORD): cv.string, + vol.Optional(CONF_PATH, default=DEFAULT_PATH): cv.string, + vol.Optional(CONF_PATH_ZMS, default=DEFAULT_PATH_ZMS): cv.string, + vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, + vol.Optional(CONF_USERNAME): cv.string, + vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, +}) + CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - vol.Required(CONF_HOST): cv.string, - vol.Optional(CONF_PASSWORD): cv.string, - vol.Optional(CONF_PATH, default=DEFAULT_PATH): cv.string, - vol.Optional(CONF_PATH_ZMS, default=DEFAULT_PATH_ZMS): cv.string, - vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, - vol.Optional(CONF_USERNAME): cv.string, - vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, - }) + DOMAIN: vol.All(cv.ensure_list, [HOST_CONFIG_SCHEMA]) }, extra=vol.ALLOW_EXTRA) SERVICE_SET_RUN_STATE = 'set_run_state' SET_RUN_STATE_SCHEMA = vol.Schema({ + vol.Required(ATTR_ID): cv.string, vol.Required(ATTR_NAME): cv.string }) @@ -48,27 +51,46 @@ def setup(hass, config): """Set up the ZoneMinder component.""" from zoneminder.zm import ZoneMinder - conf = config[DOMAIN] - if conf[CONF_SSL]: - schema = 'https' - else: - schema = 'http' + hass.data[DOMAIN] = {} - server_origin = '{}://{}'.format(schema, conf[CONF_HOST]) - hass.data[DOMAIN] = ZoneMinder(server_origin, - conf.get(CONF_USERNAME), - conf.get(CONF_PASSWORD), - conf.get(CONF_PATH), - conf.get(CONF_PATH_ZMS), - conf.get(CONF_VERIFY_SSL)) + success = True + + for conf in config[DOMAIN]: + if conf[CONF_SSL]: + schema = 'https' + else: + schema = 'http' + + host_name = conf[CONF_HOST] + server_origin = '{}://{}'.format(schema, host_name) + zm_client = ZoneMinder( + server_origin, + conf.get(CONF_USERNAME), + conf.get(CONF_PASSWORD), + conf.get(CONF_PATH), + conf.get(CONF_PATH_ZMS), + conf.get(CONF_VERIFY_SSL) + ) + hass.data[DOMAIN][host_name] = zm_client + + success = zm_client.login() and success def set_active_state(call): """Set the ZoneMinder run state to the given state name.""" - return hass.data[DOMAIN].set_active_state(call.data[ATTR_NAME]) + zm_id = call.data[ATTR_ID] + state_name = call.data[ATTR_NAME] + if zm_id not in hass.data[DOMAIN]: + _LOGGER.error('Invalid ZoneMinder host provided: %s', zm_id) + if not hass.data[DOMAIN][zm_id].set_active_state(state_name): + _LOGGER.error( + 'Unable to change ZoneMinder state. Host: %s, state: %s', + zm_id, + state_name + ) hass.services.register( DOMAIN, SERVICE_SET_RUN_STATE, set_active_state, schema=SET_RUN_STATE_SCHEMA ) - return hass.data[DOMAIN].login() + return success