Implement dispatching

This commit is contained in:
Nicolas Mowen 2024-07-10 12:57:21 -06:00
parent 0fd1ad0537
commit c918382d19
3 changed files with 73 additions and 30 deletions

View File

@ -23,6 +23,7 @@ from frigate.api.app import create_app
from frigate.api.auth import hash_password from frigate.api.auth import hash_password
from frigate.comms.config_updater import ConfigPublisher from frigate.comms.config_updater import ConfigPublisher
from frigate.comms.dispatcher import Communicator, Dispatcher from frigate.comms.dispatcher import Communicator, Dispatcher
from frigate.comms.firebase import FirebaseClient
from frigate.comms.inter_process import InterProcessCommunicator from frigate.comms.inter_process import InterProcessCommunicator
from frigate.comms.mqtt import MqttClient from frigate.comms.mqtt import MqttClient
from frigate.comms.ws import WebSocketClient from frigate.comms.ws import WebSocketClient
@ -401,6 +402,9 @@ class FrigateApp:
if self.config.mqtt.enabled: if self.config.mqtt.enabled:
comms.append(MqttClient(self.config)) 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(WebSocketClient(self.config))
comms.append(self.inter_process_communicator) comms.append(self.inter_process_communicator)

View File

@ -1,8 +1,13 @@
"""Handle sending notifications for Frigate via Firebase."""
import json
import logging import logging
import threading
from multiprocessing.synchronize import Event as MpEvent
from typing import Any, Callable from typing import Any, Callable
import firebase_admin import firebase_admin
from firebase_admin import messaging from firebase_admin import credentials, messaging
from frigate.comms.dispatcher import Communicator from frigate.comms.dispatcher import Communicator
from frigate.config import FrigateConfig from frigate.config import FrigateConfig
@ -13,18 +18,9 @@ logger = logging.getLogger(__name__)
class FirebaseClient(Communicator): # type: ignore[misc] class FirebaseClient(Communicator): # type: ignore[misc]
"""Frigate wrapper for firebase client.""" """Frigate wrapper for firebase client."""
def __init__(self, config: FrigateConfig) -> None: def __init__(self, config: FrigateConfig, stop_event: MpEvent) -> None:
firebase_admin.initialize_app( self.messenger = FirebaseMessenger(config, stop_event)
options={ self.messenger.start()
"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 subscribe(self, receiver: Callable) -> None: def subscribe(self, receiver: Callable) -> None:
"""Wrapper for allowing dispatcher to subscribe.""" """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: def publish(self, topic: str, payload: Any, retain: bool = False) -> None:
"""Wrapper for publishing when client is in valid state.""" """Wrapper for publishing when client is in valid state."""
logger.info(f"got a message on {topic}")
if topic == "reviews": if topic == "reviews":
message = messaging.MulticastMessage( self.messenger.send_message(json.loads(payload))
notification=messaging.Notification(
title="Something happened",
body="There is a body",
),
tokens=[],
)
messaging.send_multicast(message)
def stop(self) -> None: def stop(self) -> None:
pass 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

View File

@ -1,10 +1,8 @@
// Give the service worker access to Firebase Messaging. // Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here. Other Firebase libraries // Note that you can only use Firebase Messaging here. Other Firebase libraries
// are not available in the service worker. // 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-app.js');
importScripts( importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-messaging.js');
"https://www.gstatic.com/firebasejs/8.10.1/firebase-messaging.js",
);
// Initialize the Firebase app in the service worker by passing in // Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object. // your app's Firebase config object.
@ -21,13 +19,18 @@ firebase.initializeApp({
// Retrieve an instance of Firebase Messaging so that it can handle background // Retrieve an instance of Firebase Messaging so that it can handle background
// messages. // messages.
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) { messaging.onBackgroundMessage((payload) => {
console.log("Received background message ", payload); console.log(
'[firebase-messaging-sw.js] Received background message ',
const notificationTitle = payload.notification.title; payload
);
// Customize notification here
const notificationTitle = 'Background Message Title';
const notificationOptions = { const notificationOptions = {
body: payload.notification.body, body: 'Background Message body.',
icon: '/images/maskable-icon.png',
}; };
self.registration.showNotification(notificationTitle, notificationOptions); self.registration.showNotification(notificationTitle, notificationOptions);