mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Axis component reflect device availability (#22401)
This commit is contained in:
parent
f4625fd561
commit
5f6037d563
@ -12,7 +12,7 @@ from .config_flow import configured_devices, DEVICE_SCHEMA
|
||||
from .const import CONF_CAMERA, CONF_EVENTS, DEFAULT_TRIGGER_TIME, DOMAIN
|
||||
from .device import AxisNetworkDevice, get_device
|
||||
|
||||
REQUIREMENTS = ['axis==17']
|
||||
REQUIREMENTS = ['axis==19']
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: cv.schema_with_slug_keys(DEVICE_SCHEMA),
|
||||
|
@ -35,23 +35,29 @@ class AxisBinarySensor(BinarySensorDevice):
|
||||
"""Initialize the Axis binary sensor."""
|
||||
self.event = event
|
||||
self.device = device
|
||||
self.delay = device.config_entry.options[CONF_TRIGGER_TIME]
|
||||
self.remove_timer = None
|
||||
self.unsub_dispatcher = None
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Subscribe sensors events."""
|
||||
self.event.register_callback(self.update_callback)
|
||||
self.unsub_dispatcher = async_dispatcher_connect(
|
||||
self.hass, self.device.event_reachable, self.update_callback)
|
||||
|
||||
def update_callback(self):
|
||||
"""Update the sensor's state, if needed."""
|
||||
@callback
|
||||
def update_callback(self, no_delay=False):
|
||||
"""Update the sensor's state, if needed.
|
||||
|
||||
Parameter no_delay is True when device_event_reachable is sent.
|
||||
"""
|
||||
delay = self.device.config_entry.options[CONF_TRIGGER_TIME]
|
||||
|
||||
if self.remove_timer is not None:
|
||||
self.remove_timer()
|
||||
self.remove_timer = None
|
||||
|
||||
if delay == 0 or self.is_on:
|
||||
self.schedule_update_ha_state()
|
||||
if self.is_on or delay == 0 or no_delay:
|
||||
self.async_schedule_update_ha_state()
|
||||
return
|
||||
|
||||
@callback
|
||||
@ -87,6 +93,10 @@ class AxisBinarySensor(BinarySensorDevice):
|
||||
return '{}-{}-{}'.format(
|
||||
self.device.serial, self.event.topic, self.event.id)
|
||||
|
||||
def available(self):
|
||||
"""Return True if device is available."""
|
||||
return self.device.available
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""No polling needed."""
|
||||
|
@ -5,6 +5,7 @@ from homeassistant.components.mjpeg.camera import (
|
||||
from homeassistant.const import (
|
||||
CONF_AUTHENTICATION, CONF_DEVICE, CONF_HOST, CONF_MAC, CONF_NAME,
|
||||
CONF_PASSWORD, CONF_PORT, CONF_USERNAME, HTTP_DIGEST_AUTHENTICATION)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .const import DOMAIN as AXIS_DOMAIN
|
||||
@ -46,12 +47,25 @@ class AxisCamera(MjpegCamera):
|
||||
self.device_config = config
|
||||
self.device = device
|
||||
self.port = device.config_entry.data[CONF_DEVICE][CONF_PORT]
|
||||
self.unsub_dispatcher = None
|
||||
self.unsub_dispatcher = []
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Subscribe camera events."""
|
||||
self.unsub_dispatcher = async_dispatcher_connect(
|
||||
self.hass, 'axis_{}_new_ip'.format(self.device.name), self._new_ip)
|
||||
self.unsub_dispatcher.append(async_dispatcher_connect(
|
||||
self.hass, 'axis_{}_new_ip'.format(self.device.name),
|
||||
self._new_ip))
|
||||
self.unsub_dispatcher.append(async_dispatcher_connect(
|
||||
self.hass, self.device.event_reachable, self.update_callback))
|
||||
|
||||
@callback
|
||||
def update_callback(self, no_delay=None):
|
||||
"""Update the cameras state."""
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if device is available."""
|
||||
return self.device.available
|
||||
|
||||
def _new_ip(self, host):
|
||||
"""Set new IP for video stream."""
|
||||
|
@ -12,6 +12,7 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
|
||||
from .const import CONF_CAMERA, CONF_EVENTS, CONF_MODEL, DOMAIN, LOGGER
|
||||
|
||||
from .errors import AuthenticationRequired, CannotConnect
|
||||
|
||||
|
||||
@ -72,8 +73,7 @@ class AxisNetworkDevice:
|
||||
|
||||
try:
|
||||
self.api = await get_device(
|
||||
hass, self.config_entry.data[CONF_DEVICE],
|
||||
event_types='on', signal_callback=self.async_signal_callback)
|
||||
hass, self.config_entry.data[CONF_DEVICE])
|
||||
|
||||
except CannotConnect:
|
||||
raise ConfigEntryNotReady
|
||||
@ -95,17 +95,38 @@ class AxisNetworkDevice:
|
||||
self.hass.async_create_task(
|
||||
self.hass.config_entries.async_forward_entry_setup(
|
||||
self.config_entry, 'binary_sensor'))
|
||||
|
||||
self.api.stream.connection_status_callback = \
|
||||
self.async_connection_status_callback
|
||||
self.api.enable_events(event_callback=self.async_event_callback)
|
||||
self.api.start()
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def event_reachable(self):
|
||||
"""Device specific event to signal a change in connection status."""
|
||||
return 'axis_reachable_{}'.format(self.serial)
|
||||
|
||||
@callback
|
||||
def async_connection_status_callback(self, status):
|
||||
"""Handle signals of gateway connection status.
|
||||
|
||||
This is called on every RTSP keep-alive message.
|
||||
Only signal state change if state change is true.
|
||||
"""
|
||||
from axis.streammanager import SIGNAL_PLAYING
|
||||
if self.available != (status == SIGNAL_PLAYING):
|
||||
self.available = not self.available
|
||||
async_dispatcher_send(self.hass, self.event_reachable, True)
|
||||
|
||||
@property
|
||||
def event_new_sensor(self):
|
||||
"""Device specific event to signal new sensor available."""
|
||||
return 'axis_add_sensor_{}'.format(self.serial)
|
||||
|
||||
@callback
|
||||
def async_signal_callback(self, action, event):
|
||||
def async_event_callback(self, action, event):
|
||||
"""Call to configure events when initialized on event stream."""
|
||||
if action == 'add':
|
||||
async_dispatcher_send(self.hass, self.event_new_sensor, event)
|
||||
@ -116,7 +137,7 @@ class AxisNetworkDevice:
|
||||
self.api.stop()
|
||||
|
||||
|
||||
async def get_device(hass, config, event_types=None, signal_callback=None):
|
||||
async def get_device(hass, config):
|
||||
"""Create a Axis device."""
|
||||
import axis
|
||||
|
||||
@ -124,8 +145,7 @@ async def get_device(hass, config, event_types=None, signal_callback=None):
|
||||
loop=hass.loop, host=config[CONF_HOST],
|
||||
username=config[CONF_USERNAME],
|
||||
password=config[CONF_PASSWORD],
|
||||
port=config[CONF_PORT], web_proto='http',
|
||||
event_types=event_types, signal=signal_callback)
|
||||
port=config[CONF_PORT], web_proto='http')
|
||||
|
||||
try:
|
||||
with async_timeout.timeout(15):
|
||||
|
@ -192,7 +192,7 @@ av==6.1.2
|
||||
# avion==0.10
|
||||
|
||||
# homeassistant.components.axis
|
||||
axis==17
|
||||
axis==19
|
||||
|
||||
# homeassistant.components.modem_callerid.sensor
|
||||
basicmodem==0.7
|
||||
|
@ -60,7 +60,7 @@ apns2==0.3.0
|
||||
av==6.1.2
|
||||
|
||||
# homeassistant.components.axis
|
||||
axis==17
|
||||
axis==19
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows-homeassistant==0.7.1
|
||||
|
@ -53,9 +53,9 @@ async def setup_device(hass):
|
||||
1, axis.DOMAIN, 'Mock Title', ENTRY_CONFIG, 'test',
|
||||
config_entries.CONN_CLASS_LOCAL_PUSH, options=ENTRY_OPTIONS)
|
||||
device = axis.AxisNetworkDevice(hass, config_entry)
|
||||
device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE],
|
||||
signal=device.async_signal_callback)
|
||||
device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE])
|
||||
hass.data[axis.DOMAIN] = {device.serial: device}
|
||||
device.api.enable_events(event_callback=device.async_event_callback)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setup(
|
||||
config_entry, 'binary_sensor')
|
||||
|
@ -37,9 +37,9 @@ async def setup_device(hass):
|
||||
1, axis.DOMAIN, 'Mock Title', ENTRY_CONFIG, 'test',
|
||||
config_entries.CONN_CLASS_LOCAL_PUSH, options=ENTRY_OPTIONS)
|
||||
device = axis.AxisNetworkDevice(hass, config_entry)
|
||||
device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE],
|
||||
signal=device.async_signal_callback)
|
||||
device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE])
|
||||
hass.data[axis.DOMAIN] = {device.serial: device}
|
||||
device.api.enable_events(event_callback=device.async_event_callback)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setup(
|
||||
config_entry, 'camera')
|
||||
|
@ -31,8 +31,7 @@ async def test_flow_works(hass):
|
||||
|
||||
with patch('axis.AxisDevice') as mock_device:
|
||||
def mock_constructor(
|
||||
loop, host, username, password, port, web_proto, event_types,
|
||||
signal):
|
||||
loop, host, username, password, port, web_proto):
|
||||
"""Fake the controller constructor."""
|
||||
mock_device.loop = loop
|
||||
mock_device.host = host
|
||||
@ -189,8 +188,7 @@ async def test_discovery_flow_known_device(hass):
|
||||
config_flow.CONF_PORT: 80}}), \
|
||||
patch('axis.AxisDevice') as mock_device:
|
||||
def mock_constructor(
|
||||
loop, host, username, password, port, web_proto, event_types,
|
||||
signal):
|
||||
loop, host, username, password, port, web_proto):
|
||||
"""Fake the controller constructor."""
|
||||
mock_device.loop = loop
|
||||
mock_device.host = host
|
||||
@ -277,8 +275,7 @@ async def test_import_flow_works(hass):
|
||||
|
||||
with patch('axis.AxisDevice') as mock_device:
|
||||
def mock_constructor(
|
||||
loop, host, username, password, port, web_proto, event_types,
|
||||
signal):
|
||||
loop, host, username, password, port, web_proto):
|
||||
"""Fake the controller constructor."""
|
||||
mock_device.loop = loop
|
||||
mock_device.host = host
|
||||
|
@ -94,7 +94,7 @@ async def test_new_event_sends_signal(hass):
|
||||
axis_device = device.AxisNetworkDevice(hass, entry)
|
||||
|
||||
with patch.object(device, 'async_dispatcher_send') as mock_dispatch_send:
|
||||
axis_device.async_signal_callback(action='add', event='event')
|
||||
axis_device.async_event_callback(action='add', event='event')
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_dispatch_send.mock_calls) == 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user