Skybell (#9681)
* New Skybell platform with components * Added skybell components to omit. * Preemptively fixing lint issues (hopefully). * Removed unused variable. * Requested changes. * Additional CRs * Hopefully the last of the CR's!
This commit is contained in:
parent
ca54bbfcc9
commit
8132989f91
8 changed files with 507 additions and 0 deletions
|
@ -169,6 +169,9 @@ omit =
|
|||
|
||||
homeassistant/components/scsgate.py
|
||||
homeassistant/components/*/scsgate.py
|
||||
|
||||
homeassistant/components/skybell.py
|
||||
homeassistant/components/*/skybell.py
|
||||
|
||||
homeassistant/components/tado.py
|
||||
homeassistant/components/*/tado.py
|
||||
|
|
97
homeassistant/components/binary_sensor/skybell.py
Normal file
97
homeassistant/components/binary_sensor/skybell.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
"""
|
||||
Binary sensor support for the Skybell HD Doorbell.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/binary_sensor.skybell/
|
||||
"""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDevice, PLATFORM_SCHEMA)
|
||||
from homeassistant.components.skybell import (
|
||||
DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice)
|
||||
from homeassistant.const import (
|
||||
CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
DEPENDENCIES = ['skybell']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=5)
|
||||
|
||||
# Sensor types: Name, device_class, event
|
||||
SENSOR_TYPES = {
|
||||
'button': ['Button', 'occupancy', 'device:sensor:button'],
|
||||
'motion': ['Motion', 'motion', 'device:sensor: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 the platform for a Skybell device."""
|
||||
skybell = hass.data.get(SKYBELL_DOMAIN)
|
||||
|
||||
sensors = []
|
||||
for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
|
||||
for device in skybell.get_devices():
|
||||
sensors.append(SkybellBinarySensor(device, sensor_type))
|
||||
|
||||
add_devices(sensors, True)
|
||||
|
||||
|
||||
class SkybellBinarySensor(SkybellDevice, BinarySensorDevice):
|
||||
"""A binary sensor implementation for Skybell devices."""
|
||||
|
||||
def __init__(self, device, sensor_type):
|
||||
"""Initialize a binary sensor for a Skybell device."""
|
||||
super().__init__(device)
|
||||
self._sensor_type = sensor_type
|
||||
self._name = "{0} {1}".format(self._device.name,
|
||||
SENSOR_TYPES[self._sensor_type][0])
|
||||
self._device_class = SENSOR_TYPES[self._sensor_type][1]
|
||||
self._event = {}
|
||||
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 = super().device_state_attributes
|
||||
|
||||
attrs['event_date'] = self._event.get('createdAt')
|
||||
|
||||
return attrs
|
||||
|
||||
def update(self):
|
||||
"""Get the latest data and updates the state."""
|
||||
super().update()
|
||||
|
||||
event = self._device.latest(SENSOR_TYPES[self._sensor_type][2])
|
||||
|
||||
self._state = bool(event and event.get('id') != self._event.get('id'))
|
||||
|
||||
self._event = event
|
67
homeassistant/components/camera/skybell.py
Normal file
67
homeassistant/components/camera/skybell.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
"""
|
||||
Camera support for the Skybell HD Doorbell.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/camera.skybell/
|
||||
"""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import requests
|
||||
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.components.skybell import (
|
||||
DOMAIN as SKYBELL_DOMAIN, SkybellDevice)
|
||||
|
||||
DEPENDENCIES = ['skybell']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=90)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up the platform for a Skybell device."""
|
||||
skybell = hass.data.get(SKYBELL_DOMAIN)
|
||||
|
||||
sensors = []
|
||||
for device in skybell.get_devices():
|
||||
sensors.append(SkybellCamera(device))
|
||||
|
||||
add_devices(sensors, True)
|
||||
|
||||
|
||||
class SkybellCamera(SkybellDevice, Camera):
|
||||
"""A camera implementation for Skybell devices."""
|
||||
|
||||
def __init__(self, device):
|
||||
"""Initialize a camera for a Skybell device."""
|
||||
SkybellDevice.__init__(self, device)
|
||||
Camera.__init__(self)
|
||||
self._name = self._device.name
|
||||
self._url = None
|
||||
self._response = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self._name
|
||||
|
||||
def camera_image(self):
|
||||
"""Get the latest camera image."""
|
||||
super().update()
|
||||
|
||||
if self._url != self._device.image:
|
||||
self._url = self._device.image
|
||||
|
||||
try:
|
||||
self._response = requests.get(
|
||||
self._url, stream=True, timeout=10)
|
||||
except requests.HTTPError as err:
|
||||
_LOGGER.warning("Failed to get camera image: %s", err)
|
||||
self._response = None
|
||||
|
||||
if not self._response:
|
||||
return None
|
||||
|
||||
return self._response.content
|
87
homeassistant/components/light/skybell.py
Normal file
87
homeassistant/components/light/skybell.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
"""
|
||||
Light/LED support for the Skybell HD Doorbell.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.skybell/
|
||||
"""
|
||||
import logging
|
||||
|
||||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS, ATTR_RGB_COLOR,
|
||||
SUPPORT_BRIGHTNESS, SUPPORT_RGB_COLOR, Light)
|
||||
from homeassistant.components.skybell import (
|
||||
DOMAIN as SKYBELL_DOMAIN, SkybellDevice)
|
||||
|
||||
DEPENDENCIES = ['skybell']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up the platform for a Skybell device."""
|
||||
skybell = hass.data.get(SKYBELL_DOMAIN)
|
||||
|
||||
sensors = []
|
||||
for device in skybell.get_devices():
|
||||
sensors.append(SkybellLight(device))
|
||||
|
||||
add_devices(sensors, True)
|
||||
|
||||
|
||||
def _to_skybell_level(level):
|
||||
"""Convert the given HASS light level (0-255) to Skybell (0-100)."""
|
||||
return int((level * 100) / 255)
|
||||
|
||||
|
||||
def _to_hass_level(level):
|
||||
"""Convert the given Skybell (0-100) light level to HASS (0-255)."""
|
||||
return int((level * 255) / 100)
|
||||
|
||||
|
||||
class SkybellLight(SkybellDevice, Light):
|
||||
"""A binary sensor implementation for Skybell devices."""
|
||||
|
||||
def __init__(self, device):
|
||||
"""Initialize a light for a Skybell device."""
|
||||
super().__init__(device)
|
||||
self._name = self._device.name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self._name
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn on the light."""
|
||||
if ATTR_RGB_COLOR in kwargs:
|
||||
self._device.led_rgb = kwargs[ATTR_RGB_COLOR]
|
||||
elif ATTR_BRIGHTNESS in kwargs:
|
||||
self._device.led_intensity = _to_skybell_level(
|
||||
kwargs[ATTR_BRIGHTNESS])
|
||||
else:
|
||||
self._device.led_intensity = _to_skybell_level(255)
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn off the light."""
|
||||
self._device.led_intensity = 0
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if device is on."""
|
||||
return self._device.led_intensity > 0
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
"""Return the brightness of the light."""
|
||||
return _to_hass_level(self._device.led_intensity)
|
||||
|
||||
@property
|
||||
def rgb_color(self):
|
||||
"""Return the color of the light."""
|
||||
return self._device.led_rgb
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
return SUPPORT_BRIGHTNESS | SUPPORT_RGB_COLOR
|
82
homeassistant/components/sensor/skybell.py
Normal file
82
homeassistant/components/sensor/skybell.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
"""
|
||||
Sensor support for Skybell Doorbells.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.skybell/
|
||||
"""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.components.skybell import (
|
||||
DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice)
|
||||
from homeassistant.const import (
|
||||
CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
DEPENDENCIES = ['skybell']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
# Sensor types: Name, icon
|
||||
SENSOR_TYPES = {
|
||||
'chime_level': ['Chime Level', 'bell-ring'],
|
||||
}
|
||||
|
||||
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 the platform for a Skybell device."""
|
||||
skybell = hass.data.get(SKYBELL_DOMAIN)
|
||||
|
||||
sensors = []
|
||||
for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
|
||||
for device in skybell.get_devices():
|
||||
sensors.append(SkybellSensor(device, sensor_type))
|
||||
|
||||
add_devices(sensors, True)
|
||||
|
||||
|
||||
class SkybellSensor(SkybellDevice):
|
||||
"""A sensor implementation for Skybell devices."""
|
||||
|
||||
def __init__(self, device, sensor_type):
|
||||
"""Initialize a sensor for a Skybell device."""
|
||||
super().__init__(device)
|
||||
self._sensor_type = sensor_type
|
||||
self._icon = 'mdi:{}'.format(SENSOR_TYPES[self._sensor_type][1])
|
||||
self._name = "{0} {1}".format(self._device.name,
|
||||
SENSOR_TYPES[self._sensor_type][0])
|
||||
self._state = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Icon to use in the frontend, if any."""
|
||||
return self._icon
|
||||
|
||||
def update(self):
|
||||
"""Get the latest data and updates the state."""
|
||||
super().update()
|
||||
|
||||
if self._sensor_type == 'chime_level':
|
||||
self._state = self._device.outdoor_chime_level
|
93
homeassistant/components/skybell.py
Normal file
93
homeassistant/components/skybell.py
Normal file
|
@ -0,0 +1,93 @@
|
|||
"""
|
||||
Support for the Skybell HD Doorbell.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/skybell/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from requests.exceptions import HTTPError, ConnectTimeout
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION, CONF_USERNAME, CONF_PASSWORD)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
REQUIREMENTS = ['skybellpy==0.1.1']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_ATTRIBUTION = "Data provided by Skybell.com"
|
||||
|
||||
NOTIFICATION_ID = 'skybell_notification'
|
||||
NOTIFICATION_TITLE = 'Skybell Sensor Setup'
|
||||
|
||||
DOMAIN = 'skybell'
|
||||
DEFAULT_CACHEDB = './skybell_cache.pickle'
|
||||
DEFAULT_ENTITY_NAMESPACE = 'skybell'
|
||||
|
||||
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 the Skybell component."""
|
||||
conf = config[DOMAIN]
|
||||
username = conf.get(CONF_USERNAME)
|
||||
password = conf.get(CONF_PASSWORD)
|
||||
|
||||
try:
|
||||
from skybellpy import Skybell
|
||||
|
||||
cache = hass.config.path(DEFAULT_CACHEDB)
|
||||
skybell = Skybell(username=username, password=password,
|
||||
get_devices=True, cache_path=cache)
|
||||
|
||||
hass.data[DOMAIN] = skybell
|
||||
except (ConnectTimeout, HTTPError) as ex:
|
||||
_LOGGER.error("Unable to connect to Skybell service: %s", str(ex))
|
||||
hass.components.persistent_notification.create(
|
||||
'Error: {}<br />'
|
||||
'You will need to restart hass after fixing.'
|
||||
''.format(ex),
|
||||
title=NOTIFICATION_TITLE,
|
||||
notification_id=NOTIFICATION_ID)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class SkybellDevice(Entity):
|
||||
"""A HA implementation for Skybell devices."""
|
||||
|
||||
def __init__(self, device):
|
||||
"""Initialize a sensor for Skybell device."""
|
||||
self._device = device
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return the polling state."""
|
||||
return True
|
||||
|
||||
def update(self):
|
||||
"""Update automation state."""
|
||||
self._device.refresh()
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return {
|
||||
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
|
||||
'device_id': self._device.device_id,
|
||||
'status': self._device.status,
|
||||
'location': self._device.location,
|
||||
'wifi_ssid': self._device.wifi_ssid,
|
||||
'wifi_status': self._device.wifi_status,
|
||||
'last_check_in': self._device.last_check_in,
|
||||
'motion_threshold': self._device.motion_threshold,
|
||||
'video_profile': self._device.video_profile,
|
||||
}
|
75
homeassistant/components/switch/skybell.py
Normal file
75
homeassistant/components/switch/skybell.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
"""
|
||||
Switch support for the Skybell HD Doorbell.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/switch.skybell/
|
||||
"""
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
|
||||
from homeassistant.components.skybell import (
|
||||
DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice)
|
||||
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice
|
||||
from homeassistant.const import (
|
||||
CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
DEPENDENCIES = ['skybell']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Switch types: Name
|
||||
SWITCH_TYPES = {
|
||||
'do_not_disturb': ['Do Not Disturb'],
|
||||
'motion_sensor': ['Motion Sensor'],
|
||||
}
|
||||
|
||||
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(SWITCH_TYPES)]),
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up the platform for a Skybell device."""
|
||||
skybell = hass.data.get(SKYBELL_DOMAIN)
|
||||
|
||||
sensors = []
|
||||
for switch_type in config.get(CONF_MONITORED_CONDITIONS):
|
||||
for device in skybell.get_devices():
|
||||
sensors.append(SkybellSwitch(device, switch_type))
|
||||
|
||||
add_devices(sensors, True)
|
||||
|
||||
|
||||
class SkybellSwitch(SkybellDevice, SwitchDevice):
|
||||
"""A switch implementation for Skybell devices."""
|
||||
|
||||
def __init__(self, device, switch_type):
|
||||
"""Initialize a light for a Skybell device."""
|
||||
super().__init__(device)
|
||||
self._switch_type = switch_type
|
||||
self._name = "{0} {1}".format(self._device.name,
|
||||
SWITCH_TYPES[self._switch_type][0])
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self._name
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn on the switch."""
|
||||
setattr(self._device, self._switch_type, True)
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn on the switch."""
|
||||
setattr(self._device, self._switch_type, False)
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if device is on."""
|
||||
return getattr(self._device, self._switch_type)
|
|
@ -931,6 +931,9 @@ simplepush==1.1.3
|
|||
# homeassistant.components.alarm_control_panel.simplisafe
|
||||
simplisafe-python==1.0.5
|
||||
|
||||
# homeassistant.components.skybell
|
||||
skybellpy==0.1.1
|
||||
|
||||
# homeassistant.components.notify.slack
|
||||
slacker==0.9.60
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue