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/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
|
||||
|
|
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