Merge pull request #2241 from kk7ds/unifi-3.2

Add support for UniFi Video >= 3.2.0
pull/2252/head
Dan Smith 2016-06-06 20:33:45 -07:00
commit e29459a1ae
3 changed files with 65 additions and 19 deletions

View File

@ -12,7 +12,7 @@ import requests
from homeassistant.components.camera import DOMAIN, Camera
from homeassistant.helpers import validate_config
REQUIREMENTS = ['uvcclient==0.8']
REQUIREMENTS = ['uvcclient==0.9.0']
_LOGGER = logging.getLogger(__name__)
@ -45,13 +45,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
_LOGGER.error('Unable to connect to NVR: %s', str(ex))
return False
identifier = nvrconn.server_version >= (3, 2, 0) and 'id' or 'uuid'
# 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']]
cameras = [
camera for camera in cameras
if 'airCam' not in nvrconn.get_camera(camera[identifier])['model']]
add_devices([UnifiVideoCamera(nvrconn,
camera['uuid'],
camera[identifier],
camera['name'])
for camera in cameras])
return True
@ -110,12 +112,17 @@ class UnifiVideoCamera(Camera):
dict(name=self._name))
password = 'ubnt'
if self._nvr.server_version >= (3, 2, 0):
client_cls = uvc_camera.UVCCameraClientV320
else:
client_cls = uvc_camera.UVCCameraClient
camera = None
for addr in addrs:
try:
camera = uvc_camera.UVCCameraClient(addr,
caminfo['username'],
password)
camera = client_cls(addr,
caminfo['username'],
password)
camera.login()
_LOGGER.debug('Logged into UVC camera %(name)s via %(addr)s',
dict(name=self._name, addr=addr))

View File

@ -375,7 +375,7 @@ unifi==1.2.5
urllib3
# homeassistant.components.camera.uvc
uvcclient==0.8
uvcclient==0.9.0
# homeassistant.components.verisure
vsure==0.8.1

View File

