Axis - prepare for config entry (#17566)

Make component more in line with other more modern components in preparation for config entry support.
This commit is contained in:
Robert Svensson 2018-10-29 06:52:30 +01:00 committed by GitHub
parent 3802fec568
commit 4579717317
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 106 additions and 118 deletions

View File

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

View File

@ -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'

View File

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

View File

@ -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.

View File

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