From f235472940550fb242e797056435a2a4e28ce67a Mon Sep 17 00:00:00 2001 From: Steven B <51370195+sdb9696@users.noreply.github.com> Date: Tue, 12 Nov 2024 11:58:23 +0000 Subject: [PATCH 1/2] Add is_update flag to ring events for updated events --- ring_doorbell/event.py | 19 ++++++++++++++++++- ring_doorbell/listen/eventlistener.py | 25 +++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/ring_doorbell/event.py b/ring_doorbell/event.py index 3733c037..3f4aa66a 100644 --- a/ring_doorbell/event.py +++ b/ring_doorbell/event.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any +from typing import Any, NamedTuple @dataclass @@ -18,6 +18,7 @@ class RingEvent: expires_in: float kind: str state: str + is_update: bool = False def __getitem__(self, key: str) -> Any: """Get a value by string.""" @@ -26,3 +27,19 @@ def __getitem__(self, key: str) -> Any: def get(self, key: str) -> Any | None: """Get a value by string and return None if not present.""" return getattr(self, key) if hasattr(self, key) else None + + def get_key(self) -> RingEventKey: + """Return the identificationkey for the event.""" + return RingEventKey(self.id, self.doorbot_id, self.kind, self.now) + + +class RingEventKey(NamedTuple): + """Class to identify an event. + + Used for determining if messages are updates to events. + """ + + id: int + doorbot_id: int + kind: str + now: float diff --git a/ring_doorbell/listen/eventlistener.py b/ring_doorbell/listen/eventlistener.py index cef0ec03..660b91c9 100644 --- a/ring_doorbell/listen/eventlistener.py +++ b/ring_doorbell/listen/eventlistener.py @@ -28,7 +28,7 @@ PUSH_NOTIFICATION_KINDS, SUBSCRIPTION_ENDPOINT, ) -from ring_doorbell.event import RingEvent +from ring_doorbell.event import RingEvent, RingEventKey from ring_doorbell.exceptions import RingError from ring_doorbell.util import parse_datetime @@ -81,6 +81,8 @@ def __init__( self.session_refresh_task: asyncio.Task | None = None self.fcm_token: str | None = None + self._seen_events: set[RingEventKey] = set() + def _credentials_updated_cb(self, creds: dict[str, Any]) -> None: self._credentials = creds if self._credentials_updated_callback: @@ -263,6 +265,24 @@ def _get_intercom_unlock_event(self, gcm_data: dict[str, Any]) -> RingEvent | No state="unlock", ) + def _check_is_update(self, ring_event: RingEvent) -> None: + """Battery doorbells send two events. + + First without an image and the second with an image. + """ + now = time.time() + seen_events = { + key + for key in self._seen_events + if (now - key.now) < DEFAULT_LISTEN_EVENT_EXPIRES_IN + } + event_key = ring_event.get_key() + if event_key in seen_events: + ring_event.is_update = True + else: + seen_events.add(event_key) + self._seen_events = seen_events + def _on_notification( self, notification: dict[str, dict[str, str]], @@ -277,6 +297,7 @@ def _on_notification( ring_event = self._get_ring_event(msg_data) if ring_event: + self._check_is_update(ring_event) _logger.debug("Event received %s", ring_event) for callback in self._callbacks.values(): callback(ring_event) @@ -299,7 +320,7 @@ def _get_ring_event(self, msg_data: dict) -> RingEvent | None: event_kind = PUSH_NOTIFICATION_KINDS.get(event_category, "Unknown") device = data["device"] event = data["event"] - event_id = event["ding"]["id"] + event_id = int(event["ding"]["id"]) created_at = event["ding"]["created_at"] create_seconds = parse_datetime(created_at).timestamp() return RingEvent( From 5bf39df8698a3539eb989a9c34c30d1c9d5af30b Mon Sep 17 00:00:00 2001 From: Steven B <51370195+sdb9696@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:02:26 +0000 Subject: [PATCH 2/2] Fix tests --- tests/test_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 6bb11c03..84012237 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -263,7 +263,7 @@ async def test_listen_event_handler(mocker, auth): "2023-10-24 09:42:18.789709: RingEvent(id=12345678901234, " "doorbot_id=12345678, device_name='Front Floodcam'" ", device_kind='floodlight_v2', now=1698140538.789709," - " expires_in=180, kind='motion', state='human') : " + " expires_in=180, kind='motion', state='human', is_update=False) : " "Currently active count = 1" ) echomock.assert_called_with(exp)