From d29d9e16f053f9d78a0743929a132c1834014c93 Mon Sep 17 00:00:00 2001 From: Ashish D'Souza Date: Sat, 20 May 2023 11:44:46 -0400 Subject: [PATCH] Added notification system --- conf/docker-compose.yaml | 19 ++++++++++++- install.yaml | 23 ++++++++++++++++ notify/Dockerfile | 11 ++++++++ notify/requirements.txt | 2 ++ notify/src/mqtt.py | 37 +++++++++++++++++++++++++ notify/src/ntfy.py | 59 ++++++++++++++++++++++++++++++++++++++++ start.yaml | 8 +++++- 7 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 notify/Dockerfile create mode 100644 notify/requirements.txt create mode 100644 notify/src/mqtt.py create mode 100644 notify/src/ntfy.py diff --git a/conf/docker-compose.yaml b/conf/docker-compose.yaml index 8b6e60c..4033621 100644 --- a/conf/docker-compose.yaml +++ b/conf/docker-compose.yaml @@ -15,7 +15,7 @@ services: timeout: 30s environment: FRIGATE_MQTT_USERNAME: frigate - FRIGATE_MQTT_PASSWORD: ${FRIGATE_MQTT_PASSWORD} + FRIGATE_MQTT_PASSWORD: ${MQTT_PASSWORD} FRIGATE_GARAGE_RTSP_USERNAME: motion FRIGATE_GARAGE_RTSP_PASSWORD: ${FRIGATE_GARAGE_RTSP_PASSWORD} FRIGATE_FRONT_DOOR_RTSP_USERNAME: motion @@ -46,6 +46,23 @@ services: # - frigate ports: - 127.0.0.1:5000:5000 + notify: + container_name: frigate-notify + image: frigate-notify:latest + restart: unless-stopped + environment: + MQTT_USERNAME: frigate + MQTT_PASSWORD: ${MQTT_PASSWORD} + REQUESTS_CA_BUNDLE: /etc/ssl/certs/ca-certificates.crt + volumes: + - type: bind + source: /data/certs/homelab_ca.crt + target: /usr/local/share/ca-certificates/homelab_ca.crt + read_only: true + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true # mqtt: # container_name: mqtt # image: eclipse-mosquitto:latest diff --git a/install.yaml b/install.yaml index 6fac74f..d497393 100644 --- a/install.yaml +++ b/install.yaml @@ -55,6 +55,29 @@ dest: /data/frigate-config/config.yaml mode: preserve + - name: Create temporary Docker build directory + ansible.builtin.tempfile: + state: directory + register: docker_build_dir + - name: Copy Docker build directory + ansible.builtin.copy: + src: notify/ + dest: '{{docker_build_dir.path}}' + mode: preserve + - name: Build frigate-notify Docker image + ansible.builtin.docker_image: + build: + path: '{{docker_build_dir.path}}' + name: frigate-notify + tag: latest + source: build + force_source: true + state: present + - name: Remove temporary Docker build directory + ansible.builtin.file: + path: '{{docker_build_dir.path}}' + state: absent + - name: Read homelab config ansible.builtin.slurp: src: '{{ansible_user_dir}}/.homelab.json' diff --git a/notify/Dockerfile b/notify/Dockerfile new file mode 100644 index 0000000..b116883 --- /dev/null +++ b/notify/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.11 +WORKDIR /code + +ENTRYPOINT ["python3", "mqtt.py"] + +RUN pip3 install --upgrade pip + +COPY requirements.txt . +RUN pip install -r requirements.txt + +COPY src . diff --git a/notify/requirements.txt b/notify/requirements.txt new file mode 100644 index 0000000..e2b8dc1 --- /dev/null +++ b/notify/requirements.txt @@ -0,0 +1,2 @@ +requests==2.30.0 +paho-mqtt==1.6.1 diff --git a/notify/src/mqtt.py b/notify/src/mqtt.py new file mode 100644 index 0000000..dafc996 --- /dev/null +++ b/notify/src/mqtt.py @@ -0,0 +1,37 @@ +import os +import json +from time import sleep + +import paho.mqtt.client as mqtt + +from ntfy import send_rate_limited_notification + + +def on_connect(client, userdata, flags, rc): + print(f'Connected with return code {rc}') + client.subscribe('frigate/events') + + +def on_message(client, userdata, message): + payload = json.loads(message.payload.decode()) + if payload['type'] == 'new' and payload['after']['label'] == 'person': + event_id = payload['after']['id'] + camera = payload['after']['camera'] + object_label = payload['after']['label'] + score = payload['after']['top_score'] + + send_rate_limited_notification(event_id, camera, object_label, score) + + +def subscribe(): + client = mqtt.Client('frigate_notifications') + client.username_pw_set(os.environ['MQTT_USERNAME'], password=os.environ['MQTT_PASSWORD']) + client.on_connect = on_connect + client.on_message = on_message + + client.connect(host='192.168.0.6', port=1883) + client.loop_forever() + + +if __name__ == '__main__': + subscribe() diff --git a/notify/src/ntfy.py b/notify/src/ntfy.py new file mode 100644 index 0000000..9c7a903 --- /dev/null +++ b/notify/src/ntfy.py @@ -0,0 +1,59 @@ +import json +import requests +from datetime import datetime, timedelta + + +last_notification_time = None + + +def send_rate_limited_notification(event_id, camera, object_label, score, quiet_period=1): + global last_notification_time + + now = datetime.now() + if last_notification_time is None or now - last_notification_time >= timedelta(minutes=quiet_period): + last_notification_time = now + send_notification(event_id, camera, object_label, score, priority=3) + + +def send_notification(event_id, camera, object_label, score, priority=3): + camera_location = ' '.join(word.capitalize() for word in camera.split('_')) + + response = requests.post('https://ntfy.homelab.net', json={ + 'topic': 'frigate_notifications', + 'title': 'Frigate', + 'message': f'{object_label.capitalize()} at {camera_location} ({score:.0%})', + 'priority': priority, + 'click': f'https://frigate.homelab.net/cameras/{camera}', + 'icon': 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/frigate.png', + 'attach': f'https://frigate.homelab.net/api/events/{event_id}/thumbnail.jpg?format=android', + 'actions': [ + { + 'action': 'http', + 'label': 'Disable (30m)', + 'url': 'https://www.google.com/', + 'method': 'GET', + 'clear': True + } + ] + }) + + response2 = requests.post('https://ntfy.homelab.net', json={ + 'topic': 'frigate_notifications_dad', + 'title': 'Frigate', + 'message': f'{object_label.capitalize()} at {camera_location} ({score:.0%})', + 'priority': priority, + 'click': f'https://frigate.homelab.net/cameras/{camera}', + 'icon': 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/frigate.png', + 'attach': f'https://frigate.homelab.net/api/events/{event_id}/thumbnail.jpg?format=android', + 'actions': [ + { + 'action': 'http', + 'label': 'DBL (30m)', + 'url': 'https://www.google.com/', + 'method': 'GET', + 'clear': True + } + ] + }) + + return response.status_code == 200 and response2.status_code == 200 diff --git a/start.yaml b/start.yaml index ddba1e8..e5ffdd8 100644 --- a/start.yaml +++ b/start.yaml @@ -35,7 +35,7 @@ - name: Docker Compose up Frigate environment: - FRIGATE_MQTT_PASSWORD: '{{frigate_config.mqtt.password}}' + MQTT_PASSWORD: '{{frigate_config.mqtt.password}}' FRIGATE_GARAGE_RTSP_PASSWORD: '{{frigate_config.rtsp.garage.password}}' FRIGATE_FRONT_DOOR_RTSP_PASSWORD: '{{frigate_config.rtsp.front_door.password}}' FRIGATE_BACK_DOOR_RTSP_PASSWORD: '{{frigate_config.rtsp.back_door.password}}' @@ -49,3 +49,9 @@ ansible.builtin.file: path: '{{docker_compose_dir.path}}' state: absent + + - name: Update CA certificates + community.docker.docker_container_exec: + container: frigate-notify + command: update-ca-certificates + user: root