Zoneminder component (#3795)

* Initial Zoneminder commit

* Fixing bug when ZM sets its function to 'None'

* Adding zoneminder to coverage

* Quick Doc fix

* Update zoneminder.py

Doc Fix

* making the url base optional
This commit is contained in:
Richard Cox 2016-10-14 20:56:40 -07:00 committed by Paulus Schoutsen
parent ad259ead50
commit 1cbf8c8049
4 changed files with 307 additions and 0 deletions

View file

@ -104,6 +104,9 @@ omit =
homeassistant/components/ffmpeg.py
homeassistant/components/*/ffmpeg.py
homeassistant/components/zoneminder.py
homeassistant/components/*/zoneminder.py
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/nx584.py
homeassistant/components/alarm_control_panel/simplisafe.py

View file

@ -0,0 +1,93 @@
"""
Support for Zoneminder Sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.zoneminder/
"""
import logging
import homeassistant.components.zoneminder as zoneminder
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['zoneminder']
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Zoneminder platform."""
sensors = []
monitors = zoneminder.get_state('api/monitors.json')
for i in monitors['monitors']:
sensors.append(
ZMSensorMonitors(int(i['Monitor']['Id']), i['Monitor']['Name'])
)
sensors.append(
ZMSensorEvents(int(i['Monitor']['Id']), i['Monitor']['Name'])
)
add_devices(sensors)
class ZMSensorMonitors(Entity):
"""Get the status of each monitor."""
def __init__(self, monitor_id, monitor_name):
"""Initiate monitor sensor."""
self._monitor_id = monitor_id
self._monitor_name = monitor_name
self._state = None
@property
def name(self):
"""Return the name of the sensor."""
return "%s Status" % self._monitor_name
@property
def state(self):
"""Return the state of the sensor."""
return self._state
def update(self):
"""Update the sensor."""
monitor = zoneminder.get_state(
'api/monitors/%i.json' % self._monitor_id
)
if monitor['monitor']['Monitor']['Function'] is None:
self._state = "None"
else:
self._state = monitor['monitor']['Monitor']['Function']
class ZMSensorEvents(Entity):
"""Get the number of events for each monitor."""
def __init__(self, monitor_id, monitor_name):
"""Initiate event sensor."""
self._monitor_id = monitor_id
self._monitor_name = monitor_name
self._state = None
@property
def name(self):
"""Return the name of the sensor."""
return "%s Events" % self._monitor_name
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return 'Events'
@property
def state(self):
"""Return the state of the sensor."""
return self._state
def update(self):
"""Update the sensor."""
event = zoneminder.get_state(
'api/events/index/MonitorId:%i.json' % self._monitor_id
)
self._state = event['pagination']['count']

View file

@ -0,0 +1,92 @@
"""
Support for Zoneminder switches.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.zoneminder/
"""
import logging
import voluptuous as vol
from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_COMMAND_ON, CONF_COMMAND_OFF)
import homeassistant.helpers.config_validation as cv
import homeassistant.components.zoneminder as zoneminder
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COMMAND_ON): cv.string,
vol.Required(CONF_COMMAND_OFF): cv.string,
})
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['zoneminder']
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Zoneminder switch."""
on_state = config.get(CONF_COMMAND_ON)
off_state = config.get(CONF_COMMAND_OFF)
switches = []
monitors = zoneminder.get_state('api/monitors.json')
for i in monitors['monitors']:
switches.append(
ZMSwitchMonitors(
int(i['Monitor']['Id']),
i['Monitor']['Name'],
on_state,
off_state
)
)
add_devices(switches)
class ZMSwitchMonitors(SwitchDevice):
"""Representation of an zoneminder switch."""
icon = 'mdi:record-rec'
def __init__(self, monitor_id, monitor_name, on_state, off_state):
"""Initialize the switch."""
self._monitor_id = monitor_id
self._monitor_name = monitor_name
self._on_state = on_state
self._off_state = off_state
self._state = None
@property
def name(self):
"""Return the name of the switch."""
return "%s State" % self._monitor_name
def update(self):
"""Update the switch value."""
monitor = zoneminder.get_state(
'api/monitors/%i.json' % self._monitor_id
)
current_state = monitor['monitor']['Monitor']['Function']
self._state = True if current_state == self._on_state else False
@property
def is_on(self):
"""Return True if entity is on."""
return self._state
def turn_on(self):
"""Turn the entity on."""
zoneminder.change_state(
'api/monitors/%i.json' % self._monitor_id,
{'Monitor[Function]': self._on_state}
)
def turn_off(self):
"""Turn the entity off."""
zoneminder.change_state(
'api/monitors/%i.json' % self._monitor_id,
{'Monitor[Function]': self._off_state}
)

View file

@ -0,0 +1,119 @@
"""
Support for Zoneminder.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/zoneminder/
"""
import logging
import json
from urllib.parse import urljoin
import requests
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
CONF_URL, CONF_HOST, CONF_PASSWORD, CONF_USERNAME)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = []
DOMAIN = 'zoneminder'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_URL, default="/zm/"): cv.string,
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string
})
}, extra=vol.ALLOW_EXTRA)
LOGIN_RETRIES = 2
ZM = {}
def setup(hass, config):
"""Setup the zonminder platform."""
global ZM
ZM = {}
conf = config[DOMAIN]
url = urljoin("http://" + conf[CONF_HOST], conf[CONF_URL])
username = conf.get(CONF_USERNAME, None)
password = conf.get(CONF_PASSWORD, None)
ZM['url'] = url
ZM['username'] = username
ZM['password'] = password
return login()
# pylint: disable=no-member
def login():
"""Login to the zoneminder api."""
_LOGGER.debug("Attempting to login to zoneminder")
login_post = {'view': 'console', 'action': 'login'}
if ZM['username']:
login_post['username'] = ZM['username']
if ZM['password']:
login_post['password'] = ZM['password']
req = requests.post(ZM['url'] + '/index.php', data=login_post)
ZM['cookies'] = req.cookies
# Login calls returns a 200 repsonse on both failure and success..
# The only way to tell if you logged in correctly is to issue an api call.
req = requests.get(
ZM['url'] + 'api/host/getVersion.json',
cookies=ZM['cookies']
)
if req.status_code != requests.codes.ok:
_LOGGER.error("Connection error logging into ZoneMinder")
return False
return True
# pylint: disable=no-member
def get_state(api_url):
"""Get a state from the zoneminder API service."""
# Since the API uses sessions that expire, sometimes we need
# to re-auth if the call fails.
for _ in range(LOGIN_RETRIES):
req = requests.get(urljoin(ZM['url'], api_url), cookies=ZM['cookies'])
if req.status_code != requests.codes.ok:
login()
else:
break
else:
_LOGGER.exception("Unable to get API response")
return json.loads(req.text)
# pylint: disable=no-member
def change_state(api_url, post_data):
"""Update a state using the Zoneminder API."""
for _ in range(LOGIN_RETRIES):
req = requests.post(
urljoin(ZM['url'], api_url),
data=post_data,
cookies=ZM['cookies'])
if req.status_code != requests.codes.ok:
login()
else:
break
else:
_LOGGER.exception("Unable to get API response")
return json.loads(req.text)