-
-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Add support for multiple Doorbird stations #13994
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
Changes from 2 commits
3a85104
2d9fb0a
332e6d1
c3907b0
21c5c38
0dfade4
a37e86e
98e7339
645bfac
ccac2c4
8fa3267
783b268
1780f30
08ec5e1
9aa0e05
e55556f
e485192
8966636
3e29719
1b19410
1ef8fff
334a3f0
d0e511e
26a93bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
|
|
@@ -30,16 +30,17 @@ | |
| @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.get(DOORBIRD_DOMAIN): | ||
| device = doorstation.device | ||
| async_add_devices([ | ||
| DoorBirdCamera(device.live_image_url, _CAMERA_LIVE.format(doorstation.name), _LIVE_INTERVAL), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (105 > 79 characters) |
||
| DoorBirdCamera( | ||
| device.history_image_url(1, 'doorbell'), _CAMERA_LAST_VISITOR.format(doorstation.name), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (103 > 79 characters) |
||
| _LAST_VISITOR_INTERVAL), | ||
| DoorBirdCamera( | ||
| device.history_image_url(1, 'motionsensor'), _CAMERA_LAST_MOTION.format(doorstation.name), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (106 > 79 characters) |
||
| _LAST_MOTION_INTERVAL), | ||
| ]) | ||
|
|
||
|
|
||
| class DoorBirdCamera(Camera): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,8 @@ | |
|
|
||
| import voluptuous as vol | ||
|
|
||
| from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD | ||
| from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD, \ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'homeassistant.const.CONF_DEVICES' imported but unused |
||
| CONF_DEVICES, CONF_NAME | ||
| from homeassistant.components.http import HomeAssistantView | ||
| import homeassistant.helpers.config_validation as cv | ||
|
|
||
|
|
@@ -24,14 +25,20 @@ | |
| CONF_DOORBELL_EVENTS = 'doorbell_events' | ||
| CONF_CUSTOM_URL = 'hass_url_override' | ||
|
|
||
| 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_DOORBELL_EVENTS): cv.boolean, | ||
| vol.Optional(CONF_CUSTOM_URL): cv.string, | ||
| vol.Optional(CONF_NAME): cv.string | ||
| }) | ||
|
|
||
| CONFIG_SCHEMA = vol.Schema({ | ||
| DOMAIN: vol.Schema({ | ||
| vol.Required(CONF_HOST): cv.string, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like to suggest this schema: In this case the schema can be extended in future with parameters applied to all gateways without another beaking change of the configuration. cp. #13517
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This schema is better (extensible) |
||
| 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, | ||
| }) | ||
| DOMAIN: vol.All( | ||
| cv.ensure_list, | ||
| [DEVICE_SCHEMA] | ||
| ), | ||
| }, extra=vol.ALLOW_EXTRA) | ||
|
|
||
| SENSOR_DOORBELL = 'doorbell' | ||
|
|
@@ -41,57 +48,77 @@ 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) | ||
|
|
||
| device = DoorBird(device_ip, username, password) | ||
| status = device.ready() | ||
|
|
||
| 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()) | ||
|
|
||
| # 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) | ||
| doorstations = [] | ||
|
|
||
| for index, doorstation_config in enumerate(config[DOMAIN]): | ||
| device_ip = doorstation_config.get(CONF_HOST) | ||
| username = doorstation_config.get(CONF_USERNAME) | ||
| password = doorstation_config.get(CONF_PASSWORD) | ||
| name = doorstation_config.get(CONF_NAME) or 'DoorBird {}'.format(index + 1) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (83 > 79 characters) |
||
|
|
||
| device = DoorBird(device_ip, username, password) | ||
| status = device.ready() | ||
|
|
||
| if status[0]: | ||
| _LOGGER.info("Connected to DoorBird at %s as %s", device_ip, username) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (82 > 79 characters) |
||
| doorstations.append(ConfiguredDoorbird(device, name)) | ||
| elif status[1] == 401: | ||
| _LOGGER.error("Authorization rejected by DoorBird at %s", device_ip) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (80 > 79 characters) |
||
| return False | ||
| else: | ||
| _LOGGER.error("Could not connect to DoorBird at %s: Error %s", | ||
| device_ip, str(status[1])) | ||
| return False | ||
|
|
||
| if doorstation_config.get(CONF_DOORBELL_EVENTS): | ||
| # Provide an endpoint for the device to call to trigger events | ||
| hass.http.register_view(DoorbirdRequestView()) | ||
|
|
||
| # Get the URL of this server | ||
| hass_url = hass.config.api.base_url | ||
|
|
||
| # Override it if another is specified in the component configuration | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (80 > 79 characters) |
||
| if doorstation_config.get(CONF_CUSTOM_URL): | ||
| hass_url = doorstation_config.get(CONF_CUSTOM_URL) | ||
|
|
||
| # This will make HA the only service that gets doorbell events | ||
| url = '{}{}/{}/{}'.format(hass_url, API_URL, index + 1, SENSOR_DOORBELL) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (84 > 79 characters) |
||
|
|
||
| _LOGGER.info("DoorBird will connect to this instance via %s", | ||
| hass_url) | ||
| 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) | ||
| device.reset_notifications() | ||
| device.subscribe_notification(SENSOR_DOORBELL, url) | ||
|
|
||
| hass.data[DOMAIN] = doorstations | ||
|
|
||
| return True | ||
|
|
||
| class ConfiguredDoorbird(): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
| def __init__(self, device, name): | ||
| self._name = name | ||
| self._device = device | ||
|
|
||
| @property | ||
| def name(self): | ||
| return self._name | ||
|
|
||
| @property | ||
| def device(self): | ||
| return self._device | ||
|
|
||
| class DoorbirdRequestView(HomeAssistantView): | ||
| """Provide a page for the device to call.""" | ||
|
|
||
| requires_auth = False | ||
| url = API_URL | ||
| name = API_URL[1:].replace('/', ':') | ||
| extra_urls = [API_URL + '/{sensor}'] | ||
| extra_urls = [API_URL + '/{index}/{sensor}'] | ||
|
|
||
| # pylint: disable=no-self-use | ||
| @asyncio.coroutine | ||
| def get(self, request, sensor): | ||
| def get(self, request, index, sensor): | ||
| """Respond to requests from the device.""" | ||
| hass = request.app['hass'] | ||
| hass.bus.async_fire('{}_{}'.format(DOMAIN, sensor)) | ||
| hass.bus.async_fire('{}_{}_{}'.format(DOMAIN, index, sensor)) | ||
| return 'OK' | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,23 +15,23 @@ | |
|
|
||
| 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" | ||
|
|
@@ -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) | ||
| for doorstation in hass.data.get(DOORBIRD_DOMAIN): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't use |
||
|
|
||
| switches = [] | ||
| for switch in SWITCHES: | ||
| _LOGGER.debug("Adding DoorBird switch %s", SWITCHES[switch]["name"]) | ||
| switches.append(DoorBirdSwitch(device, switch)) | ||
| device = doorstation.device | ||
|
|
||
| add_devices(switches) | ||
| _LOGGER.info("Added DoorBird switches") | ||
| switches = [] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this out of the loop. It's accessed outside the loop. |
||
| 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) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make a single call to |
||
| _LOGGER.info("Added DoorBird switches") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this. There's already a debug log above and the core will log platforms being setup. |
||
|
|
||
|
|
||
| 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): | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use
dict.get.