Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for multiple Doorbird stations #13994

Merged
merged 24 commits into from
Jun 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 19 additions & 13 deletions homeassistant/components/camera/doorbird.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@

DEPENDENCIES = ['doorbird']

_CAMERA_LAST_VISITOR = "DoorBird Last Ring"
_CAMERA_LAST_MOTION = "DoorBird Last Motion"
_CAMERA_LIVE = "DoorBird Live"
_CAMERA_LAST_VISITOR = "{} Last Ring"
_CAMERA_LAST_MOTION = "{} Last Motion"
_CAMERA_LIVE = "{} Live"
_LAST_VISITOR_INTERVAL = datetime.timedelta(minutes=1)
_LAST_MOTION_INTERVAL = datetime.timedelta(minutes=1)
_LIVE_INTERVAL = datetime.timedelta(seconds=1)
Expand All @@ -30,16 +30,22 @@
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the DoorBird camera platform."""
device = hass.data.get(DOORBIRD_DOMAIN)
async_add_devices([
DoorBirdCamera(device.live_image_url, _CAMERA_LIVE, _LIVE_INTERVAL),
DoorBirdCamera(
device.history_image_url(1, 'doorbell'), _CAMERA_LAST_VISITOR,
_LAST_VISITOR_INTERVAL),
DoorBirdCamera(
device.history_image_url(1, 'motionsensor'), _CAMERA_LAST_MOTION,
_LAST_MOTION_INTERVAL),
])
for doorstation in hass.data[DOORBIRD_DOMAIN]:
device = doorstation.device
async_add_devices([
DoorBirdCamera(
device.live_image_url,
_CAMERA_LIVE.format(doorstation.name),
_LIVE_INTERVAL),
DoorBirdCamera(
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):
Expand Down
159 changes: 121 additions & 38 deletions homeassistant/components/doorbird.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/doorbird/
"""
import asyncio
import logging

import asyncio
import voluptuous as vol

from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
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
from homeassistant.util import slugify

REQUIREMENTS = ['DoorBirdPy==0.1.3']

Expand All @@ -24,60 +26,139 @@
CONF_DOORBELL_EVENTS = 'doorbell_events'
CONF_CUSTOM_URL = 'hass_url_override'

DOORBELL_EVENT = 'doorbell'
MOTION_EVENT = 'motionsensor'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you call the event motion? I will approved and merge the PR afterwards.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the event name required by the device API for event subscriptions.

image

https://www.doorbird.com/downloads/api_lan.pdf?rev=0.20

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Sounds a bit weird.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oblogic7 That's only for monitor.cgi. The new schedule.cgi uses 'motion'. No need to update this PR, I'm fixing it in mine 🙂

image


# 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({
DOMAIN: vol.Schema({
vol.Required(CONF_HOST): cv.string,
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,
})
vol.Required(CONF_DEVICES): vol.All(cv.ensure_list, [DEVICE_SCHEMA])
}),
}, extra=vol.ALLOW_EXTRA)

SENSOR_DOORBELL = 'doorbell'


def setup(hass, config):
"""Set up the DoorBird component."""
from doorbirdpy import DoorBird

device_ip = config[DOMAIN].get(CONF_HOST)
username = config[DOMAIN].get(CONF_USERNAME)
password = config[DOMAIN].get(CONF_PASSWORD)
# Provide an endpoint for the doorstations to call to trigger events
hass.http.register_view(DoorbirdRequestView())

doorstations = []

for index, doorstation_config in enumerate(config[DOMAIN][CONF_DEVICES]):
device_ip = doorstation_config.get(CONF_HOST)
username = doorstation_config.get(CONF_USERNAME)
password = doorstation_config.get(CONF_PASSWORD)
custom_url = doorstation_config.get(CONF_CUSTOM_URL)
events = doorstation_config.get(CONF_MONITORED_CONDITIONS)
name = (doorstation_config.get(CONF_NAME)
or 'DoorBird {}'.format(index + 1))

device = DoorBird(device_ip, username, password)
status = device.ready()

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

device = DoorBird(device_ip, username, password)
status = device.ready()
return True

if status[0]:
_LOGGER.info("Connected to DoorBird at %s as %s", device_ip, username)
hass.data[DOMAIN] = device
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

if config[DOMAIN].get(CONF_DOORBELL_EVENTS):
# Provide an endpoint for the device to call to trigger events
hass.http.register_view(DoorbirdRequestView())
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
hass_url = hass.config.api.base_url

# Override it if another is specified in the component configuration
if config[DOMAIN].get(CONF_CUSTOM_URL):
hass_url = config[DOMAIN].get(CONF_CUSTOM_URL)
_LOGGER.info("DoorBird will connect to this instance via %s",
hass_url)
# Override url if another is specified onth configuration
if doorstation.custom_url is not None:
hass_url = doorstation.custom_url

# This will make HA the only service that gets doorbell events
url = '{}{}/{}'.format(hass_url, API_URL, SENSOR_DOORBELL)
device.reset_notifications()
device.subscribe_notification(SENSOR_DOORBELL, url)
slug = slugify(name)

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)

return True

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):
Expand All @@ -93,5 +174,7 @@ class DoorbirdRequestView(HomeAssistantView):
def get(self, request, sensor):
"""Respond to requests from the device."""
hass = request.app['hass']

hass.bus.async_fire('{}_{}'.format(DOMAIN, sensor))

return 'OK'
29 changes: 17 additions & 12 deletions homeassistant/components/switch/doorbird.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,34 @@

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.components.doorbird import DOMAIN as DOORBIRD_DOMAIN
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice
from homeassistant.const import CONF_SWITCHES
import homeassistant.helpers.config_validation as cv

DEPENDENCIES = ['doorbird']

_LOGGER = logging.getLogger(__name__)

SWITCHES = {
"open_door": {
"name": "Open Door",
"name": "{} Open Door",
"icon": {
True: "lock-open",
False: "lock"
},
"time": datetime.timedelta(seconds=3)
},
"open_door_2": {
"name": "Open Door 2",
"name": "{} Open Door 2",
"icon": {
True: "lock-open",
False: "lock"
},
"time": datetime.timedelta(seconds=3)
},
"light_on": {
"name": "Light On",
"name": "{} Light On",
"icon": {
True: "lightbulb-on",
False: "lightbulb"
Expand All @@ -48,31 +48,36 @@

def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the DoorBird switch platform."""
device = hass.data.get(DOORBIRD_DOMAIN)

switches = []
for switch in SWITCHES:
_LOGGER.debug("Adding DoorBird switch %s", SWITCHES[switch]["name"])
switches.append(DoorBirdSwitch(device, switch))

for doorstation in hass.data[DOORBIRD_DOMAIN]:

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)
_LOGGER.info("Added DoorBird switches")


class DoorBirdSwitch(SwitchDevice):
"""A relay in a DoorBird device."""

def __init__(self, device, switch):
def __init__(self, device, switch, name):
"""Initialize a relay in a DoorBird device."""
self._device = device
self._switch = switch
self._name = name
self._state = False
self._assume_off = datetime.datetime.min

@property
def name(self):
"""Return the name of the switch."""
return SWITCHES[self._switch]["name"]
return SWITCHES[self._switch]["name"].format(self._name)

@property
def icon(self):
Expand Down