mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-13 06:35:24 +03:00
Implement webpush from server
This commit is contained in:
parent
5398a661ff
commit
f946690520
@ -39,3 +39,4 @@ ollama == 0.2.*
|
|||||||
openai == 1.30.*
|
openai == 1.30.*
|
||||||
# push notifications
|
# push notifications
|
||||||
py-vapid == 1.9.*
|
py-vapid == 1.9.*
|
||||||
|
pywebpush == 2.0.*
|
||||||
@ -41,13 +41,13 @@ def get_vapid_pub_key():
|
|||||||
def register_notifications():
|
def register_notifications():
|
||||||
username = request.headers.get("remote-user", type=str) or "admin"
|
username = request.headers.get("remote-user", type=str) or "admin"
|
||||||
json: dict[str, any] = request.get_json(silent=True) or {}
|
json: dict[str, any] = request.get_json(silent=True) or {}
|
||||||
token = json["token"]
|
sub = json.get("sub")
|
||||||
|
|
||||||
if not token:
|
if not sub:
|
||||||
return jsonify({"success": False, "message": "Token must be provided."}), 400
|
return jsonify({"success": False, "message": "Subscription must be provided."}), 400
|
||||||
|
|
||||||
try:
|
try:
|
||||||
User.update(notification_tokens=User.notification_tokens.append(token)).where(
|
User.update(notification_tokens=User.notification_tokens.append(sub)).where(
|
||||||
User.username == username
|
User.username == username
|
||||||
).execute()
|
).execute()
|
||||||
return make_response(
|
return make_response(
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
"""Handle sending notifications for Frigate via Firebase."""
|
"""Handle sending notifications for Frigate via Firebase."""
|
||||||
|
|
||||||
|
import datetime
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
from py_vapid import Vapid01
|
from py_vapid import Vapid01
|
||||||
|
from pywebpush import WebPusher
|
||||||
|
|
||||||
from frigate.comms.dispatcher import Communicator
|
from frigate.comms.dispatcher import Communicator
|
||||||
from frigate.config import FrigateConfig
|
from frigate.config import FrigateConfig
|
||||||
@ -20,17 +22,17 @@ class WebPushClient(Communicator): # type: ignore[misc]
|
|||||||
|
|
||||||
def __init__(self, config: FrigateConfig) -> None:
|
def __init__(self, config: FrigateConfig) -> None:
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.claim = None
|
||||||
|
self.claim_headers = None
|
||||||
|
self.web_pushers: list[WebPusher] = []
|
||||||
|
|
||||||
# Pull keys from PEM or generate if they do not exist
|
# Pull keys from PEM or generate if they do not exist
|
||||||
self.key = Vapid01.from_file(os.path.join(CONFIG_DIR, "notifications.pem"))
|
self.vapid = Vapid01.from_file(os.path.join(CONFIG_DIR, "notifications.pem"))
|
||||||
|
|
||||||
self.tokens = []
|
|
||||||
self.invalid_tokens = []
|
|
||||||
|
|
||||||
users: list[User] = User.select(User.notification_tokens).dicts().iterator()
|
users: list[User] = User.select(User.notification_tokens).dicts().iterator()
|
||||||
|
|
||||||
for user in users:
|
for user in users:
|
||||||
self.tokens.extend(user["notification_tokens"])
|
for sub in user["notification_tokens"]:
|
||||||
|
self.web_pushers.append(WebPusher(sub))
|
||||||
|
|
||||||
def subscribe(self, receiver: Callable) -> None:
|
def subscribe(self, receiver: Callable) -> None:
|
||||||
"""Wrapper for allowing dispatcher to subscribe."""
|
"""Wrapper for allowing dispatcher to subscribe."""
|
||||||
@ -42,6 +44,20 @@ class WebPushClient(Communicator): # type: ignore[misc]
|
|||||||
self.send_message(json.loads(payload))
|
self.send_message(json.loads(payload))
|
||||||
|
|
||||||
def send_message(self, payload: dict[str, any]) -> None:
|
def send_message(self, payload: dict[str, any]) -> None:
|
||||||
|
# check for valid claim or create new one
|
||||||
|
now = datetime.datetime.now().timestamp()
|
||||||
|
if self.claim is None or self.claim["exp"] < now:
|
||||||
|
# create new claim
|
||||||
|
self.claim = {
|
||||||
|
"sub": "mailto:test@example.com",
|
||||||
|
"aud": "https://fcm.googleapis.com",
|
||||||
|
"exp": (
|
||||||
|
datetime.datetime.now() + datetime.timedelta(hours=1)
|
||||||
|
).timestamp(),
|
||||||
|
}
|
||||||
|
self.claim_headers = self.vapid.sign(self.claim)
|
||||||
|
logger.info(f"Updated claim with new headers {self.claim_headers}")
|
||||||
|
|
||||||
# Only notify for alerts
|
# Only notify for alerts
|
||||||
if payload["after"]["severity"] != "alert":
|
if payload["after"]["severity"] != "alert":
|
||||||
return
|
return
|
||||||
@ -72,5 +88,17 @@ class WebPushClient(Communicator): # type: ignore[misc]
|
|||||||
direct_url = f"{self.config.notifications.base_url}/review?id={reviewId}"
|
direct_url = f"{self.config.notifications.base_url}/review?id={reviewId}"
|
||||||
image = f'{self.config.notifications.base_url}{payload["after"]["thumb_path"].replace("/media/frigate", "")}'
|
image = f'{self.config.notifications.base_url}{payload["after"]["thumb_path"].replace("/media/frigate", "")}'
|
||||||
|
|
||||||
|
for pusher in self.web_pushers:
|
||||||
|
pusher.send(
|
||||||
|
headers=self.claim_headers,
|
||||||
|
ttl=0,
|
||||||
|
data=json.dumps({
|
||||||
|
"title": title,
|
||||||
|
"message": message,
|
||||||
|
"direct_url": direct_url,
|
||||||
|
"image": image,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|||||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "frigate",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
@ -70,7 +70,10 @@ export default function NotificationView() {
|
|||||||
// TODO make the notifications button show enable / disable depending on current state
|
// TODO make the notifications button show enable / disable depending on current state
|
||||||
}
|
}
|
||||||
<Button
|
<Button
|
||||||
disabled={notificationsSubscribed == undefined}
|
disabled={
|
||||||
|
notificationsSubscribed == undefined ||
|
||||||
|
publicKey == undefined
|
||||||
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
Notification.requestPermission().then((permission) => {
|
Notification.requestPermission().then((permission) => {
|
||||||
console.log("notification permissions are ", permission);
|
console.log("notification permissions are ", permission);
|
||||||
@ -79,7 +82,10 @@ export default function NotificationView() {
|
|||||||
.register(NOTIFICATION_SERVICE_WORKER)
|
.register(NOTIFICATION_SERVICE_WORKER)
|
||||||
.then((registration) => {
|
.then((registration) => {
|
||||||
registration.pushManager
|
registration.pushManager
|
||||||
.subscribe()
|
.subscribe({
|
||||||
|
userVisibleOnly: true,
|
||||||
|
applicationServerKey: publicKey,
|
||||||
|
})
|
||||||
.then((pushSubscription) => {
|
.then((pushSubscription) => {
|
||||||
console.log(pushSubscription.endpoint);
|
console.log(pushSubscription.endpoint);
|
||||||
axios.post("notifications/register", {
|
axios.post("notifications/register", {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user