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.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,
|
||||
EVENT_HOMEASSISTANT_STOP)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers import discovery
|
||||
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.util.json import load_json, save_json
|
||||
|
||||
REQUIREMENTS = ['axis==14']
|
||||
REQUIREMENTS = ['axis==16']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = 'axis'
|
||||
CONFIG_FILE = 'axis.conf'
|
||||
|
||||
AXIS_DEVICES = {}
|
||||
|
||||
EVENT_TYPES = ['motion', 'vmd3', 'pir', 'sound',
|
||||
'daynight', 'tampering', 'input']
|
||||
|
||||
|
@ -99,8 +96,6 @@ def request_configuration(hass, config, name, host, serialnumber):
|
|||
return False
|
||||
|
||||
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[serialnumber] = dict(device_config)
|
||||
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):
|
||||
"""Set up for Axis devices."""
|
||||
hass.data[DOMAIN] = {}
|
||||
|
||||
def _shutdown(call):
|
||||
"""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)
|
||||
device.stop()
|
||||
|
||||
|
@ -160,7 +157,7 @@ def setup(hass, config):
|
|||
name = discovery_info['hostname']
|
||||
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))
|
||||
if serialnumber in config_file:
|
||||
# Device config previously saved to file
|
||||
|
@ -178,7 +175,7 @@ def setup(hass, config):
|
|||
request_configuration(hass, config, name, host, serialnumber)
|
||||
else:
|
||||
# Device already registered, but on a different IP
|
||||
device = AXIS_DEVICES[serialnumber]
|
||||
device = hass.data[DOMAIN][serialnumber]
|
||||
device.config.host = host
|
||||
dispatcher_send(hass, DOMAIN + '_' + device.name + '_new_ip', host)
|
||||
|
||||
|
@ -195,7 +192,7 @@ def setup(hass, config):
|
|||
|
||||
def vapix_service(call):
|
||||
"""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]:
|
||||
response = device.vapix.do_request(
|
||||
call.data[SERVICE_CGI],
|
||||
|
@ -214,7 +211,7 @@ def setup(hass, config):
|
|||
|
||||
def setup_device(hass, config, device_config):
|
||||
"""Set up an Axis device."""
|
||||
from axis import AxisDevice
|
||||
import axis
|
||||
|
||||
def signal_callback(action, event):
|
||||
"""Call to configure events when initialized on event stream."""
|
||||
|
@ -229,18 +226,32 @@ def setup_device(hass, config, device_config):
|
|||
discovery.load_platform(
|
||||
hass, component, DOMAIN, event_config, config)
|
||||
|
||||
event_types = list(filter(lambda x: x in device_config[CONF_INCLUDE],
|
||||
EVENT_TYPES))
|
||||
device_config['events'] = event_types
|
||||
device_config['signal'] = signal_callback
|
||||
device = AxisDevice(hass.loop, **device_config)
|
||||
device.name = device_config[CONF_NAME]
|
||||
event_types = [
|
||||
event
|
||||
for event in device_config[CONF_INCLUDE]
|
||||
if event in EVENT_TYPES
|
||||
]
|
||||
|
||||
if device.serial_number is None:
|
||||
# If there is no serial number a connection could not be made
|
||||
_LOGGER.error("Couldn't connect to %s", device_config[CONF_HOST])
|
||||
device = axis.AxisDevice(
|
||||
loop=hass.loop, host=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
|
||||
|
||||
except axis.RequestError:
|
||||
return False
|
||||
|
||||
device.name = device_config[CONF_NAME]
|
||||
|
||||
for component in device_config[CONF_INCLUDE]:
|
||||
if component == 'camera':
|
||||
camera_config = {
|
||||
|
@ -253,51 +264,6 @@ def setup_device(hass, config, device_config):
|
|||
discovery.load_platform(
|
||||
hass, component, DOMAIN, camera_config, config)
|
||||
|
||||
AXIS_DEVICES[device.serial_number] = device
|
||||
if event_types:
|
||||
hass.add_job(device.start)
|
||||
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
|
||||
import logging
|
||||
|
||||
from homeassistant.components.axis import AxisDeviceEvent
|
||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||
from homeassistant.const import CONF_TRIGGER_TIME
|
||||
from homeassistant.helpers.event import track_point_in_utc_time
|
||||
from homeassistant.const import (
|
||||
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
|
||||
|
||||
DEPENDENCIES = ['axis']
|
||||
|
@ -20,48 +21,71 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""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."""
|
||||
|
||||
def __init__(self, hass, event_config):
|
||||
def __init__(self, event_config):
|
||||
"""Initialize the Axis binary sensor."""
|
||||
self.hass = hass
|
||||
self._state = False
|
||||
self._delay = event_config[CONF_TRIGGER_TIME]
|
||||
self._timer = None
|
||||
AxisDeviceEvent.__init__(self, event_config)
|
||||
self.axis_event = event_config[CONF_EVENT]
|
||||
self.device_name = event_config[CONF_NAME]
|
||||
self.location = event_config[ATTR_LOCATION]
|
||||
self.delay = event_config[CONF_TRIGGER_TIME]
|
||||
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
|
||||
def is_on(self):
|
||||
"""Return true if event is active."""
|
||||
return self._state
|
||||
return self.axis_event.is_tripped
|
||||
|
||||
def update(self):
|
||||
"""Get the latest data and update the state."""
|
||||
self._state = self.axis_event.is_tripped
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the event."""
|
||||
return '{}_{}_{}'.format(
|
||||
self.device_name, self.axis_event.event_type, self.axis_event.id)
|
||||
|
||||
def _update_callback(self):
|
||||
"""Update the sensor's state, if needed."""
|
||||
self.update()
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the class of the event."""
|
||||
return self.axis_event.event_class
|
||||
|
||||
if self._timer is not None:
|
||||
self._timer()
|
||||
self._timer = None
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""No polling needed."""
|
||||
return False
|
||||
|
||||
if self._delay > 0 and not self.is_on:
|
||||
# Set timer to wait until updating the state
|
||||
def _delay_update(now):
|
||||
"""Timer callback for sensor update."""
|
||||
_LOGGER.debug("%s called delayed (%s sec) update",
|
||||
self._name, self._delay)
|
||||
self.schedule_update_ha_state()
|
||||
self._timer = None
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes of the event."""
|
||||
attr = {}
|
||||
|
||||
self._timer = track_point_in_utc_time(
|
||||
self.hass, _delay_update,
|
||||
utcnow() + timedelta(seconds=self._delay))
|
||||
else:
|
||||
self.schedule_update_ha_state()
|
||||
attr[ATTR_LOCATION] = self.location
|
||||
|
||||
return attr
|
||||
|
|
|
@ -260,23 +260,6 @@ eight_sleep:
|
|||
description: Duration to heat at the target level in seconds.
|
||||
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_authenticate:
|
||||
description: Start AirPlay device authentication.
|
||||
|
|
|
@ -162,7 +162,7 @@ async-upnp-client==0.12.7
|
|||
# avion==0.7
|
||||
|
||||
# homeassistant.components.axis
|
||||
axis==14
|
||||
axis==16
|
||||
|
||||
# homeassistant.components.tts.baidu
|
||||
baidu-aip==1.6.6
|
||||
|
|
Loading…
Reference in New Issue