From e99e939cd01bdc21798ee37dc2d575af3f4c6b33 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 20 Feb 2020 18:47:43 +0000 Subject: [PATCH 1/2] Add ability to update doorbell images from activity * Fix tests for doors when timezone is not utc * Add check for standby status to doorbells so they do not unexpectedly get marked unavailable --- august/activity.py | 22 ++++- august/doorbell.py | 34 +++++++ august/util.py | 24 +++++ setup.py | 2 +- tests/fixtures/doorbell_motion_activity.json | 56 +++++++++++ .../doorbell_motion_activity_no_image.json | 38 ++++++++ .../doorbell_motion_activity_old.json | 56 +++++++++++ .../doorbell_motion_activity_wrong.json | 56 +++++++++++ .../fixtures/get_doorbell_missing_image.json | 65 +++++++++++++ tests/test_api.py | 55 +++++++++-- tests/test_util.py | 94 ++++++++++++++++++- 11 files changed, 486 insertions(+), 16 deletions(-) create mode 100644 tests/fixtures/doorbell_motion_activity.json create mode 100644 tests/fixtures/doorbell_motion_activity_no_image.json create mode 100644 tests/fixtures/doorbell_motion_activity_old.json create mode 100644 tests/fixtures/doorbell_motion_activity_wrong.json create mode 100644 tests/fixtures/get_doorbell_missing_image.json diff --git a/august/activity.py b/august/activity.py index 170f1e8..ac44968 100644 --- a/august/activity.py +++ b/august/activity.py @@ -1,5 +1,6 @@ from datetime import datetime from enum import Enum +import dateutil.parser from august.lock import LockDoorStatus, LockStatus @@ -13,10 +14,17 @@ ACTION_DOORBELL_CALL_MISSED = "doorbell_call_missed" ACTION_DOORBELL_CALL_HANGUP = "doorbell_call_hangup" -ACTIVITY_ACTIONS_DOORBELL_DING = [ACTION_DOORBELL_CALL_MISSED, ACTION_DOORBELL_CALL_HANGUP] +ACTIVITY_ACTIONS_DOORBELL_DING = [ + ACTION_DOORBELL_CALL_MISSED, + ACTION_DOORBELL_CALL_HANGUP, +] ACTIVITY_ACTIONS_DOORBELL_MOTION = [ACTION_DOORBELL_MOTION_DETECTED] ACTIVITY_ACTIONS_DOORBELL_VIEW = [ACTION_DOORBELL_CALL_INITIATED] -ACTIVITY_ACTIONS_LOCK_OPERATION = [ACTION_LOCK_LOCK, ACTION_LOCK_UNLOCK, ACTION_LOCK_ONETOUCHLOCK] +ACTIVITY_ACTIONS_LOCK_OPERATION = [ + ACTION_LOCK_LOCK, + ACTION_LOCK_UNLOCK, + ACTION_LOCK_ONETOUCHLOCK, +] ACTIVITY_ACTIONS_DOOR_OPERATION = [ACTION_DOOR_CLOSED, ACTION_DOOR_OPEN] ACTIVITY_ACTION_STATES = { @@ -97,11 +105,18 @@ def __init__(self, data): image = data.get("info", {}).get("image") self._image_url = None if image is None else image.get("secure_url") + self._image_created_at_datetime = None + if image is not None and "created_at" in image: + self._image_created_at_datetime = dateutil.parser.parse(image["created_at"]) @property def image_url(self): return self._image_url + @property + def image_created_at_datetime(self): + return self._image_created_at_datetime + class DoorbellDingActivity(Activity): def __init__(self, data): @@ -153,8 +168,7 @@ def __init__(self, data): calling_user = data.get("callingUser", {}) self._operated_by = "{} {}".format( - calling_user.get("FirstName"), - calling_user.get("LastName"), + calling_user.get("FirstName"), calling_user.get("LastName"), ) @property diff --git a/august/doorbell.py b/august/doorbell.py index e1e35f4..dc10cff 100644 --- a/august/doorbell.py +++ b/august/doorbell.py @@ -1,3 +1,7 @@ +import datetime + +import dateutil.parser + from august.device import Device, DeviceDetail @@ -18,6 +22,10 @@ def serial_number(self): def status(self): return self._status + @property + def is_standby(self): + return self.status == "standby" + @property def is_online(self): return self.status == "doorbell_call_status_online" @@ -50,6 +58,12 @@ def __init__(self, data): recent_image = data.get("recentImage", {}) self._image_url = recent_image.get("secure_url", None) self._has_subscription = data.get("dvrSubscriptionSetupDone", False) + self._image_created_at_datetime = None + + if "created_at" in recent_image: + self._image_created_at_datetime = dateutil.parser.parse( + recent_image["created_at"] + ) self._battery_level = None if "telemetry" in data: @@ -63,10 +77,30 @@ def status(self): def is_online(self): return self.status == "doorbell_call_status_online" + @property + def is_standby(self): + return self.status == "standby" + + @property + def image_created_at_datetime(self): + return self._image_created_at_datetime + + @image_created_at_datetime.setter + def image_created_at_datetime(self, var): + """Update the doorbell image created_at datetime (usually form the activity log).""" + if not isinstance(var, datetime.date): + raise ValueError + self._image_created_at_datetime = var + @property def image_url(self): return self._image_url + @image_url.setter + def image_url(self, var): + """Update the doorbell image url (usually form the activity log).""" + self._image_url = var + @property def battery_level(self): return self._battery_level diff --git a/august/util.py b/august/util.py index bd92e16..1902bf2 100644 --- a/august/util.py +++ b/august/util.py @@ -2,6 +2,7 @@ from august.activity import ( ACTIVITY_ACTION_STATES, + DoorbellMotionActivity, DoorOperationActivity, LockOperationActivity, ) @@ -28,6 +29,29 @@ def update_lock_detail_from_activity(lock_detail, activity): return True +def update_doorbell_image_from_activity(doorbell_detail, activity): + """Update the DoorDetail from an activity with a new image.""" + if activity.device_id != doorbell_detail.device_id: + raise ValueError + if isinstance(activity, DoorbellMotionActivity): + if activity.image_created_at_datetime is None: + return False + + if (doorbell_detail.image_created_at_datetime is None + or doorbell_detail.image_created_at_datetime + < activity.image_created_at_datetime): + doorbell_detail.image_url = activity.image_url + doorbell_detail.image_created_at_datetime = ( + activity.image_created_at_datetime + ) + else: + return False + else: + raise ValueError + + return True + + def as_utc_from_local(dtime): """Converts the datetime returned from an activity to UTC.""" return dtime.astimezone(tz=datetime.timezone.utc) diff --git a/setup.py b/setup.py index b3691c5..7d9285a 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='py-august', - version='0.17.0', + version='0.18.0', packages=['august'], url='https://github.com/snjoetw/py-august', license='MIT', diff --git a/tests/fixtures/doorbell_motion_activity.json b/tests/fixtures/doorbell_motion_activity.json new file mode 100644 index 0000000..b37a5f7 --- /dev/null +++ b/tests/fixtures/doorbell_motion_activity.json @@ -0,0 +1,56 @@ +{ + "action" : "doorbell_motion_detected", + "callingUser" : { + "FirstName" : "Unknown", + "LastName" : "User", + "PhoneNo" : "deleted", + "UserID" : "deleted", + "UserName" : "deleteduser" + }, + "dateTime" : 1582220686158, + "deviceID" : "K98GiDT45GUL", + "deviceName" : "Front Door", + "deviceType" : "doorbell", + "entities" : { + "activity" : "any", + "callingUser" : "deleted", + "device" : "K98GiDT45GUL", + "house" : "any", + "otherUser" : "deleted" + }, + "house" : { + "houseID" : "any", + "houseName" : "any" + }, + "info" : { + "dvrID" : "any", + "hasSubscription" : false, + "image" : { + "bytes" : 42865, + "created_at" : "2020-02-20T17:44:45Z", + "etag" : "andy", + "format" : "jpg", + "height" : 576, + "original_filename" : "file", + "placeholder" : false, + "public_id" : "any", + "resource_type" : "image", + "secure_url" : "https://my.updated.image/image.jpg", + "signature" : "abc", + "tags" : [], + "type" : "upload", + "url" : "http://my.updated.image/image.jpg", + "version" : 1582220685, + "width" : 720 + }, + "videoAvailable" : true, + "videoUploadProgress" : "complete" + }, + "otherUser" : { + "FirstName" : "Unknown", + "LastName" : "User", + "PhoneNo" : "deleted", + "UserID" : "deleted", + "UserName" : "deleteduser" + } +} diff --git a/tests/fixtures/doorbell_motion_activity_no_image.json b/tests/fixtures/doorbell_motion_activity_no_image.json new file mode 100644 index 0000000..b4edc48 --- /dev/null +++ b/tests/fixtures/doorbell_motion_activity_no_image.json @@ -0,0 +1,38 @@ +{ + "action" : "doorbell_motion_detected", + "callingUser" : { + "FirstName" : "Unknown", + "LastName" : "User", + "PhoneNo" : "deleted", + "UserID" : "deleted", + "UserName" : "deleteduser" + }, + "dateTime" : 1582220686158, + "deviceID" : "K98GiDT45GUL", + "deviceName" : "Front Door", + "deviceType" : "doorbell", + "entities" : { + "activity" : "any", + "callingUser" : "deleted", + "device" : "K98GiDT45GUL", + "house" : "any", + "otherUser" : "deleted" + }, + "house" : { + "houseID" : "any", + "houseName" : "any" + }, + "info" : { + "dvrID" : "any", + "hasSubscription" : false, + "videoAvailable" : true, + "videoUploadProgress" : "complete" + }, + "otherUser" : { + "FirstName" : "Unknown", + "LastName" : "User", + "PhoneNo" : "deleted", + "UserID" : "deleted", + "UserName" : "deleteduser" + } +} diff --git a/tests/fixtures/doorbell_motion_activity_old.json b/tests/fixtures/doorbell_motion_activity_old.json new file mode 100644 index 0000000..3eb2338 --- /dev/null +++ b/tests/fixtures/doorbell_motion_activity_old.json @@ -0,0 +1,56 @@ +{ + "action" : "doorbell_motion_detected", + "callingUser" : { + "FirstName" : "Unknown", + "LastName" : "User", + "PhoneNo" : "deleted", + "UserID" : "deleted", + "UserName" : "deleteduser" + }, + "dateTime" : 1482220686158, + "deviceID" : "K98GiDT45GUL", + "deviceName" : "Front Door", + "deviceType" : "doorbell", + "entities" : { + "activity" : "any", + "callingUser" : "deleted", + "device" : "K98GiDT45GUL", + "house" : "any", + "otherUser" : "deleted" + }, + "house" : { + "houseID" : "any", + "houseName" : "any" + }, + "info" : { + "dvrID" : "any", + "hasSubscription" : false, + "image" : { + "bytes" : 42865, + "created_at" : "2020-02-20T17:44:45Z", + "etag" : "andy", + "format" : "jpg", + "height" : 576, + "original_filename" : "file", + "placeholder" : false, + "public_id" : "any", + "resource_type" : "image", + "secure_url" : "https://this.is.the.old.url/should_not_take.jpg", + "signature" : "abc", + "tags" : [], + "type" : "upload", + "url" : "http://this.is.the.old.url/should_not_take.jpg", + "version" : 1482220685, + "width" : 720 + }, + "videoAvailable" : true, + "videoUploadProgress" : "complete" + }, + "otherUser" : { + "FirstName" : "Unknown", + "LastName" : "User", + "PhoneNo" : "deleted", + "UserID" : "deleted", + "UserName" : "deleteduser" + } +} diff --git a/tests/fixtures/doorbell_motion_activity_wrong.json b/tests/fixtures/doorbell_motion_activity_wrong.json new file mode 100644 index 0000000..6ec2b18 --- /dev/null +++ b/tests/fixtures/doorbell_motion_activity_wrong.json @@ -0,0 +1,56 @@ +{ + "action" : "doorbell_motion_detected", + "callingUser" : { + "FirstName" : "Unknown", + "LastName" : "User", + "PhoneNo" : "deleted", + "UserID" : "deleted", + "UserName" : "deleteduser" + }, + "dateTime" : 1582220686158, + "deviceID" : "thisisthewrongdeviceid", + "deviceName" : "Front Door", + "deviceType" : "doorbell", + "entities" : { + "activity" : "any", + "callingUser" : "deleted", + "device" : "thisisthewrongdeviceid", + "house" : "any", + "otherUser" : "deleted" + }, + "house" : { + "houseID" : "any", + "houseName" : "any" + }, + "info" : { + "dvrID" : "any", + "hasSubscription" : false, + "image" : { + "bytes" : 42865, + "created_at" : "2020-02-20T17:44:45Z", + "etag" : "andy", + "format" : "jpg", + "height" : 576, + "original_filename" : "file", + "placeholder" : false, + "public_id" : "any", + "resource_type" : "image", + "secure_url" : "https://my.updated.image/image.jpg", + "signature" : "abc", + "tags" : [], + "type" : "upload", + "url" : "http://my.updated.image/image.jpg", + "version" : 1582220685, + "width" : 720 + }, + "videoAvailable" : true, + "videoUploadProgress" : "complete" + }, + "otherUser" : { + "FirstName" : "Unknown", + "LastName" : "User", + "PhoneNo" : "deleted", + "UserID" : "deleted", + "UserName" : "deleteduser" + } +} diff --git a/tests/fixtures/get_doorbell_missing_image.json b/tests/fixtures/get_doorbell_missing_image.json new file mode 100644 index 0000000..86282ef --- /dev/null +++ b/tests/fixtures/get_doorbell_missing_image.json @@ -0,0 +1,65 @@ +{ + "doorbellID": "K98GiDT45GUL", + "serialNumber": "tBXZR0Z35E", + "appID": "august-iphone", + "installUserID": "c3b2a94e-373e-aaaa-bbbb-36e996827777", + "name": "Front Door", + "installDate": "2016-11-26T22:27:11.176Z", + "pubsubChannel": "7c7a6672-59c8-3333-ffff-dcd98705cccc", + "settings": { + "speakerVolume": 92, + "micVolume": 100, + "IREnabled": true, + "debug": false, + "initialBitrate": 384000, + "bitrateCeiling": 512000, + "ABREnabled": true, + "JPGQuality": 70, + "IVAEnabled": false, + "batteryLowThreshold": 3.1, + "batteryUseThreshold": 3.4, + "directLink": true, + "irConfiguration": 8448272, + "ringSoundEnabled": true, + "batteryRun": false, + "videoResolution": "640x480", + "minACNoScaling": 40, + "turnOffCamera": false, + "overlayEnabled": true, + "keepEncoderRunning": true, + "motion_notifications": true, + "notify_when_offline": true, + "buttonpush_notifications": true + }, + "createdAt": "2016-11-26T22:27:11.176Z", + "updatedAt": "2017-12-10T08:05:13.650Z", + "status": "doorbell_call_status_online", + "telemetry": { + "date": "2017-12-10 08:05:12", + "BSSID": "88:ee:00:dd:aa:11", + "SSID": "foo_ssid", + "wifi_freq": 5745, + "ip_addr": "10.0.1.11", + "link_quality": 54, + "signal_level": -56, + "uptime": "16168.75 13830.49", + "load_average": "0.50 0.47 0.35 1/154 9345", + "battery_soc": 96, + "battery_soh": 95, + "temperature": 28.25, + "steady_ac_in": 22.196405, + "battery": 4.061763, + "ac_in": 23.856874, + "doorbell_low_battery": false, + "updated_at": "2017-12-10T08:05:13.650Z" + }, + "doorbellServerURL": "https://doorbells.august.com", + "caps": [ + "reconnect" + ], + "dvrSubscriptionSetupDone": true, + "status_timestamp": 1512811834532, + "LockID": "BBBB1F5F11114C24CCCC97571DD6AAAA", + "firmwareVersion": "2.3.0-RC153+201711151527", + "HouseID": "3dd2accaea08" +} diff --git a/tests/test_api.py b/tests/test_api.py index 19681da..992ba6b 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -4,7 +4,7 @@ import requests_mock import dateutil.parser -from dateutil.tz import tzutc +from dateutil.tz import tzutc, tzlocal from requests.exceptions import HTTPError from requests.models import Response from requests.structures import CaseInsensitiveDict @@ -90,11 +90,40 @@ def test_get_doorbell_detail(self, mock): self.assertEqual("doorbell_call_status_online", doorbell.status) self.assertEqual(96, doorbell.battery_level) self.assertEqual(True, doorbell.is_online) + self.assertEqual(False, doorbell.is_standby) + self.assertEqual( + dateutil.parser.parse("2017-12-10T08:01:35Z"), + doorbell.image_created_at_datetime, + ) self.assertEqual(True, doorbell.has_subscription) self.assertEqual( "https://image.com/vmk16naaaa7ibuey7sar.jpg", doorbell.image_url ) + @requests_mock.Mocker() + def test_get_doorbell_detail_missing_image(self, mock): + mock.register_uri( + "get", + API_GET_DOORBELL_URL.format(doorbell_id="K98GiDT45GUL"), + text=load_fixture("get_doorbell_missing_image.json"), + ) + + api = Api() + doorbell = api.get_doorbell_detail(ACCESS_TOKEN, "K98GiDT45GUL") + + self.assertEqual("K98GiDT45GUL", doorbell.device_id) + self.assertEqual("Front Door", doorbell.device_name) + self.assertEqual("3dd2accaea08", doorbell.house_id) + self.assertEqual("tBXZR0Z35E", doorbell.serial_number) + self.assertEqual("2.3.0-RC153+201711151527", doorbell.firmware_version) + self.assertEqual("doorbell_call_status_online", doorbell.status) + self.assertEqual(96, doorbell.battery_level) + self.assertEqual(True, doorbell.is_online) + self.assertEqual(False, doorbell.is_standby) + self.assertEqual(None, doorbell.image_created_at_datetime) + self.assertEqual(True, doorbell.has_subscription) + self.assertEqual(None, doorbell.image_url) + @requests_mock.Mocker() def test_get_locks(self, mock): mock.register_uri("get", API_GET_LOCKS_URL, text=load_fixture("get_locks.json")) @@ -420,8 +449,10 @@ def test_lock_return_activities_from_fixture(self, mock): api = Api() activities = api.lock_return_activities(ACCESS_TOKEN, lock_id) - expected_lock_dt = dateutil.parser.parse("2020-02-19T19:44:54.371Z").replace( - tzinfo=None + expected_lock_dt = ( + dateutil.parser.parse("2020-02-19T19:44:54.371Z") + .astimezone(tz=tzlocal()) + .replace(tzinfo=None) ) self.assertEqual(len(activities), 2) @@ -449,8 +480,10 @@ def test_unlock_return_activities_from_fixture(self, mock): api = Api() activities = api.unlock_return_activities(ACCESS_TOKEN, lock_id) - expected_unlock_dt = dateutil.parser.parse("2020-02-19T19:44:26.745Z").replace( - tzinfo=None + expected_unlock_dt = ( + dateutil.parser.parse("2020-02-19T19:44:26.745Z") + .astimezone(tz=tzlocal()) + .replace(tzinfo=None) ) self.assertEqual(len(activities), 2) @@ -478,8 +511,10 @@ def test_lock_return_activities_from_fixture_with_no_doorstate(self, mock): api = Api() activities = api.lock_return_activities(ACCESS_TOKEN, lock_id) - expected_lock_dt = dateutil.parser.parse("2020-02-19T19:44:54.371Z").replace( - tzinfo=None + expected_lock_dt = ( + dateutil.parser.parse("2020-02-19T19:44:54.371Z") + .astimezone(tz=tzlocal()) + .replace(tzinfo=None) ) self.assertEqual(len(activities), 1) @@ -501,8 +536,10 @@ def test_unlock_return_activities_from_fixture_with_no_doorstate(self, mock): api = Api() activities = api.unlock_return_activities(ACCESS_TOKEN, lock_id) - expected_unlock_dt = dateutil.parser.parse("2020-02-19T19:44:26.745Z").replace( - tzinfo=None + expected_unlock_dt = ( + dateutil.parser.parse("2020-02-19T19:44:26.745Z") + .astimezone(tz=tzlocal()) + .replace(tzinfo=None) ) self.assertEqual(len(activities), 1) diff --git a/tests/test_util.py b/tests/test_util.py index 00ff8de..9a40a93 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -5,9 +5,18 @@ import dateutil.parser import datetime -from august.activity import DoorOperationActivity, LockOperationActivity +from august.activity import ( + DoorOperationActivity, + LockOperationActivity, + DoorbellMotionActivity, +) from august.lock import LockDetail, LockDoorStatus, LockStatus -from august.util import update_lock_detail_from_activity, as_utc_from_local +from august.doorbell import DoorbellDetail +from august.util import ( + update_lock_detail_from_activity, + as_utc_from_local, + update_doorbell_image_from_activity, +) from august.api import _convert_lock_result_to_activities @@ -116,3 +125,84 @@ def test_update_lock_with_activity(self): self.assertTrue(update_lock_detail_from_activity(lock, activity)) self.assertEqual(LockDoorStatus.CLOSED, lock.door_state) self.assertEqual(LockStatus.UNLOCKED, lock.lock_status) + + +class TestDetail(unittest.TestCase): + def test_update_doorbell_image_from_activity(self): + doorbell = DoorbellDetail(json.loads(load_fixture("get_doorbell.json"))) + self.assertEqual("K98GiDT45GUL", doorbell.device_id) + self.assertEqual( + dateutil.parser.parse("2017-12-10T08:01:35Z"), + doorbell.image_created_at_datetime, + ) + self.assertEqual( + "https://image.com/vmk16naaaa7ibuey7sar.jpg", doorbell.image_url + ) + doorbell_motion_activity_no_image = DoorbellMotionActivity( + json.loads(load_fixture("doorbell_motion_activity_no_image.json")) + ) + self.assertFalse( + update_doorbell_image_from_activity( + doorbell, doorbell_motion_activity_no_image + ) + ) + doorbell_motion_activity = DoorbellMotionActivity( + json.loads(load_fixture("doorbell_motion_activity.json")) + ) + self.assertTrue( + update_doorbell_image_from_activity(doorbell, doorbell_motion_activity) + ) + self.assertEqual( + dateutil.parser.parse("2020-02-20T17:44:45Z"), + doorbell.image_created_at_datetime, + ) + self.assertEqual("https://my.updated.image/image.jpg", doorbell.image_url) + old_doorbell_motion_activity = DoorbellMotionActivity( + json.loads(load_fixture("doorbell_motion_activity_old.json")) + ) + # returns false we send an older activity + self.assertFalse( + update_doorbell_image_from_activity(doorbell, old_doorbell_motion_activity) + ) + self.assertEqual( + dateutil.parser.parse("2020-02-20T17:44:45Z"), + doorbell.image_created_at_datetime, + ) + self.assertEqual("https://my.updated.image/image.jpg", doorbell.image_url) + wrong_doorbell_motion_activity = DoorbellMotionActivity( + json.loads(load_fixture("doorbell_motion_activity_wrong.json")) + ) + + with self.assertRaises(ValueError): + update_doorbell_image_from_activity( + doorbell, wrong_doorbell_motion_activity + ) + + def test_update_doorbell_image_from_activity_missing_image_at_start(self): + doorbell = DoorbellDetail( + json.loads(load_fixture("get_doorbell_missing_image.json")) + ) + self.assertEqual("K98GiDT45GUL", doorbell.device_id) + self.assertEqual( + None, doorbell.image_created_at_datetime, + ) + self.assertEqual(None, doorbell.image_url) + doorbell_motion_activity_no_image = DoorbellMotionActivity( + json.loads(load_fixture("doorbell_motion_activity_no_image.json")) + ) + self.assertFalse( + update_doorbell_image_from_activity( + doorbell, doorbell_motion_activity_no_image + ) + ) + doorbell_motion_activity = DoorbellMotionActivity( + json.loads(load_fixture("doorbell_motion_activity.json")) + ) + self.assertTrue( + update_doorbell_image_from_activity(doorbell, doorbell_motion_activity) + ) + self.assertEqual( + dateutil.parser.parse("2020-02-20T17:44:45Z"), + doorbell.image_created_at_datetime, + ) + self.assertEqual("https://my.updated.image/image.jpg", doorbell.image_url) From 1e86023f9254699bd3bb994cace79b775f2d9d1f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 22 Feb 2020 00:57:55 +0000 Subject: [PATCH 2/2] Add support for fetching the doorbell image Homeassistant wants all requests handled in the py-august module --- august/doorbell.py | 4 ++++ setup.py | 2 +- tests/test_api.py | 8 +++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/august/doorbell.py b/august/doorbell.py index dc10cff..54a3147 100644 --- a/august/doorbell.py +++ b/august/doorbell.py @@ -1,6 +1,7 @@ import datetime import dateutil.parser +import requests from august.device import Device, DeviceDetail @@ -108,3 +109,6 @@ def battery_level(self): @property def has_subscription(self): return self._has_subscription + + def get_doorbell_image(self, timeout=10): + return requests.get(self._image_url, timeout=timeout).content diff --git a/setup.py b/setup.py index 7d9285a..cdea0a2 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='py-august', - version='0.18.0', + version='0.19.0', packages=['august'], url='https://github.com/snjoetw/py-august', license='MIT', diff --git a/tests/test_api.py b/tests/test_api.py index 992ba6b..b77c945 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -73,11 +73,15 @@ def test_get_doorbells(self, mock): @requests_mock.Mocker() def test_get_doorbell_detail(self, mock): + expected_doorbell_image_url = "https://image.com/vmk16naaaa7ibuey7sar.jpg" mock.register_uri( "get", API_GET_DOORBELL_URL.format(doorbell_id="K98GiDT45GUL"), text=load_fixture("get_doorbell.json"), ) + mock.register_uri( + "get", expected_doorbell_image_url, text="doorbell_image_mocked" + ) api = Api() doorbell = api.get_doorbell_detail(ACCESS_TOKEN, "K98GiDT45GUL") @@ -96,8 +100,10 @@ def test_get_doorbell_detail(self, mock): doorbell.image_created_at_datetime, ) self.assertEqual(True, doorbell.has_subscription) + self.assertEqual(expected_doorbell_image_url, doorbell.image_url) + self.assertEqual(doorbell.get_doorbell_image(), b"doorbell_image_mocked") self.assertEqual( - "https://image.com/vmk16naaaa7ibuey7sar.jpg", doorbell.image_url + doorbell.get_doorbell_image(timeout=50), b"doorbell_image_mocked" ) @requests_mock.Mocker()