Axis - prepare for config entry (#17566)
Make component more in line with other more modern components in preparation for config entry support.pull/17938/head
parent
3802fec568
commit
4579717317
|
@ -10,24 +10,21 @@ import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.discovery import SERVICE_AXIS
|
from homeassistant.components.discovery import SERVICE_AXIS
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_LOCATION, ATTR_TRIPPED, CONF_EVENT, CONF_HOST, CONF_INCLUDE,
|
ATTR_LOCATION, CONF_EVENT, CONF_HOST, CONF_INCLUDE,
|
||||||
CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TRIGGER_TIME, CONF_USERNAME,
|
CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TRIGGER_TIME, CONF_USERNAME,
|
||||||
EVENT_HOMEASSISTANT_STOP)
|
EVENT_HOMEASSISTANT_STOP)
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers import discovery
|
from homeassistant.helpers import discovery
|
||||||
from homeassistant.helpers.dispatcher import dispatcher_send
|
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||||
from homeassistant.helpers.entity import Entity
|
|
||||||
from homeassistant.util.json import load_json, save_json
|
from homeassistant.util.json import load_json, save_json
|
||||||
|
|
||||||
REQUIREMENTS = ['axis==14']
|
REQUIREMENTS = ['axis==16']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DOMAIN = 'axis'
|
DOMAIN = 'axis'
|
||||||
CONFIG_FILE = 'axis.conf'
|
CONFIG_FILE = 'axis.conf'
|
||||||
|
|
||||||
AXIS_DEVICES = {}
|
|
||||||
|
|
||||||
EVENT_TYPES = ['motion', 'vmd3', 'pir', 'sound',
|
EVENT_TYPES = ['motion', 'vmd3', 'pir', 'sound',
|
||||||
'daynight', 'tampering', 'input']
|
'daynight', 'tampering', 'input']
|
||||||
|
|
||||||
|
@ -99,8 +96,6 @@ def request_configuration(hass, config, name, host, serialnumber):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if setup_device(hass, config, device_config):
|
if setup_device(hass, config, device_config):
|
||||||
del device_config['events']
|
|
||||||
del device_config['signal']
|
|
||||||
config_file = load_json(hass.config.path(CONFIG_FILE))
|
config_file = load_json(hass.config.path(CONFIG_FILE))
|
||||||
config_file[serialnumber] = dict(device_config)
|
config_file[serialnumber] = dict(device_config)
|
||||||
save_json(hass.config.path(CONFIG_FILE), config_file)
|
save_json(hass.config.path(CONFIG_FILE), config_file)
|
||||||
|
@ -146,9 +141,11 @@ def request_configuration(hass, config, name, host, serialnumber):
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
"""Set up for Axis devices."""
|
"""Set up for Axis devices."""
|
||||||
|
hass.data[DOMAIN] = {}
|
||||||
|
|
||||||
def _shutdown(call):
|
def _shutdown(call):
|
||||||
"""Stop the event stream on shutdown."""
|
"""Stop the event stream on shutdown."""
|
||||||
for serialnumber, device in AXIS_DEVICES.items():
|
for serialnumber, device in hass.data[DOMAIN].items():
|
||||||
_LOGGER.info("Stopping event stream for %s.", serialnumber)
|
_LOGGER.info("Stopping event stream for %s.", serialnumber)
|
||||||
device.stop()
|
device.stop()
|
||||||
|
|
||||||
|
@ -160,7 +157,7 @@ def setup(hass, config):
|
||||||
name = discovery_info['hostname']
|
name = discovery_info['hostname']
|
||||||
serialnumber = discovery_info['properties']['macaddress']
|
serialnumber = discovery_info['properties']['macaddress']
|
||||||
|
|
||||||
if serialnumber not in AXIS_DEVICES:
|
if serialnumber not in hass.data[DOMAIN]:
|
||||||
config_file = load_json(hass.config.path(CONFIG_FILE))
|
config_file = load_json(hass.config.path(CONFIG_FILE))
|
||||||
if serialnumber in config_file:
|
if serialnumber in config_file:
|
||||||
# Device config previously saved to file
|
# Device config previously saved to file
|
||||||
|
@ -178,7 +175,7 @@ def setup(hass, config):
|
||||||
request_configuration(hass, config, name, host, serialnumber)
|
request_configuration(hass, config, name, host, serialnumber)
|
||||||
else:
|
else:
|
||||||
# Device already registered, but on a different IP
|
# Device already registered, but on a different IP
|
||||||
device = AXIS_DEVICES[serialnumber]
|
device = hass.data[DOMAIN][serialnumber]
|
||||||
device.config.host = host
|
device.config.host = host
|
||||||
dispatcher_send(hass, DOMAIN + '_' + device.name + '_new_ip', host)
|
dispatcher_send(hass, DOMAIN + '_' + device.name + '_new_ip', host)
|
||||||
|
|
||||||
|
@ -195,7 +192,7 @@ def setup(hass, config):
|
||||||
|
|
||||||
def vapix_service(call):
|
def vapix_service(call):
|
||||||
"""Service to send a message."""
|
"""Service to send a message."""
|
||||||
for _, device in AXIS_DEVICES.items():
|
for device in hass.data[DOMAIN].values():
|
||||||
if device.name == call.data[CONF_NAME]:
|
if device.name == call.data[CONF_NAME]:
|
||||||
response = device.vapix.do_request(
|
response = device.vapix.do_request(
|
||||||
call.data[SERVICE_CGI],
|
call.data[SERVICE_CGI],
|
||||||
|
@ -214,7 +211,7 @@ def setup(hass, config):
|
||||||
|
|
||||||
def setup_device(hass, config, device_config):
|
def setup_device(hass, config, device_config):
|
||||||
"""Set up an Axis device."""
|
"""Set up an Axis device."""
|
||||||
from axis import AxisDevice
|
import axis
|
||||||
|
|
||||||
def signal_callback(action, event):
|
def signal_callback(action, event):
|
||||||
"""Call to configure events when initialized on event stream."""
|
"""Call to configure events when initialized on event stream."""
|
||||||
|
@ -229,18 +226,32 @@ def setup_device(hass, config, device_config):
|
||||||
discovery.load_platform(
|
discovery.load_platform(
|
||||||
hass, component, DOMAIN, event_config, config)
|
hass, component, DOMAIN, event_config, config)
|
||||||
|
|
||||||
event_types = list(filter(lambda x: x in device_config[CONF_INCLUDE],
|
event_types = [
|
||||||
EVENT_TYPES))
|
event
|
||||||
device_config['events'] = event_types
|
for event in device_config[CONF_INCLUDE]
|
||||||
device_config['signal'] = signal_callback
|
if event in EVENT_TYPES
|
||||||
device = AxisDevice(hass.loop, **device_config)
|
]
|
||||||
device.name = device_config[CONF_NAME]
|
|
||||||
|
|
||||||
if device.serial_number is None:
|
device = axis.AxisDevice(
|
||||||
# If there is no serial number a connection could not be made
|
loop=hass.loop, host=device_config[CONF_HOST],
|
||||||
_LOGGER.error("Couldn't connect to %s", device_config[CONF_HOST])
|
username=device_config[CONF_USERNAME],
|
||||||
|
password=device_config[CONF_PASSWORD],
|
||||||
|
port=device_config[CONF_PORT], web_proto='http',
|
||||||
|
event_types=event_types, signal=signal_callback)
|
||||||
|
|
||||||
|
try:
|
||||||
|
hass.data[DOMAIN][device.vapix.serial_number] = device
|
||||||
|
|
||||||
|
except axis.Unauthorized:
|
||||||
|
_LOGGER.error("Credentials for %s are faulty",
|
||||||
|
device_config[CONF_HOST])
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
except axis.RequestError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
device.name = device_config[CONF_NAME]
|
||||||
|
|
||||||
for component in device_config[CONF_INCLUDE]:
|
for component in device_config[CONF_INCLUDE]:
|
||||||
if component == 'camera':
|
if component == 'camera':
|
||||||
camera_config = {
|
camera_config = {
|
||||||
|
@ -253,51 +264,6 @@ def setup_device(hass, config, device_config):
|
||||||
discovery.load_platform(
|
discovery.load_platform(
|
||||||
hass, component, DOMAIN, camera_config, config)
|
hass, component, DOMAIN, camera_config, config)
|
||||||
|
|
||||||
AXIS_DEVICES[device.serial_number] = device
|
|
||||||
if event_types:
|
if event_types:
|
||||||
hass.add_job(device.start)
|
hass.add_job(device.start)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class AxisDeviceEvent(Entity):
|
|
||||||
"""Representation of a Axis device event."""
|
|
||||||
|
|
||||||
def __init__(self, event_config):
|
|
||||||
"""Initialize the event."""
|
|
||||||
self.axis_event = event_config[CONF_EVENT]
|
|
||||||
self._name = '{}_{}_{}'.format(
|
|
||||||
event_config[CONF_NAME], self.axis_event.event_type,
|
|
||||||
self.axis_event.id)
|
|
||||||
self.location = event_config[ATTR_LOCATION]
|
|
||||||
self.axis_event.callback = self._update_callback
|
|
||||||
|
|
||||||
def _update_callback(self):
|
|
||||||
"""Update the sensor's state, if needed."""
|
|
||||||
self.schedule_update_ha_state(True)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""Return the name of the event."""
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_class(self):
|
|
||||||
"""Return the class of the event."""
|
|
||||||
return self.axis_event.event_class
|
|
||||||
|
|
||||||
@property
|
|
||||||
def should_poll(self):
|
|
||||||
"""Return the polling state. No polling needed."""
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_state_attributes(self):
|
|
||||||
"""Return the state attributes of the event."""
|
|
||||||
attr = {}
|
|
||||||
|
|
||||||
tripped = self.axis_event.is_tripped
|
|
||||||
attr[ATTR_TRIPPED] = 'True' if tripped else 'False'
|
|
||||||
|
|
||||||
attr[ATTR_LOCATION] = self.location
|
|
||||||
|
|
||||||
return attr
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
vapix_call:
|
||||||
|
description: Configure device using Vapix parameter management.
|
||||||
|
fields:
|
||||||
|
name:
|
||||||
|
description: Name of device to Configure. [Required]
|
||||||
|
example: M1065-W
|
||||||
|
cgi:
|
||||||
|
description: Which cgi to call on device. [Optional] Default is 'param.cgi'
|
||||||
|
example: 'applications/control.cgi'
|
||||||
|
action:
|
||||||
|
description: What type of call. [Optional] Default is 'update'
|
||||||
|
example: 'start'
|
||||||
|
param:
|
||||||
|
description: What parameter to operate on. [Required]
|
||||||
|
example: 'package=VideoMotionDetection'
|
|
@ -7,10 +7,11 @@ https://home-assistant.io/components/binary_sensor.axis/
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.axis import AxisDeviceEvent
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||||
from homeassistant.const import CONF_TRIGGER_TIME
|
from homeassistant.const import (
|
||||||
from homeassistant.helpers.event import track_point_in_utc_time
|
ATTR_LOCATION, CONF_EVENT, CONF_NAME, CONF_TRIGGER_TIME)
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.event import async_track_point_in_utc_time
|
||||||
from homeassistant.util.dt import utcnow
|
from homeassistant.util.dt import utcnow
|
||||||
|
|
||||||
DEPENDENCIES = ['axis']
|
DEPENDENCIES = ['axis']
|
||||||
|
@ -20,48 +21,71 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
"""Set up the Axis binary devices."""
|
"""Set up the Axis binary devices."""
|
||||||
add_entities([AxisBinarySensor(hass, discovery_info)], True)
|
add_entities([AxisBinarySensor(discovery_info)], True)
|
||||||
|
|
||||||
|
|
||||||
class AxisBinarySensor(AxisDeviceEvent, BinarySensorDevice):
|
class AxisBinarySensor(BinarySensorDevice):
|
||||||
"""Representation of a binary Axis event."""
|
"""Representation of a binary Axis event."""
|
||||||
|
|
||||||
def __init__(self, hass, event_config):
|
def __init__(self, event_config):
|
||||||
"""Initialize the Axis binary sensor."""
|
"""Initialize the Axis binary sensor."""
|
||||||
self.hass = hass
|
self.axis_event = event_config[CONF_EVENT]
|
||||||
self._state = False
|
self.device_name = event_config[CONF_NAME]
|
||||||
self._delay = event_config[CONF_TRIGGER_TIME]
|
self.location = event_config[ATTR_LOCATION]
|
||||||
self._timer = None
|
self.delay = event_config[CONF_TRIGGER_TIME]
|
||||||
AxisDeviceEvent.__init__(self, event_config)
|
self.remove_timer = None
|
||||||
|
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
"""Subscribe sensors events."""
|
||||||
|
self.axis_event.callback = self._update_callback
|
||||||
|
|
||||||
|
def _update_callback(self):
|
||||||
|
"""Update the sensor's state, if needed."""
|
||||||
|
if self.remove_timer is not None:
|
||||||
|
self.remove_timer()
|
||||||
|
self.remove_timer = None
|
||||||
|
|
||||||
|
if self.delay == 0 or self.is_on:
|
||||||
|
self.schedule_update_ha_state()
|
||||||
|
else: # Run timer to delay updating the state
|
||||||
|
@callback
|
||||||
|
def _delay_update(now):
|
||||||
|
"""Timer callback for sensor update."""
|
||||||
|
_LOGGER.debug("%s called delayed (%s sec) update",
|
||||||
|
self.name, self.delay)
|
||||||
|
self.async_schedule_update_ha_state()
|
||||||
|
self.remove_timer = None
|
||||||
|
|
||||||
|
self.remove_timer = async_track_point_in_utc_time(
|
||||||
|
self.hass, _delay_update,
|
||||||
|
utcnow() + timedelta(seconds=self.delay))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Return true if event is active."""
|
"""Return true if event is active."""
|
||||||
return self._state
|
return self.axis_event.is_tripped
|
||||||
|
|
||||||
def update(self):
|
@property
|
||||||
"""Get the latest data and update the state."""
|
def name(self):
|
||||||
self._state = self.axis_event.is_tripped
|
"""Return the name of the event."""
|
||||||
|
return '{}_{}_{}'.format(
|
||||||
|
self.device_name, self.axis_event.event_type, self.axis_event.id)
|
||||||
|
|
||||||
def _update_callback(self):
|
@property
|
||||||
"""Update the sensor's state, if needed."""
|
def device_class(self):
|
||||||
self.update()
|
"""Return the class of the event."""
|
||||||
|
return self.axis_event.event_class
|
||||||
|
|
||||||
if self._timer is not None:
|
@property
|
||||||
self._timer()
|
def should_poll(self):
|
||||||
self._timer = None
|
"""No polling needed."""
|
||||||
|
return False
|
||||||
|
|
||||||
if self._delay > 0 and not self.is_on:
|
@property
|
||||||
# Set timer to wait until updating the state
|
def device_state_attributes(self):
|
||||||
def _delay_update(now):
|
"""Return the state attributes of the event."""
|
||||||
"""Timer callback for sensor update."""
|
attr = {}
|
||||||
_LOGGER.debug("%s called delayed (%s sec) update",
|
|
||||||
self._name, self._delay)
|
|
||||||
self.schedule_update_ha_state()
|
|
||||||
self._timer = None
|
|
||||||
|
|
||||||
self._timer = track_point_in_utc_time(
|
attr[ATTR_LOCATION] = self.location
|
||||||
self.hass, _delay_update,
|
|
||||||
utcnow() + timedelta(seconds=self._delay))
|
return attr
|
||||||
else:
|
|
||||||
self.schedule_update_ha_state()
|
|
||||||
|
|
|
@ -260,23 +260,6 @@ eight_sleep:
|
||||||
description: Duration to heat at the target level in seconds.
|
description: Duration to heat at the target level in seconds.
|
||||||
example: 3600
|
example: 3600
|
||||||
|
|
||||||
axis:
|
|
||||||
vapix_call:
|
|
||||||
description: Configure device using Vapix parameter management.
|
|
||||||
fields:
|
|
||||||
name:
|
|
||||||
description: Name of device to Configure. [Required]
|
|
||||||
example: M1065-W
|
|
||||||
cgi:
|
|
||||||
description: Which cgi to call on device. [Optional] Default is 'param.cgi'
|
|
||||||
example: 'applications/control.cgi'
|
|
||||||
action:
|
|
||||||
description: What type of call. [Optional] Default is 'update'
|
|
||||||
example: 'start'
|
|
||||||
param:
|
|
||||||
description: What parameter to operate on. [Required]
|
|
||||||
example: 'package=VideoMotionDetection'
|
|
||||||
|
|
||||||
apple_tv:
|
apple_tv:
|
||||||
apple_tv_authenticate:
|
apple_tv_authenticate:
|
||||||
description: Start AirPlay device authentication.
|
description: Start AirPlay device authentication.
|
||||||
|
|
|
@ -162,7 +162,7 @@ async-upnp-client==0.12.7
|
||||||
# avion==0.7
|
# avion==0.7
|
||||||
|
|
||||||
# homeassistant.components.axis
|
# homeassistant.components.axis
|
||||||
axis==14
|
axis==16
|
||||||
|
|
||||||
# homeassistant.components.tts.baidu
|
# homeassistant.components.tts.baidu
|
||||||
baidu-aip==1.6.6
|
baidu-aip==1.6.6
|
||||||
|
|
Loading…
Reference in New Issue