@ -23,14 +23,14 @@ class TestUVCSetup(unittest.TestCase):
'key': 'secret',
}
fake_cameras = [
{'uuid': 'one', 'name': 'Front'},
{'uuid': 'two', 'name': 'Back'},
{'uuid': 'three', 'name': 'Old AirCam'},
{'uuid': 'one', 'name': 'Front', 'id': 'id1'},
{'uuid': 'two', 'name': 'Back', 'id': 'id2'},
{'uuid': 'three', 'name': 'Old AirCam', 'id': 'id3'},
]
def fake_get_camera(uuid):
""""Create a fake camera."""
if uuid == 'three':
if uuid == 'id3':
return {'model': 'airCam'}
else:
return {'model': 'UVC'}
@ -39,13 +39,14 @@ class TestUVCSetup(unittest.TestCase):
add_devices = mock.MagicMock()
mock_remote.return_value.index.return_value = fake_cameras
mock_remote.return_value.get_camera.side_effect = fake_get_camera
mock_remote.return_value.server_version = (3, 2, 0)
self.assertTrue(uvc.setup_platform(hass, config, add_devices))
mock_remote.assert_called_once_with('foo', 123, 'secret')
add_devices.assert_called_once_with([
mock_uvc.return_value, mock_uvc.return_value])
mock_uvc.assert_has_calls([
mock.call(mock_remote.return_value, 'one', 'Front'),
mock.call(mock_remote.return_value, 'two', 'Back'),
mock.call(mock_remote.return_value, 'id1', 'Front'),
mock.call(mock_remote.return_value, 'id2', 'Back'),
])
@mock.patch('uvcclient.nvr.UVCRemote')
@ -57,13 +58,40 @@ class TestUVCSetup(unittest.TestCase):
'key': 'secret',
}
fake_cameras = [
{'uuid': 'one', 'name': 'Front'},
{'uuid': 'two', 'name': 'Back'},
{'uuid': 'one', 'name': 'Front', 'id': 'id1'},
{'uuid': 'two', 'name': 'Back', 'id': 'id2'},
]
hass = mock.MagicMock()
add_devices = mock.MagicMock()
mock_remote.return_value.index.return_value = fake_cameras
mock_remote.return_value.get_camera.return_value = {'model': 'UVC'}
mock_remote.return_value.server_version = (3, 2, 0)
self.assertTrue(uvc.setup_platform(hass, config, add_devices))
mock_remote.assert_called_once_with('foo', 7080, 'secret')
add_devices.assert_called_once_with([
mock_uvc.return_value, mock_uvc.return_value])
mock_uvc.assert_has_calls([
mock.call(mock_remote.return_value, 'id1', 'Front'),
mock.call(mock_remote.return_value, 'id2', 'Back'),
])
@mock.patch('uvcclient.nvr.UVCRemote')
@mock.patch.object(uvc, 'UnifiVideoCamera')
def test_setup_partial_config_v31x(self, mock_uvc, mock_remote):
"""Test the setup with a v3.1.x server."""
config = {
'nvr': 'foo',
'key': 'secret',
}
fake_cameras = [
{'uuid': 'one', 'name': 'Front', 'id': 'id1'},
{'uuid': 'two', 'name': 'Back', 'id': 'id2'},
]
hass = mock.MagicMock()
add_devices = mock.MagicMock()
mock_remote.return_value.index.return_value = fake_cameras
mock_remote.return_value.get_camera.return_value = {'model': 'UVC'}
mock_remote.return_value.server_version = (3, 1, 3)
self.assertTrue(uvc.setup_platform(hass, config, add_devices))
mock_remote.assert_called_once_with('foo', 7080, 'secret')
add_devices.assert_called_once_with([
@ -114,6 +142,7 @@ class TestUVC(unittest.TestCase):
'internalHost': 'host-b',
'username': 'admin',
}
self.nvr.server_version = (3, 2, 0)
def test_properties(self):
""""Test the properties."""
@ -123,7 +152,7 @@ class TestUVC(unittest.TestCase):
self.assertEqual('UVC Fake', self.uvc.model)
@mock.patch('uvcclient.store.get_info_store')
@mock.patch('uvcclient.camera.UVCCameraClient')
@mock.patch('uvcclient.camera.UVCCameraClientV320')
def test_login(self, mock_camera, mock_store):
""""Test the login."""
mock_store.return_value.get_camera_password.return_value = 'seekret'
@ -133,6 +162,16 @@ class TestUVC(unittest.TestCase):
@mock.patch('uvcclient.store.get_info_store')
@mock.patch('uvcclient.camera.UVCCameraClient')
def test_login_v31x(self, mock_camera, mock_store):
"""Test login with v3.1.x server."""
mock_store.return_value.get_camera_password.return_value = 'seekret'
self.nvr.server_version = (3, 1, 3)
self.uvc._login()
mock_camera.assert_called_once_with('host-a', 'admin', 'seekret')
mock_camera.return_value.login.assert_called_once_with()
@mock.patch('uvcclient.store.get_info_store')
@mock.patch('uvcclient.camera.UVCCameraClientV320')
def test_login_no_password(self, mock_camera, mock_store):
""""Test the login with no password."""
mock_store.return_value.get_camera_password.return_value = None
@ -141,7 +180,7 @@ class TestUVC(unittest.TestCase):
mock_camera.return_value.login.assert_called_once_with()
@mock.patch('uvcclient.store.get_info_store')
@mock.patch('uvcclient.camera.UVCCameraClient')
@mock.patch('uvcclient.camera.UVCCameraClientV320')
def test_login_tries_both_addrs_and_caches(self, mock_camera, mock_store):
""""Test the login tries."""
responses = [0]
@ -165,7 +204,7 @@ class TestUVC(unittest.TestCase):
mock_camera.return_value.login.assert_called_once_with()
@mock.patch('uvcclient.store.get_info_store')
@mock.patch('uvcclient.camera.UVCCameraClient')
@mock.patch('uvcclient.camera.UVCCameraClientV320')
def test_login_fails_both_properly(self, mock_camera, mock_store):
""""Test if login fails properly."""
mock_camera.return_value.login.side_effect = socket.error