mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 16:57:10 +00:00
Add support for multiple Doorbird stations (#13994)
This commit is contained in:
parent
1da30032a0
commit
1c561eaf0d
@ -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):
|
||||||
|
@ -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'
|
||||||
|
@ -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):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user