Add support for multiple Doorbird stations (#13994)

This commit is contained in:
Matt Snyder 2018-06-10 12:02:44 -05:00 committed by Sebastian Muszynski
parent 1da30032a0
commit 1c561eaf0d
3 changed files with 157 additions and 63 deletions

View File

@ -17,9 +17,9 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
DEPENDENCIES = ['doorbird'] DEPENDENCIES = ['doorbird']
_CAMERA_LAST_VISITOR = "DoorBird Last Ring" _CAMERA_LAST_VISITOR = "{} Last Ring"
_CAMERA_LAST_MOTION = "DoorBird Last Motion" _CAMERA_LAST_MOTION = "{} Last Motion"
_CAMERA_LIVE = "DoorBird Live" _CAMERA_LIVE = "{} Live"
_LAST_VISITOR_INTERVAL = datetime.timedelta(minutes=1) _LAST_VISITOR_INTERVAL = datetime.timedelta(minutes=1)
_LAST_MOTION_INTERVAL = datetime.timedelta(minutes=1) _LAST_MOTION_INTERVAL = datetime.timedelta(minutes=1)
_LIVE_INTERVAL = datetime.timedelta(seconds=1) _LIVE_INTERVAL = datetime.timedelta(seconds=1)
@ -30,16 +30,22 @@ _TIMEOUT = 10 # seconds
@asyncio.coroutine @asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None): def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the DoorBird camera platform.""" """Set up the DoorBird camera platform."""
device = hass.data.get(DOORBIRD_DOMAIN) for doorstation in hass.data[DOORBIRD_DOMAIN]:
async_add_devices([ device = doorstation.device
DoorBirdCamera(device.live_image_url, _CAMERA_LIVE, _LIVE_INTERVAL), async_add_devices([
DoorBirdCamera( DoorBirdCamera(
device.history_image_url(1, 'doorbell'), _CAMERA_LAST_VISITOR, device.live_image_url,
_LAST_VISITOR_INTERVAL), _CAMERA_LIVE.format(doorstation.name),
DoorBirdCamera( _LIVE_INTERVAL),
device.history_image_url(1, 'motionsensor'), _CAMERA_LAST_MOTION, DoorBirdCamera(
_LAST_MOTION_INTERVAL), device.history_image_url(1, 'doorbell'),
]) _CAMERA_LAST_VISITOR.format(doorstation.name),
_LAST_VISITOR_INTERVAL),
DoorBirdCamera(
device.history_image_url(1, 'motionsensor'),
_CAMERA_LAST_MOTION.format(doorstation.name),
_LAST_MOTION_INTERVAL),
])
class DoorBirdCamera(Camera): class DoorBirdCamera(Camera):

View File

@ -4,14 +4,16 @@ Support for DoorBird device.
For more details about this component, please refer to the documentation at For more details about this component, please refer to the documentation at
https://home-assistant.io/components/doorbird/ https://home-assistant.io/components/doorbird/
""" """
import asyncio
import logging import logging
import asyncio
import voluptuous as vol import voluptuous as vol
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.http import HomeAssistantView from homeassistant.components.http import HomeAssistantView
from homeassistant.const import CONF_HOST, CONF_USERNAME, \
CONF_PASSWORD, CONF_NAME, CONF_DEVICES, CONF_MONITORED_CONDITIONS
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.util import slugify
REQUIREMENTS = ['DoorBirdPy==0.1.3'] REQUIREMENTS = ['DoorBirdPy==0.1.3']
@ -24,60 +26,139 @@ API_URL = '/api/{}'.format(DOMAIN)
CONF_DOORBELL_EVENTS = 'doorbell_events' CONF_DOORBELL_EVENTS = 'doorbell_events'
CONF_CUSTOM_URL = 'hass_url_override' CONF_CUSTOM_URL = 'hass_url_override'
DOORBELL_EVENT = 'doorbell'
MOTION_EVENT = 'motionsensor'
# Sensor types: Name, device_class, event
SENSOR_TYPES = {
'doorbell': ['Button', 'occupancy', DOORBELL_EVENT],
'motion': ['Motion', 'motion', MOTION_EVENT],
}
DEVICE_SCHEMA = vol.Schema({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_CUSTOM_URL): cv.string,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_MONITORED_CONDITIONS, default=[]):
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
})
CONFIG_SCHEMA = vol.Schema({ CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({ DOMAIN: vol.Schema({
vol.Required(CONF_HOST): cv.string, vol.Required(CONF_DEVICES): vol.All(cv.ensure_list, [DEVICE_SCHEMA])
vol.Required(CONF_USERNAME): cv.string, }),
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_DOORBELL_EVENTS): cv.boolean,
vol.Optional(CONF_CUSTOM_URL): cv.string,
})
}, extra=vol.ALLOW_EXTRA) }, extra=vol.ALLOW_EXTRA)
SENSOR_DOORBELL = 'doorbell'
def setup(hass, config): def setup(hass, config):
"""Set up the DoorBird component.""" """Set up the DoorBird component."""
from doorbirdpy import DoorBird from doorbirdpy import DoorBird
device_ip = config[DOMAIN].get(CONF_HOST) # Provide an endpoint for the doorstations to call to trigger events
username = config[DOMAIN].get(CONF_USERNAME) hass.http.register_view(DoorbirdRequestView())
password = config[DOMAIN].get(CONF_PASSWORD)
device = DoorBird(device_ip, username, password) doorstations = []
status = device.ready()
if status[0]: for index, doorstation_config in enumerate(config[DOMAIN][CONF_DEVICES]):
_LOGGER.info("Connected to DoorBird at %s as %s", device_ip, username) device_ip = doorstation_config.get(CONF_HOST)
hass.data[DOMAIN] = device username = doorstation_config.get(CONF_USERNAME)
elif status[1] == 401: password = doorstation_config.get(CONF_PASSWORD)
_LOGGER.error("Authorization rejected by DoorBird at %s", device_ip) custom_url = doorstation_config.get(CONF_CUSTOM_URL)
return False events = doorstation_config.get(CONF_MONITORED_CONDITIONS)
else: name = (doorstation_config.get(CONF_NAME)
_LOGGER.error("Could not connect to DoorBird at %s: Error %s", or 'DoorBird {}'.format(index + 1))
device_ip, str(status[1]))
return False
if config[DOMAIN].get(CONF_DOORBELL_EVENTS): device = DoorBird(device_ip, username, password)
# Provide an endpoint for the device to call to trigger events status = device.ready()
hass.http.register_view(DoorbirdRequestView())
if status[0]:
_LOGGER.info("Connected to DoorBird at %s as %s", device_ip,
username)
doorstation = ConfiguredDoorbird(device, name, events, custom_url)
doorstations.append(doorstation)
elif status[1] == 401:
_LOGGER.error("Authorization rejected by DoorBird at %s",
device_ip)
return False
else:
_LOGGER.error("Could not connect to DoorBird at %s: Error %s",
device_ip, str(status[1]))
return False
# SETUP EVENT SUBSCRIBERS
if events is not None:
# This will make HA the only service that receives events.
doorstation.device.reset_notifications()
# Subscribe to doorbell or motion events
subscribe_events(hass, doorstation)
hass.data[DOMAIN] = doorstations
return True
def subscribe_events(hass, doorstation):
"""Initialize the subscriber."""
for sensor_type in doorstation.monitored_events:
name = '{} {}'.format(doorstation.name,
SENSOR_TYPES[sensor_type][0])
event_type = SENSOR_TYPES[sensor_type][2]
# Get the URL of this server # Get the URL of this server
hass_url = hass.config.api.base_url hass_url = hass.config.api.base_url
# Override it if another is specified in the component configuration # Override url if another is specified onth configuration
if config[DOMAIN].get(CONF_CUSTOM_URL): if doorstation.custom_url is not None:
hass_url = config[DOMAIN].get(CONF_CUSTOM_URL) hass_url = doorstation.custom_url
_LOGGER.info("DoorBird will connect to this instance via %s",
hass_url)
# This will make HA the only service that gets doorbell events slug = slugify(name)
url = '{}{}/{}'.format(hass_url, API_URL, SENSOR_DOORBELL)
device.reset_notifications()
device.subscribe_notification(SENSOR_DOORBELL, url)
return True url = '{}{}/{}'.format(hass_url, API_URL, slug)
_LOGGER.info("DoorBird will connect to this instance via %s",
url)
_LOGGER.info("You may use the following event name for automations"
": %s_%s", DOMAIN, slug)
doorstation.device.subscribe_notification(event_type, url)
class ConfiguredDoorbird():
"""Attach additional information to pass along with configured device."""
def __init__(self, device, name, events=None, custom_url=None):
"""Initialize configured device."""
self._name = name
self._device = device
self._custom_url = custom_url
self._monitored_events = events
@property
def name(self):
"""Custom device name."""
return self._name
@property
def device(self):
"""The configured device."""
return self._device
@property
def custom_url(self):
"""Custom url for device."""
return self._custom_url
@property
def monitored_events(self):
"""Get monitored events."""
if self._monitored_events is None:
return []
return self._monitored_events
class DoorbirdRequestView(HomeAssistantView): class DoorbirdRequestView(HomeAssistantView):
@ -93,5 +174,7 @@ class DoorbirdRequestView(HomeAssistantView):
def get(self, request, sensor): def get(self, request, sensor):
"""Respond to requests from the device.""" """Respond to requests from the device."""
hass = request.app['hass'] hass = request.app['hass']
hass.bus.async_fire('{}_{}'.format(DOMAIN, sensor)) hass.bus.async_fire('{}_{}'.format(DOMAIN, sensor))
return 'OK' return 'OK'

View File

@ -4,10 +4,10 @@ import logging
import voluptuous as vol import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.doorbird import DOMAIN as DOORBIRD_DOMAIN from homeassistant.components.doorbird import DOMAIN as DOORBIRD_DOMAIN
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice
from homeassistant.const import CONF_SWITCHES from homeassistant.const import CONF_SWITCHES
import homeassistant.helpers.config_validation as cv
DEPENDENCIES = ['doorbird'] DEPENDENCIES = ['doorbird']
@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__)
SWITCHES = { SWITCHES = {
"open_door": { "open_door": {
"name": "Open Door", "name": "{} Open Door",
"icon": { "icon": {
True: "lock-open", True: "lock-open",
False: "lock" False: "lock"
@ -23,7 +23,7 @@ SWITCHES = {
"time": datetime.timedelta(seconds=3) "time": datetime.timedelta(seconds=3)
}, },
"open_door_2": { "open_door_2": {
"name": "Open Door 2", "name": "{} Open Door 2",
"icon": { "icon": {
True: "lock-open", True: "lock-open",
False: "lock" False: "lock"
@ -31,7 +31,7 @@ SWITCHES = {
"time": datetime.timedelta(seconds=3) "time": datetime.timedelta(seconds=3)
}, },
"light_on": { "light_on": {
"name": "Light On", "name": "{} Light On",
"icon": { "icon": {
True: "lightbulb-on", True: "lightbulb-on",
False: "lightbulb" False: "lightbulb"
@ -48,31 +48,36 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the DoorBird switch platform.""" """Set up the DoorBird switch platform."""
device = hass.data.get(DOORBIRD_DOMAIN)
switches = [] switches = []
for switch in SWITCHES:
_LOGGER.debug("Adding DoorBird switch %s", SWITCHES[switch]["name"]) for doorstation in hass.data[DOORBIRD_DOMAIN]:
switches.append(DoorBirdSwitch(device, switch))
device = doorstation.device
for switch in SWITCHES:
_LOGGER.debug("Adding DoorBird switch %s",
SWITCHES[switch]["name"].format(doorstation.name))
switches.append(DoorBirdSwitch(device, switch, doorstation.name))
add_devices(switches) add_devices(switches)
_LOGGER.info("Added DoorBird switches")
class DoorBirdSwitch(SwitchDevice): class DoorBirdSwitch(SwitchDevice):
"""A relay in a DoorBird device.""" """A relay in a DoorBird device."""
def __init__(self, device, switch): def __init__(self, device, switch, name):
"""Initialize a relay in a DoorBird device.""" """Initialize a relay in a DoorBird device."""
self._device = device self._device = device
self._switch = switch self._switch = switch
self._name = name
self._state = False self._state = False
self._assume_off = datetime.datetime.min self._assume_off = datetime.datetime.min
@property @property
def name(self): def name(self):
"""Return the name of the switch.""" """Return the name of the switch."""
return SWITCHES[self._switch]["name"] return SWITCHES[self._switch]["name"].format(self._name)
@property @property
def icon(self): def icon(self):