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
Introduced Ring binary sensors and refactored Ring component #6520
Changes from 14 commits
48977f3
9e01fb0
10fe2b0
f9f4a62
944bc5e
b3ebf42
85457d6
1970017
42b123b
c5baa36
b9cddc2
7b776c5
f4c1707
e381158
e61015a
47c2ff1
7877450
2c02030
fcf0725
2694aef
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 |
---|---|---|
@@ -0,0 +1,107 @@ | ||
""" | ||
This component provides HA sensor support for Ring Door Bell/Chimes. | ||
|
||
For more details about this platform, please refer to the documentation at | ||
https://home-assistant.io/components/binary_sensor.ring/ | ||
""" | ||
import logging | ||
import voluptuous as vol | ||
import homeassistant.helpers.config_validation as cv | ||
|
||
from homeassistant.components.ring import ( | ||
CONF_ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE, DEFAULT_CACHEDB) | ||
|
||
from homeassistant.loader import get_component | ||
from homeassistant.components.binary_sensor import ( | ||
BinarySensorDevice, PLATFORM_SCHEMA) | ||
from homeassistant.const import ( | ||
CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS, | ||
ATTR_ATTRIBUTION) | ||
|
||
DEPENDENCIES = ['ring'] | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
# Sensor types: Name, category, device_class | ||
SENSOR_TYPES = { | ||
'ding': ['Ding', ['doorbell'], 'occupancy'], | ||
'motion': ['Motion', ['doorbell'], 'motion'], | ||
} | ||
|
||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ | ||
vol.Optional(CONF_ENTITY_NAMESPACE, default=DEFAULT_ENTITY_NAMESPACE): | ||
cv.string, | ||
vol.Required(CONF_MONITORED_CONDITIONS, default=[]): | ||
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), | ||
}) | ||
|
||
|
||
def setup_platform(hass, config, add_devices, discovery_info=None): | ||
"""Set up a sensor for a Ring device.""" | ||
ring = get_component('ring') | ||
|
||
sensors = [] | ||
for sensor_type in config.get(CONF_MONITORED_CONDITIONS): | ||
for device in ring.RING.data.doorbells: | ||
if 'doorbell' in SENSOR_TYPES[sensor_type][1]: | ||
sensors.append(RingBinarySensor(hass, | ||
device, | ||
sensor_type)) | ||
add_devices(sensors, True) | ||
return True | ||
|
||
|
||
class RingBinarySensor(BinarySensorDevice): | ||
"""A binary sensor implementation for Ring device.""" | ||
|
||
def __init__(self, hass, data, sensor_type): | ||
"""Initialize a sensor for Ring device.""" | ||
super(RingBinarySensor, self).__init__() | ||
self._cache = hass.config.path(DEFAULT_CACHEDB) | ||
self._sensor_type = sensor_type | ||
self._data = data | ||
self._name = "{0} {1}".format(self._data.name, | ||
SENSOR_TYPES.get(self._sensor_type)[0]) | ||
self._device_class = SENSOR_TYPES.get(self._sensor_type)[2] | ||
self._state = None | ||
|
||
@property | ||
def name(self): | ||
"""Return the name of the sensor.""" | ||
return self._name | ||
|
||
@property | ||
def is_on(self): | ||
"""Return True if the binary sensor is on.""" | ||
return self._state | ||
|
||
@property | ||
def device_class(self): | ||
"""Return the class of the binary sensor.""" | ||
return self._device_class | ||
|
||
@property | ||
def device_state_attributes(self): | ||
"""Return the state attributes.""" | ||
attrs = {} | ||
attrs[ATTR_ATTRIBUTION] = CONF_ATTRIBUTION | ||
|
||
attrs['device_id'] = self._data.id | ||
attrs['firmware'] = self._data.firmware | ||
attrs['timezone'] = self._data.timezone | ||
|
||
if self._data.alert and self._data.alert_expires_at: | ||
attrs['expires_at'] = self._data.alert_expires_at | ||
attrs['state'] = self._data.alert.get('state') | ||
|
||
return attrs | ||
|
||
def update(self): | ||
"""Get the latest data and updates the state.""" | ||
self._data.check_alerts(cache=self._cache) | ||
|
||
if self._data.alert: | ||
self._state = bool(self._sensor_type == | ||
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. The |
||
self._data.alert.get('kind')) | ||
else: | ||
self._state = False |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
""" | ||
Support for Ring Doorbell/Chimes. | ||
|
||
For more details about this platform, please refer to the documentation at | ||
https://home-assistant.io/components/ring/ | ||
""" | ||
from datetime import timedelta | ||
import logging | ||
import voluptuous as vol | ||
import homeassistant.helpers.config_validation as cv | ||
|
||
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD | ||
import homeassistant.loader as loader | ||
|
||
from requests.exceptions import HTTPError, ConnectTimeout | ||
|
||
|
||
REQUIREMENTS = ['ring_doorbell==0.1.2'] | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
CONF_ATTRIBUTION = "Data provided by Ring.com" | ||
|
||
NOTIFICATION_ID = 'ring_notification' | ||
NOTIFICATION_TITLE = 'Ring Sensor Setup' | ||
|
||
DOMAIN = 'ring' | ||
DEFAULT_CACHEDB = 'ring_cache.pickle' | ||
DEFAULT_ENTITY_NAMESPACE = 'ring' | ||
DEFAULT_SCAN_INTERVAL = timedelta(seconds=30) | ||
SCAN_INTERVAL = timedelta(seconds=5) | ||
|
||
RING = None | ||
|
||
CONFIG_SCHEMA = vol.Schema({ | ||
DOMAIN: vol.Schema({ | ||
vol.Required(CONF_USERNAME): cv.string, | ||
vol.Required(CONF_PASSWORD): cv.string, | ||
}), | ||
}, extra=vol.ALLOW_EXTRA) | ||
|
||
|
||
def setup(hass, config): | ||
"""Set up Ring component.""" | ||
global RING | ||
conf = config[DOMAIN] | ||
username = conf.get(CONF_USERNAME) | ||
password = conf.get(CONF_PASSWORD) | ||
|
||
persistent_notification = loader.get_component('persistent_notification') | ||
try: | ||
from ring_doorbell import Ring | ||
|
||
ring = Ring(username, password) | ||
if ring.is_connected: | ||
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. You want to return |
||
RING = RingData(ring) | ||
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. Use 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 see. Thanks for the heads. |
||
except (ConnectTimeout, HTTPError) as ex: | ||
_LOGGER.error("Unable to connect to Ring service: %s", str(ex)) | ||
persistent_notification.create( | ||
hass, 'Error: {}<br />' | ||
'You will need to restart hass after fixing.' | ||
''.format(ex), | ||
title=NOTIFICATION_TITLE, | ||
notification_id=NOTIFICATION_ID) | ||
return False | ||
return True | ||
|
||
|
||
class RingData(object): | ||
"""Stores the data retrived for Ring device.""" | ||
|
||
def __init__(self, data): | ||
"""Initialize the data object.""" | ||
self.data = data |
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.
Why do you need to pass in the cache? That is something that should be handled by the shared data class.
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.
The cache file is required for multiple sensors. It basically will save a pickle data file with the notification state shared by different sensors. The cache if required because it is shared on the object level and not on the account level which is shared by hass.data.