2016-02-06 01:24:44 +00:00
|
|
|
"""
|
2016-02-07 10:52:17 +00:00
|
|
|
Support for Ubiquiti's UVC cameras.
|
2016-02-06 01:24:44 +00:00
|
|
|
|
2016-02-07 10:52:17 +00:00
|
|
|
For more details about this platform, please refer to the documentation at
|
|
|
|
https://home-assistant.io/components/camera.uvc/
|
2016-02-06 01:24:44 +00:00
|
|
|
"""
|
|
|
|
import logging
|
|
|
|
import socket
|
|
|
|
|
|
|
|
import requests
|
|
|
|
|
|
|
|
from homeassistant.components.camera import DOMAIN, Camera
|
2016-02-19 05:27:50 +00:00
|
|
|
from homeassistant.helpers import validate_config
|
2016-02-06 01:24:44 +00:00
|
|
|
|
2016-02-22 21:17:53 +00:00
|
|
|
REQUIREMENTS = ['uvcclient==0.8']
|
2016-02-06 01:24:44 +00:00
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
2016-03-07 16:45:06 +00:00
|
|
|
"""Discover cameras on a Unifi NVR."""
|
2016-02-06 01:24:44 +00:00
|
|
|
if not validate_config({DOMAIN: config}, {DOMAIN: ['nvr', 'key']},
|
|
|
|
_LOGGER):
|
|
|
|
return None
|
|
|
|
|
|
|
|
addr = config.get('nvr')
|
|
|
|
key = config.get('key')
|
2016-02-22 22:06:06 +00:00
|
|
|
try:
|
|
|
|
port = int(config.get('port', 7080))
|
|
|
|
except ValueError:
|
|
|
|
_LOGGER.error('Invalid port number provided')
|
|
|
|
return False
|
2016-02-06 01:24:44 +00:00
|
|
|
|
|
|
|
from uvcclient import nvr
|
|
|
|
nvrconn = nvr.UVCRemote(addr, port, key)
|
|
|
|
try:
|
|
|
|
cameras = nvrconn.index()
|
|
|
|
except nvr.NotAuthorized:
|
|
|
|
_LOGGER.error('Authorization failure while connecting to NVR')
|
|
|
|
return False
|
|
|
|
except nvr.NvrError:
|
|
|
|
_LOGGER.error('NVR refuses to talk to me')
|
|
|
|
return False
|
|
|
|
except requests.exceptions.ConnectionError as ex:
|
|
|
|
_LOGGER.error('Unable to connect to NVR: %s', str(ex))
|
|
|
|
return False
|
|
|
|
|
2016-02-23 20:01:51 +00:00
|
|
|
# Filter out airCam models, which are not supported in the latest
|
|
|
|
# version of UnifiVideo and which are EOL by Ubiquiti
|
|
|
|
cameras = [camera for camera in cameras
|
|
|
|
if 'airCam' not in nvrconn.get_camera(camera['uuid'])['model']]
|
|
|
|
|
2016-02-22 22:06:06 +00:00
|
|
|
add_devices([UnifiVideoCamera(nvrconn,
|
|
|
|
camera['uuid'],
|
|
|
|
camera['name'])
|
|
|
|
for camera in cameras])
|
|
|
|
return True
|
2016-02-06 01:24:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
class UnifiVideoCamera(Camera):
|
2016-03-07 16:45:06 +00:00
|
|
|
"""A Ubiquiti Unifi Video Camera."""
|
2016-03-07 19:29:54 +00:00
|
|
|
|
2016-02-06 01:24:44 +00:00
|
|
|
def __init__(self, nvr, uuid, name):
|
2016-03-07 19:29:54 +00:00
|
|
|
"""Initialize an Unifi camera."""
|
2016-02-06 01:24:44 +00:00
|
|
|
super(UnifiVideoCamera, self).__init__()
|
|
|
|
self._nvr = nvr
|
|
|
|
self._uuid = uuid
|
|
|
|
self._name = name
|
|
|
|
self.is_streaming = False
|
2016-02-14 17:06:46 +00:00
|
|
|
self._connect_addr = None
|
|
|
|
self._camera = None
|
2016-02-06 01:24:44 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
2016-03-07 16:45:06 +00:00
|
|
|
"""Return the name of this camera."""
|
2016-02-06 01:24:44 +00:00
|
|
|
return self._name
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_recording(self):
|
2016-03-07 16:45:06 +00:00
|
|
|
"""Return true if the camera is recording."""
|
2016-02-06 01:24:44 +00:00
|
|
|
caminfo = self._nvr.get_camera(self._uuid)
|
|
|
|
return caminfo['recordingSettings']['fullTimeRecordEnabled']
|
|
|
|
|
2016-02-14 17:09:54 +00:00
|
|
|
@property
|
|
|
|
def brand(self):
|
2016-03-07 16:45:06 +00:00
|
|
|
"""Return the brand of this camera."""
|
2016-02-14 17:09:54 +00:00
|
|
|
return 'Ubiquiti'
|
|
|
|
|
|
|
|
@property
|
|
|
|
def model(self):
|
2016-03-07 16:45:06 +00:00
|
|
|
"""Return the model of this camera."""
|
2016-02-14 17:09:54 +00:00
|
|
|
caminfo = self._nvr.get_camera(self._uuid)
|
|
|
|
return caminfo['model']
|
|
|
|
|
2016-02-14 17:06:46 +00:00
|
|
|
def _login(self):
|
2016-03-07 16:45:06 +00:00
|
|
|
"""Login to the camera."""
|
2016-02-06 01:24:44 +00:00
|
|
|
from uvcclient import camera as uvc_camera
|
2016-02-22 21:17:53 +00:00
|
|
|
from uvcclient import store as uvc_store
|
|
|
|
|
2016-02-06 01:24:44 +00:00
|
|
|
caminfo = self._nvr.get_camera(self._uuid)
|
2016-02-14 17:06:46 +00:00
|
|
|
if self._connect_addr:
|
|
|
|
addrs = [self._connect_addr]
|
|
|
|
else:
|
|
|
|
addrs = [caminfo['host'], caminfo['internalHost']]
|
|
|
|
|
2016-02-22 21:17:53 +00:00
|
|
|
store = uvc_store.get_info_store()
|
|
|
|
password = store.get_camera_password(self._uuid)
|
|
|
|
if password is None:
|
|
|
|
_LOGGER.debug('Logging into camera %(name)s with default password',
|
2016-02-22 22:06:06 +00:00
|
|
|
dict(name=self._name))
|
2016-02-22 21:17:53 +00:00
|
|
|
password = 'ubnt'
|
|
|
|
|
2016-02-06 01:24:44 +00:00
|
|
|
camera = None
|
2016-02-14 17:06:46 +00:00
|
|
|
for addr in addrs:
|
2016-02-06 01:24:44 +00:00
|
|
|
try:
|
|
|
|
camera = uvc_camera.UVCCameraClient(addr,
|
|
|
|
caminfo['username'],
|
2016-02-22 21:17:53 +00:00
|
|
|
password)
|
2016-02-14 17:06:46 +00:00
|
|
|
camera.login()
|
2016-02-06 01:24:44 +00:00
|
|
|
_LOGGER.debug('Logged into UVC camera %(name)s via %(addr)s',
|
|
|
|
dict(name=self._name, addr=addr))
|
2016-02-14 17:06:46 +00:00
|
|
|
self._connect_addr = addr
|
2016-02-22 22:06:06 +00:00
|
|
|
break
|
2016-02-06 01:24:44 +00:00
|
|
|
except socket.error:
|
|
|
|
pass
|
2016-02-14 16:36:51 +00:00
|
|
|
except uvc_camera.CameraConnectError:
|
|
|
|
pass
|
|
|
|
except uvc_camera.CameraAuthError:
|
|
|
|
pass
|
2016-02-22 22:06:06 +00:00
|
|
|
if not self._connect_addr:
|
2016-02-06 01:24:44 +00:00
|
|
|
_LOGGER.error('Unable to login to camera')
|
|
|
|
return None
|
|
|
|
|
2016-02-14 17:06:46 +00:00
|
|
|
self._camera = camera
|
|
|
|
return True
|
|
|
|
|
|
|
|
def camera_image(self):
|
2016-03-07 16:45:06 +00:00
|
|
|
"""Return the image of this camera."""
|
2016-02-14 17:06:46 +00:00
|
|
|
from uvcclient import camera as uvc_camera
|
|
|
|
if not self._camera:
|
|
|
|
if not self._login():
|
|
|
|
return
|
|
|
|
|
|
|
|
def _get_image(retry=True):
|
|
|
|
try:
|
|
|
|
return self._camera.get_snapshot()
|
|
|
|
except uvc_camera.CameraConnectError:
|
|
|
|
_LOGGER.error('Unable to contact camera')
|
|
|
|
except uvc_camera.CameraAuthError:
|
|
|
|
if retry:
|
|
|
|
self._login()
|
|
|
|
return _get_image(retry=False)
|
|
|
|
else:
|
|
|
|
_LOGGER.error('Unable to log into camera, unable '
|
|
|
|
'to get snapshot')
|
|
|
|
raise
|
|
|
|
|
|
|
|
return _get_image()
|