mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 10:17:09 +00:00
Support for Blink Camera System (#6444)
* Passing pep8, no tests yet * Fixed some issues with the request throttling * Removed ability to set throttle time because it was causing more issues than it was worth * Added blink to .coveragerc * Changed blinkpy version * Removed global var, fixed per PR requests * Added services for camera, migrated switch to binary_sensor * Added schema for service, fixed naming, removed unused function
This commit is contained in:
parent
3508f74fb2
commit
629b2e81ba
@ -16,6 +16,9 @@ omit =
|
|||||||
|
|
||||||
homeassistant/components/bbb_gpio.py
|
homeassistant/components/bbb_gpio.py
|
||||||
homeassistant/components/*/bbb_gpio.py
|
homeassistant/components/*/bbb_gpio.py
|
||||||
|
|
||||||
|
homeassistant/components/blink.py
|
||||||
|
homeassistant/components/*/blink.py
|
||||||
|
|
||||||
homeassistant/components/bloomsky.py
|
homeassistant/components/bloomsky.py
|
||||||
homeassistant/components/*/bloomsky.py
|
homeassistant/components/*/bloomsky.py
|
||||||
|
74
homeassistant/components/binary_sensor/blink.py
Normal file
74
homeassistant/components/binary_sensor/blink.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
"""
|
||||||
|
Support for Blink system camera control.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/binary_sensor.blink/
|
||||||
|
"""
|
||||||
|
from homeassistant.components.blink import DOMAIN
|
||||||
|
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||||
|
|
||||||
|
DEPENDENCIES = ['blink']
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
"""Setup the blink binary sensors."""
|
||||||
|
if discovery_info is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
data = hass.data[DOMAIN].blink
|
||||||
|
devs = list()
|
||||||
|
for name in data.cameras:
|
||||||
|
devs.append(BlinkCameraMotionSensor(name, data))
|
||||||
|
devs.append(BlinkSystemSensor(data))
|
||||||
|
add_devices(devs, True)
|
||||||
|
|
||||||
|
|
||||||
|
class BlinkCameraMotionSensor(BinarySensorDevice):
|
||||||
|
"""A representation of a Blink binary sensor."""
|
||||||
|
|
||||||
|
def __init__(self, name, data):
|
||||||
|
"""Initialize the sensor."""
|
||||||
|
self._name = 'blink_' + name + '_motion_enabled'
|
||||||
|
self._camera_name = name
|
||||||
|
self.data = data
|
||||||
|
self._state = self.data.cameras[self._camera_name].armed
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the blink sensor."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
"""Return the status of the sensor."""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Update sensor state."""
|
||||||
|
self.data.refresh()
|
||||||
|
self._state = self.data.cameras[self._camera_name].armed
|
||||||
|
|
||||||
|
|
||||||
|
class BlinkSystemSensor(BinarySensorDevice):
|
||||||
|
"""A representation of a Blink system sensor."""
|
||||||
|
|
||||||
|
def __init__(self, data):
|
||||||
|
"""Initialize the sensor."""
|
||||||
|
self._name = 'blink armed status'
|
||||||
|
self.data = data
|
||||||
|
self._state = self.data.arm
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the blink sensor."""
|
||||||
|
return self._name.replace(" ", "_")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
"""Return the status of the sensor."""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Update sensor state."""
|
||||||
|
self.data.refresh()
|
||||||
|
self._state = self.data.arm
|
87
homeassistant/components/blink.py
Normal file
87
homeassistant/components/blink.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
"""
|
||||||
|
Support for Blink Home Camera System.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/blink/
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import voluptuous as vol
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.const import (CONF_USERNAME,
|
||||||
|
CONF_PASSWORD,
|
||||||
|
ATTR_FRIENDLY_NAME,
|
||||||
|
ATTR_ARMED)
|
||||||
|
from homeassistant.helpers import discovery
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DOMAIN = 'blink'
|
||||||
|
REQUIREMENTS = ['blinkpy==0.4.4']
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
|
DOMAIN: vol.Schema({
|
||||||
|
vol.Required(CONF_USERNAME): cv.string,
|
||||||
|
vol.Required(CONF_PASSWORD): cv.string
|
||||||
|
})
|
||||||
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
ARM_SYSTEM_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ARMED): cv.boolean
|
||||||
|
})
|
||||||
|
|
||||||
|
ARM_CAMERA_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(ATTR_FRIENDLY_NAME): cv.string,
|
||||||
|
vol.Optional(ATTR_ARMED): cv.boolean
|
||||||
|
})
|
||||||
|
|
||||||
|
SNAP_PICTURE_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(ATTR_FRIENDLY_NAME): cv.string
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class BlinkSystem(object):
|
||||||
|
"""Blink System class."""
|
||||||
|
|
||||||
|
def __init__(self, config_info):
|
||||||
|
"""Initialize the system."""
|
||||||
|
import blinkpy
|
||||||
|
self.blink = blinkpy.Blink(username=config_info[DOMAIN][CONF_USERNAME],
|
||||||
|
password=config_info[DOMAIN][CONF_PASSWORD])
|
||||||
|
self.blink.setup_system()
|
||||||
|
|
||||||
|
|
||||||
|
def setup(hass, config):
|
||||||
|
"""Setup Blink System."""
|
||||||
|
hass.data[DOMAIN] = BlinkSystem(config)
|
||||||
|
discovery.load_platform(hass, 'camera', DOMAIN, {}, config)
|
||||||
|
discovery.load_platform(hass, 'sensor', DOMAIN, {}, config)
|
||||||
|
discovery.load_platform(hass, 'binary_sensor', DOMAIN, {}, config)
|
||||||
|
|
||||||
|
def snap_picture(call):
|
||||||
|
"""Take a picture."""
|
||||||
|
cameras = hass.data[DOMAIN].blink.cameras
|
||||||
|
name = call.data.get(ATTR_FRIENDLY_NAME, '')
|
||||||
|
if name in cameras:
|
||||||
|
cameras[name].snap_picture()
|
||||||
|
|
||||||
|
def arm_camera(call):
|
||||||
|
"""Arm a camera."""
|
||||||
|
cameras = hass.data[DOMAIN].blink.cameras
|
||||||
|
name = call.data.get(ATTR_FRIENDLY_NAME, '')
|
||||||
|
value = call.data.get(ATTR_ARMED, True)
|
||||||
|
if name in cameras:
|
||||||
|
cameras[name].set_motion_detect(value)
|
||||||
|
|
||||||
|
def arm_system(call):
|
||||||
|
"""Arm the system."""
|
||||||
|
value = call.data.get(ATTR_ARMED, True)
|
||||||
|
hass.data[DOMAIN].blink.arm = value
|
||||||
|
hass.data[DOMAIN].blink.refresh()
|
||||||
|
|
||||||
|
hass.services.register(DOMAIN, 'snap_picture', snap_picture,
|
||||||
|
schema=SNAP_PICTURE_SCHEMA)
|
||||||
|
hass.services.register(DOMAIN, 'arm_camera', arm_camera,
|
||||||
|
schema=ARM_CAMERA_SCHEMA)
|
||||||
|
hass.services.register(DOMAIN, 'arm_system', arm_system,
|
||||||
|
schema=ARM_SYSTEM_SCHEMA)
|
||||||
|
|
||||||
|
return True
|
81
homeassistant/components/camera/blink.py
Normal file
81
homeassistant/components/camera/blink.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
"""
|
||||||
|
Support for Blink system camera.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/camera.blink/
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from homeassistant.components.blink import DOMAIN
|
||||||
|
from homeassistant.components.camera import Camera
|
||||||
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
|
DEPENDENCIES = ['blink']
|
||||||
|
|
||||||
|
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
"""Setup a Blink Camera."""
|
||||||
|
if discovery_info is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
data = hass.data[DOMAIN].blink
|
||||||
|
devs = list()
|
||||||
|
for name in data.cameras:
|
||||||
|
devs.append(BlinkCamera(hass, config, data, name))
|
||||||
|
|
||||||
|
add_devices(devs)
|
||||||
|
|
||||||
|
|
||||||
|
class BlinkCamera(Camera):
|
||||||
|
"""An implementation of a Blink Camera."""
|
||||||
|
|
||||||
|
def __init__(self, hass, config, data, name):
|
||||||
|
"""Initialize a camera."""
|
||||||
|
super().__init__()
|
||||||
|
self.data = data
|
||||||
|
self.hass = hass
|
||||||
|
self._name = name
|
||||||
|
self.notifications = self.data.cameras[self._name].notifications
|
||||||
|
self.response = None
|
||||||
|
|
||||||
|
_LOGGER.info("Initialized blink camera %s", self._name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""A camera name."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
|
def request_image(self):
|
||||||
|
"""An image request from Blink servers."""
|
||||||
|
_LOGGER.info("Requesting new image from blink servers")
|
||||||
|
image_url = self.check_for_motion()
|
||||||
|
header = self.data.cameras[self._name].header
|
||||||
|
self.response = requests.get(image_url, headers=header, stream=True)
|
||||||
|
|
||||||
|
def check_for_motion(self):
|
||||||
|
"""A method to check if motion has been detected since last update."""
|
||||||
|
self.data.refresh()
|
||||||
|
notifs = self.data.cameras[self._name].notifications
|
||||||
|
if notifs > self.notifications:
|
||||||
|
# We detected motion at some point
|
||||||
|
self.data.last_motion()
|
||||||
|
self.notifications = notifs
|
||||||
|
# returning motion image currently not working
|
||||||
|
# return self.data.cameras[self._name].motion['image']
|
||||||
|
elif notifs < self.notifications:
|
||||||
|
self.notifications = notifs
|
||||||
|
|
||||||
|
return self.data.camera_thumbs[self._name]
|
||||||
|
|
||||||
|
def camera_image(self):
|
||||||
|
"""Return a still image reponse from the camera."""
|
||||||
|
self.request_image()
|
||||||
|
return self.response.content
|
84
homeassistant/components/sensor/blink.py
Normal file
84
homeassistant/components/sensor/blink.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
"""
|
||||||
|
Support for Blink system camera sensors.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.blink/
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.components.blink import DOMAIN
|
||||||
|
from homeassistant.const import TEMP_FAHRENHEIT
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
|
DEPENDENCIES = ['blink']
|
||||||
|
SENSOR_TYPES = {
|
||||||
|
'temperature': ['Temperature', TEMP_FAHRENHEIT],
|
||||||
|
'battery': ['Battery', ''],
|
||||||
|
'notifications': ['Notifications', '']
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
"""Setup a Blink sensor."""
|
||||||
|
if discovery_info is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
data = hass.data[DOMAIN].blink
|
||||||
|
devs = list()
|
||||||
|
index = 0
|
||||||
|
for name in data.cameras:
|
||||||
|
devs.append(BlinkSensor(name, 'temperature', index, data))
|
||||||
|
devs.append(BlinkSensor(name, 'battery', index, data))
|
||||||
|
devs.append(BlinkSensor(name, 'notifications', index, data))
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
add_devices(devs, True)
|
||||||
|
|
||||||
|
|
||||||
|
class BlinkSensor(Entity):
|
||||||
|
"""A Blink camera sensor."""
|
||||||
|
|
||||||
|
def __init__(self, name, sensor_type, index, data):
|
||||||
|
"""A method to initialize sensors from Blink camera."""
|
||||||
|
self._name = 'blink_' + name + '_' + SENSOR_TYPES[sensor_type][0]
|
||||||
|
self._camera_name = name
|
||||||
|
self._type = sensor_type
|
||||||
|
self.data = data
|
||||||
|
self.index = index
|
||||||
|
self._state = None
|
||||||
|
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""A method to return the name of the camera."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""A camera's current state."""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""A unique camera sensor identifier."""
|
||||||
|
return "sensor_{}_{}".format(self._name, self.index)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
"""A method to determine the unit of measurement for temperature."""
|
||||||
|
return self._unit_of_measurement
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""A method to retrieve sensor data from the camera."""
|
||||||
|
camera = self.data.cameras[self._camera_name]
|
||||||
|
if self._type == 'temperature':
|
||||||
|
self._state = camera.temperature
|
||||||
|
elif self._type == 'battery':
|
||||||
|
self._state = camera.battery
|
||||||
|
elif self._type == 'notifications':
|
||||||
|
self._state = camera.notifications
|
||||||
|
else:
|
||||||
|
self._state = None
|
||||||
|
_LOGGER.warning("Could not retrieve state from %s", self.name)
|
@ -67,6 +67,9 @@ batinfo==0.4.2
|
|||||||
# homeassistant.components.sensor.scrape
|
# homeassistant.components.sensor.scrape
|
||||||
beautifulsoup4==4.5.3
|
beautifulsoup4==4.5.3
|
||||||
|
|
||||||
|
# homeassistant.components.blink
|
||||||
|
blinkpy==0.4.4
|
||||||
|
|
||||||
# homeassistant.components.light.blinksticklight
|
# homeassistant.components.light.blinksticklight
|
||||||
blinkstick==1.1.8
|
blinkstick==1.1.8
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user