diff --git a/.coveragerc b/.coveragerc
index ba936e424e1170398438a776658f576d484264af..9eb9ee2f5b8e000dccb7dc25374fbf102d3cb507 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -69,7 +69,10 @@ omit =
     homeassistant/components/binary_sensor/arest.py
     homeassistant/components/binary_sensor/rest.py
     homeassistant/components/browser.py
-    homeassistant/components/camera/*
+    homeassistant/components/camera/bloomsky.py
+    homeassistant/components/camera/foscam.py
+    homeassistant/components/camera/generic.py
+    homeassistant/components/camera/mjpeg.py
     homeassistant/components/device_tracker/actiontec.py
     homeassistant/components/device_tracker/aruba.py
     homeassistant/components/device_tracker/asuswrt.py
diff --git a/homeassistant/components/camera/uvc.py b/homeassistant/components/camera/uvc.py
index c69f1f18e1f0c957f565c63c3fbb013a5f39ec66..5a84c53554005298e882512773d4948cb01ba1bf 100644
--- a/homeassistant/components/camera/uvc.py
+++ b/homeassistant/components/camera/uvc.py
@@ -26,8 +26,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
         return None
 
     addr = config.get('nvr')
-    port = int(config.get('port', 7080))
     key = config.get('key')
+    try:
+        port = int(config.get('port', 7080))
+    except ValueError:
+        _LOGGER.error('Invalid port number provided')
+        return False
 
     from uvcclient import nvr
     nvrconn = nvr.UVCRemote(addr, port, key)
@@ -43,10 +47,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
         _LOGGER.error('Unable to connect to NVR: %s', str(ex))
         return False
 
-    for camera in cameras:
-        add_devices([UnifiVideoCamera(nvrconn,
-                                      camera['uuid'],
-                                      camera['name'])])
+    add_devices([UnifiVideoCamera(nvrconn,
+                                  camera['uuid'],
+                                  camera['name'])
+                 for camera in cameras])
+    return True
 
 
 class UnifiVideoCamera(Camera):
@@ -93,7 +98,7 @@ class UnifiVideoCamera(Camera):
         password = store.get_camera_password(self._uuid)
         if password is None:
             _LOGGER.debug('Logging into camera %(name)s with default password',
-                         dict(name=self._name))
+                          dict(name=self._name))
             password = 'ubnt'
 
         camera = None
@@ -106,13 +111,14 @@ class UnifiVideoCamera(Camera):
                 _LOGGER.debug('Logged into UVC camera %(name)s via %(addr)s',
                               dict(name=self._name, addr=addr))
                 self._connect_addr = addr
+                break
             except socket.error:
                 pass
             except uvc_camera.CameraConnectError:
                 pass
             except uvc_camera.CameraAuthError:
                 pass
-        if not camera:
+        if not self._connect_addr:
             _LOGGER.error('Unable to login to camera')
             return None
 
diff --git a/requirements_all.txt b/requirements_all.txt
index b5736ef3801c29ea629810621d019f7b4b08b896..c93dc53545118aecfa910058c319f3ca7e2a969c 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -262,7 +262,7 @@ unifi==1.2.4
 urllib3
 
 # homeassistant.components.camera.uvc
-uvcclient==0.6
+uvcclient==0.8
 
 # homeassistant.components.verisure
 vsure==0.5.1
diff --git a/tests/components/camera/test_uvc.py b/tests/components/camera/test_uvc.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c87945cff2b0b08cb066006df9c9ded2be82dd8
--- /dev/null
+++ b/tests/components/camera/test_uvc.py
@@ -0,0 +1,194 @@
+"""
+tests.components.camera.test_uvc
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Tests for uvc camera module.
+"""
+
+import socket
+import unittest
+from unittest import mock
+
+import requests
+from uvcclient import camera
+from uvcclient import nvr
+
+from homeassistant.components.camera import uvc
+
+
+class TestUVCSetup(unittest.TestCase):
+    @mock.patch('uvcclient.nvr.UVCRemote')
+    @mock.patch.object(uvc, 'UnifiVideoCamera')
+    def test_setup_full_config(self, mock_uvc, mock_remote):
+        config = {
+            'nvr': 'foo',
+            'port': 123,
+            'key': 'secret',
+        }
+        fake_cameras = [
+            {'uuid': 'one', 'name': 'Front'},
+            {'uuid': 'two', 'name': 'Back'},
+        ]
+        hass = mock.MagicMock()
+        add_devices = mock.MagicMock()
+        mock_remote.return_value.index.return_value = fake_cameras
+        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.patch('uvcclient.nvr.UVCRemote')
+    @mock.patch.object(uvc, 'UnifiVideoCamera')
+    def test_setup_partial_config(self, mock_uvc, mock_remote):
+        config = {
+            'nvr': 'foo',
+            'key': 'secret',
+        }
+        fake_cameras = [
+            {'uuid': 'one', 'name': 'Front'},
+            {'uuid': 'two', 'name': 'Back'},
+        ]
+        hass = mock.MagicMock()
+        add_devices = mock.MagicMock()
+        mock_remote.return_value.index.return_value = fake_cameras
+        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, 'one', 'Front'),
+            mock.call(mock_remote.return_value, 'two', 'Back'),
+        ])
+
+    def test_setup_incomplete_config(self):
+        self.assertFalse(uvc.setup_platform(
+            None, {'nvr': 'foo'}, None))
+        self.assertFalse(uvc.setup_platform(
+            None, {'key': 'secret'}, None))
+        self.assertFalse(uvc.setup_platform(
+            None, {'port': 'invalid'}, None))
+
+    @mock.patch('uvcclient.nvr.UVCRemote')
+    def test_setup_nvr_errors(self, mock_remote):
+        errors = [nvr.NotAuthorized, nvr.NvrError,
+                  requests.exceptions.ConnectionError]
+        config = {
+            'nvr': 'foo',
+            'key': 'secret',
+        }
+        for error in errors:
+            mock_remote.return_value.index.side_effect = error
+            self.assertFalse(uvc.setup_platform(None, config, None))
+
+
+class TestUVC(unittest.TestCase):
+    def setup_method(self, method):
+        self.nvr = mock.MagicMock()
+        self.uuid = 'uuid'
+        self.name = 'name'
+        self.uvc = uvc.UnifiVideoCamera(self.nvr, self.uuid, self.name)
+        self.nvr.get_camera.return_value = {
+            'model': 'UVC Fake',
+            'recordingSettings': {
+                'fullTimeRecordEnabled': True,
+            },
+            'host': 'host-a',
+            'internalHost': 'host-b',
+            'username': 'admin',
+        }
+
+    def test_properties(self):
+        self.assertEqual(self.name, self.uvc.name)
+        self.assertTrue(self.uvc.is_recording)
+        self.assertEqual('Ubiquiti', self.uvc.brand)
+        self.assertEqual('UVC Fake', self.uvc.model)
+
+    @mock.patch('uvcclient.store.get_info_store')
+    @mock.patch('uvcclient.camera.UVCCameraClient')
+    def test_login(self, mock_camera, mock_store):
+        mock_store.return_value.get_camera_password.return_value = 'seekret'
+        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.UVCCameraClient')
+    def test_login_no_password(self, mock_camera, mock_store):
+        mock_store.return_value.get_camera_password.return_value = None
+        self.uvc._login()
+        mock_camera.assert_called_once_with('host-a', 'admin', 'ubnt')
+        mock_camera.return_value.login.assert_called_once_with()
+
+    @mock.patch('uvcclient.store.get_info_store')
+    @mock.patch('uvcclient.camera.UVCCameraClient')
+    def test_login_tries_both_addrs_and_caches(self, mock_camera, mock_store):
+        responses = [0]
+
+        def fake_login(*a):
+            try:
+                responses.pop(0)
+                raise socket.error
+            except IndexError:
+                pass
+
+        mock_store.return_value.get_camera_password.return_value = None
+        mock_camera.return_value.login.side_effect = fake_login
+        self.uvc._login()
+        self.assertEqual(2, mock_camera.call_count)
+        self.assertEqual('host-b', self.uvc._connect_addr)
+
+        mock_camera.reset_mock()
+        self.uvc._login()
+        mock_camera.assert_called_once_with('host-b', 'admin', 'ubnt')
+        mock_camera.return_value.login.assert_called_once_with()
+
+    @mock.patch('uvcclient.store.get_info_store')
+    @mock.patch('uvcclient.camera.UVCCameraClient')
+    def test_login_fails_both_properly(self, mock_camera, mock_store):
+        mock_camera.return_value.login.side_effect = socket.error
+        self.assertEqual(None, self.uvc._login())
+        self.assertEqual(None, self.uvc._connect_addr)
+
+    def test_camera_image_tries_login_bails_on_failure(self):
+        with mock.patch.object(self.uvc, '_login') as mock_login:
+            mock_login.return_value = False
+            self.assertEqual(None, self.uvc.camera_image())
+            mock_login.assert_called_once_with()
+
+    def test_camera_image_logged_in(self):
+        self.uvc._camera = mock.MagicMock()
+        self.assertEqual(self.uvc._camera.get_snapshot.return_value,
+                         self.uvc.camera_image())
+
+    def test_camera_image_error(self):
+        self.uvc._camera = mock.MagicMock()
+        self.uvc._camera.get_snapshot.side_effect = camera.CameraConnectError
+        self.assertEqual(None, self.uvc.camera_image())
+
+    def test_camera_image_reauths(self):
+        responses = [0]
+
+        def fake_snapshot():
+            try:
+                responses.pop()
+                raise camera.CameraAuthError()
+            except IndexError:
+                pass
+            return 'image'
+
+        self.uvc._camera = mock.MagicMock()
+        self.uvc._camera.get_snapshot.side_effect = fake_snapshot
+        with mock.patch.object(self.uvc, '_login') as mock_login:
+            self.assertEqual('image', self.uvc.camera_image())
+            mock_login.assert_called_once_with()
+            self.assertEqual([], responses)
+
+    def test_camera_image_reauths_only_once(self):
+        self.uvc._camera = mock.MagicMock()
+        self.uvc._camera.get_snapshot.side_effect = camera.CameraAuthError
+        with mock.patch.object(self.uvc, '_login') as mock_login:
+            self.assertRaises(camera.CameraAuthError, self.uvc.camera_image)
+            mock_login.assert_called_once_with()