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:
parent
ad259ead50
commit
1cbf8c8049
4 changed files with 307 additions and 0 deletions
|
@ -104,6 +104,9 @@ omit =
|
||||||
homeassistant/components/ffmpeg.py
|
homeassistant/components/ffmpeg.py
|
||||||
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/alarmdotcom.py
|
||||||
homeassistant/components/alarm_control_panel/nx584.py
|
homeassistant/components/alarm_control_panel/nx584.py
|
||||||
homeassistant/components/alarm_control_panel/simplisafe.py
|
homeassistant/components/alarm_control_panel/simplisafe.py
|
||||||
|
|
93
homeassistant/components/sensor/zoneminder.py
Normal file
93
homeassistant/components/sensor/zoneminder.py
Normal 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']
|
92
homeassistant/components/switch/zoneminder.py
Normal file
92
homeassistant/components/switch/zoneminder.py
Normal 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}
|
||||||
|
)
|
119
homeassistant/components/zoneminder.py
Normal file
119
homeassistant/components/zoneminder.py
Normal 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)
|
Loading…
Add table
Add a link
Reference in a new issue