Skip to content

Commit

Permalink
Fix NVR communication by 'id' for version >= 3.2.x
Browse files Browse the repository at this point in the history
This makes us resolve names to the 'id' instead of 'uuid' for newer
versions of the NVR server software.

Fixes most of #1
  • Loading branch information
kk7ds committed Jun 4, 2016
1 parent 70ac477 commit 6a32dbe
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 2 deletions.
63 changes: 63 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ def setUp(self):
http_mock = mock.patch('http.client.HTTPConnection')
http_mock.start()
self._patches.append(http_mock)
bootstrap_mock = mock.patch.object(nvr.UVCRemote, '_get_bootstrap',
side_effect=self._bootstrap)
bootstrap_mock.start()
self._patches.append(bootstrap_mock)

def _bootstrap(self):
return {'systemInfo': {'version': '3.1.3'}}

def cleanUp(self):
for i in self._patches:
Expand Down Expand Up @@ -95,7 +102,63 @@ def test_uvc_request_deflated(self):
None, headers)


class TestClient32(unittest.TestCase):
@mock.patch.object(nvr.UVCRemote, '_get_bootstrap')
def test_bootstrap_server_version(self, mock_bootstrap):
mock_bootstrap.return_value = {'systemInfo': {'version': '3.4.5'}}
client = nvr.UVCRemote('foo', 7080, 'key')
self.assertEqual((3, 4, 5), client.server_version)

@mock.patch.object(nvr.UVCRemote, '_get_bootstrap')
@mock.patch.object(nvr.UVCRemote, 'index')
def test_310_returns_uuid(self, mock_index, mock_bootstrap):
mock_index.return_value = [{
'name': mock.sentinel.name,
'uuid': mock.sentinel.uuid,
'id': mock.sentinel.id,
}]
mock_bootstrap.return_value = {'systemInfo': {'version': '3.1.0'}}
client = nvr.UVCRemote('foo', 7080, 'key')
self.assertEqual(mock.sentinel.uuid, client.name_to_uuid(
mock.sentinel.name))

@mock.patch.object(nvr.UVCRemote, '_get_bootstrap')
@mock.patch.object(nvr.UVCRemote, 'index')
def test_320_returns_uuid(self, mock_index, mock_bootstrap):
mock_index.return_value = [{
'name': mock.sentinel.name,
'uuid': mock.sentinel.uuid,
'id': mock.sentinel.id,
}]
mock_bootstrap.return_value = {'systemInfo': {'version': '3.2.0'}}
client = nvr.UVCRemote('foo', 7080, 'key')
self.assertEqual(mock.sentinel.id, client.name_to_uuid(
mock.sentinel.name))


class TestClient(unittest.TestCase):
def setUp(self):
super(TestClient, self).setUp()
self._patches = []
try:
import httplib
http_mock = mock.patch('httplib.HTTPConnection')
except ImportError:
http_mock = mock.patch('http.client.HTTPConnection')
http_mock.start()
self._patches.append(http_mock)
bootstrap_mock = mock.patch.object(nvr.UVCRemote, '_get_bootstrap',
side_effect=self._bootstrap)
bootstrap_mock.start()
self._patches.append(bootstrap_mock)

def _bootstrap(self):
return {'systemInfo': {'version': '3.1.3'}}

def cleanUp(self):
for i in self._patches:
i.stop()

def test_set_recordmode(self):
fake_resp1 = {'data': [{'recordingSettings': {
'fullTimeRecordEnabled': False,
Expand Down
20 changes: 18 additions & 2 deletions uvcclient/nvr.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ def __init__(self, host, port, apikey, path='/'):
raise Invalid('Path not supported yet')
self._apikey = apikey
self._log = logging.getLogger('UVC(%s:%s)' % (host, port))
self._bootstrap = self._get_bootstrap()
version = '.'.join(str(x) for x in self.server_version)
self._log.debug('Server version is %s' % version)

@property
def server_version(self):
return tuple(
int(x) for x in self._bootstrap['systemInfo']['version'].split('.')
)

def _safe_request(self, *args, **kwargs):
try:
Expand Down Expand Up @@ -110,6 +119,9 @@ def _uvc_request_safe(self, path, method='GET', data=None,
data = zlib.decompress(data, 32 + zlib.MAX_WBITS)
return json.loads(data.decode())

def _get_bootstrap(self):
return self._uvc_request('/api/2.0/bootstrap')['data'][0]

def dump(self, uuid):
"""Dump information for a camera by UUID."""
data = self._uvc_request('/api/2.0/camera/%s' % uuid)
Expand Down Expand Up @@ -187,17 +199,21 @@ def index(self):
'uuid': x['uuid'],
'state': x['state'],
'managed': x['managed'],
'id': x['_id'],
} for x in cams]

def name_to_uuid(self, name):
"""Attempt to convert a camera name to its UUID.
:param name: Camera name
:returns: The UUID of the first camera with the same name if found,
otherwise None
otherwise None. On v3.2.0 and later, returns id.
"""
cameras = self.index()
cams_by_name = {x['name']: x['uuid'] for x in cameras}
if self.server_version >= (3, 2, 0):
cams_by_name = {x['name']: x['id'] for x in cameras}
else:
cams_by_name = {x['name']: x['uuid'] for x in cameras}
return cams_by_name.get(name)

def get_camera(self, uuid):
Expand Down

0 comments on commit 6a32dbe

Please sign in to comment.