From c918382d193b0eaeefad252bae32f72b9aa4978b Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Wed, 10 Jul 2024 12:57:21 -0600 Subject: [PATCH] Implement dispatching --- frigate/app.py | 4 + frigate/comms/firebase.py | 78 ++++++++++++++----- ...ssaging-sw.ts => firebase-messaging-sw.js} | 21 ++--- 3 files changed, 73 insertions(+), 30 deletions(-) rename web/public/{firebase-messaging-sw.ts => firebase-messaging-sw.js} (63%) diff --git a/frigate/app.py b/frigate/app.py index ef9360354..063d2471a 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -23,6 +23,7 @@ from frigate.api.app import create_app from frigate.api.auth import hash_password from frigate.comms.config_updater import ConfigPublisher from frigate.comms.dispatcher import Communicator, Dispatcher +from frigate.comms.firebase import FirebaseClient from frigate.comms.inter_process import InterProcessCommunicator from frigate.comms.mqtt import MqttClient from frigate.comms.ws import WebSocketClient @@ -401,6 +402,9 @@ class FrigateApp: if self.config.mqtt.enabled: comms.append(MqttClient(self.config)) + # TODO check if notifications are enabled + comms.append(FirebaseClient(self.config, self.stop_event)) + comms.append(WebSocketClient(self.config)) comms.append(self.inter_process_communicator) diff --git a/frigate/comms/firebase.py b/frigate/comms/firebase.py index 8946e95a5..b2f75058e 100644 --- a/frigate/comms/firebase.py +++ b/frigate/comms/firebase.py @@ -1,8 +1,13 @@ +"""Handle sending notifications for Frigate via Firebase.""" + +import json import logging +import threading +from multiprocessing.synchronize import Event as MpEvent from typing import Any, Callable import firebase_admin -from firebase_admin import messaging +from firebase_admin import credentials, messaging from frigate.comms.dispatcher import Communicator from frigate.config import FrigateConfig @@ -13,18 +18,9 @@ logger = logging.getLogger(__name__) class FirebaseClient(Communicator): # type: ignore[misc] """Frigate wrapper for firebase client.""" - def __init__(self, config: FrigateConfig) -> None: - firebase_admin.initialize_app( - options={ - "apiKey": "AIzaSyCoweRLtvai8iNwhsoT-GH_CH_0pckqMmA", - "authDomain": "frigate-ed674.firebaseapp.com", - "projectId": "frigate-ed674", - "storageBucket": "frigate-ed674.appspot.com", - "messagingSenderId": "76314288339", - "appId": "1:76314288339:web:090e170610d3bf0966f426", - "measurementId": "G-GZ1JKNDJZK", - } - ) + def __init__(self, config: FrigateConfig, stop_event: MpEvent) -> None: + self.messenger = FirebaseMessenger(config, stop_event) + self.messenger.start() def subscribe(self, receiver: Callable) -> None: """Wrapper for allowing dispatcher to subscribe.""" @@ -32,15 +28,55 @@ class FirebaseClient(Communicator): # type: ignore[misc] def publish(self, topic: str, payload: Any, retain: bool = False) -> None: """Wrapper for publishing when client is in valid state.""" + logger.info(f"got a message on {topic}") if topic == "reviews": - message = messaging.MulticastMessage( - notification=messaging.Notification( - title="Something happened", - body="There is a body", - ), - tokens=[], - ) - messaging.send_multicast(message) + self.messenger.send_message(json.loads(payload)) def stop(self) -> None: pass + + +class FirebaseMessenger(threading.Thread): + def __init__(self, config: FrigateConfig, stop_event: MpEvent) -> None: + threading.Thread.__init__(self) + self.name = "firebase_messenger" + self.config = config + self.stop_event = stop_event + + def send_message(self, payload: dict[str, any]) -> None: + state = payload["type"] + sorted_objects: set[str] = set() + + for obj in payload["after"]["data"]["objects"]: + if "-verified" not in obj: + sorted_objects.add(obj) + + sorted_objects.update(payload["after"]["data"]["sub_labels"]) + + title = f"{', '.join(sorted_objects).replace('_', ' ')}{' was' if state == 'end' else ''} detected in {', '.join(payload['after']['data']['zones']).replace('_', ' ')}" + logger.info(f"sending message with title {title}") + message = messaging.MulticastMessage( + notification=messaging.Notification( + title=f"{', '.join(sorted_objects).replace('_', ' ')}{' was' if state == 'end' else ''} detected in {', '.join(payload['after']['data']['zones']).replace('_', ' ')}", + body=f"Detected on {payload['after']['camera']}", + ), + tokens=[ + "cNNicZp6S92qn4kAVJnzd7:APA91bGv-MvDmNoZ2xqJTkPyCTmyv2WG0tfwIqWUuNtq3SXlpQJpdPCCjTEehOLDa0Yphv__KdxOQYEfaFvYfTW2qQevX-tSnRCVa_sJazQ_rfTervpo_zBVJD1T5GfYaY6kr41Wr_fP" + ], + ) + messaging.send_multicast(message) + + def run(self) -> None: + logger.info("Starting notifications setup") + + try: + firebase_admin.get_app() + except ValueError: + cred = credentials.Certificate("/config/firebase-priv-key.json") + firebase_admin.initialize_app(credential=cred) + + logger.info("finished notifications startup") + + while self.stop_event.wait(0.1): + # TODO check for a delete invalid tokens + pass diff --git a/web/public/firebase-messaging-sw.ts b/web/public/firebase-messaging-sw.js similarity index 63% rename from web/public/firebase-messaging-sw.ts rename to web/public/firebase-messaging-sw.js index 8737368b6..081448894 100644 --- a/web/public/firebase-messaging-sw.ts +++ b/web/public/firebase-messaging-sw.js @@ -1,10 +1,8 @@ // Give the service worker access to Firebase Messaging. // Note that you can only use Firebase Messaging here. Other Firebase libraries // are not available in the service worker. -importScripts("https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"); -importScripts( - "https://www.gstatic.com/firebasejs/8.10.1/firebase-messaging.js", -); +importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js'); +importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-messaging.js'); // Initialize the Firebase app in the service worker by passing in // your app's Firebase config object. @@ -21,13 +19,18 @@ firebase.initializeApp({ // Retrieve an instance of Firebase Messaging so that it can handle background // messages. +const messaging = firebase.messaging(); -messaging.onBackgroundMessage(function (payload) { - console.log("Received background message ", payload); - - const notificationTitle = payload.notification.title; +messaging.onBackgroundMessage((payload) => { + console.log( + '[firebase-messaging-sw.js] Received background message ', + payload + ); + // Customize notification here + const notificationTitle = 'Background Message Title'; const notificationOptions = { - body: payload.notification.body, + body: 'Background Message body.', + icon: '/images/maskable-icon.png', }; self.registration.showNotification(notificationTitle, notificationOptions);