From ce6b363d4e7dd1e557b0be5347dc8c14fc864d27 Mon Sep 17 00:00:00 2001 From: Ashish D'Souza Date: Sun, 21 May 2023 19:33:10 -0400 Subject: [PATCH] Added webcontrol API --- conf/config.yaml | 2 +- conf/docker-compose.yaml | 30 +++++++++++++++++++++++++++++- conf/nginx.conf | 6 +++++- install.yaml | 30 +++++++++++++++++++++++++++--- notify/Dockerfile | 2 +- notify/src/mqtt.py | 4 ++-- notify/src/ntfy.py | 25 +++++++++++++++++++------ webcontrol/Dockerfile | 11 +++++++++++ webcontrol/requirements.txt | 2 ++ webcontrol/src/detection.py | 37 +++++++++++++++++++++++++++++++++++++ webcontrol/src/server.py | 20 ++++++++++++++++++++ 11 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 webcontrol/Dockerfile create mode 100644 webcontrol/requirements.txt create mode 100644 webcontrol/src/detection.py create mode 100644 webcontrol/src/server.py diff --git a/conf/config.yaml b/conf/config.yaml index 63659c6..07c8600 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -1,5 +1,5 @@ mqtt: - host: 192.168.0.6 + host: mqtt user: '{FRIGATE_MQTT_USERNAME}' password: '{FRIGATE_MQTT_PASSWORD}' diff --git a/conf/docker-compose.yaml b/conf/docker-compose.yaml index 4033621..6c690d1 100644 --- a/conf/docker-compose.yaml +++ b/conf/docker-compose.yaml @@ -45,7 +45,9 @@ services: # networks: # - frigate ports: - - 127.0.0.1:5000:5000 + - 127.0.0.1:10000:5000 + extra_hosts: + - mqtt:192.168.0.6 notify: container_name: frigate-notify image: frigate-notify:latest @@ -63,6 +65,32 @@ services: source: /etc/localtime target: /etc/localtime read_only: true + extra_hosts: + - mqtt:192.168.0.6 + webcontrol: + container_name: frigate-webcontrol + image: frigate-webcontrol:latest + labels: + autoheal: 'true' + restart: unless-stopped + healthcheck: + test: curl -s -f http://localhost -o /dev/null + interval: 60s + retries: 1 + start_period: 30s + timeout: 30s + environment: + MQTT_USERNAME: frigate + MQTT_PASSWORD: ${MQTT_PASSWORD} + volumes: + - type: bind + source: /etc/localtime + target: /etc/localtime + read_only: true + ports: + - 127.0.0.1:10001:80 + extra_hosts: + - mqtt:192.168.0.6 # mqtt: # container_name: mqtt # image: eclipse-mosquitto:latest diff --git a/conf/nginx.conf b/conf/nginx.conf index 91f59d4..bdf2966 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -6,9 +6,13 @@ server { ssl_certificate_key "/certs/homelab.net/homelab.net.key"; location / { - proxy_pass "http://localhost:5000/"; + proxy_pass "http://localhost:10000/"; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } + + location /webcontrol/ { + proxy_pass "http://localhost:10001/"; + } } diff --git a/install.yaml b/install.yaml index d497393..7ba64f7 100644 --- a/install.yaml +++ b/install.yaml @@ -55,7 +55,7 @@ dest: /data/frigate-config/config.yaml mode: preserve - - name: Create temporary Docker build directory + - name: Create temporary Docker build directory for frigate-notify ansible.builtin.tempfile: state: directory register: docker_build_dir @@ -73,7 +73,30 @@ source: build force_source: true state: present - - name: Remove temporary Docker build directory + - name: Remove temporary Docker build directory for frigate-notify + ansible.builtin.file: + path: '{{docker_build_dir.path}}' + state: absent + + - name: Create temporary Docker build directory for frigate-webcontrol + ansible.builtin.tempfile: + state: directory + register: docker_build_dir + - name: Copy Docker build directory + ansible.builtin.copy: + src: webcontrol/ + dest: '{{docker_build_dir.path}}' + mode: preserve + - name: Build frigate-webcontrol Docker image + ansible.builtin.docker_image: + build: + path: '{{docker_build_dir.path}}' + name: frigate-webcontrol + tag: latest + source: build + force_source: true + state: present + - name: Remove temporary Docker build directory for frigate-webcontrol ansible.builtin.file: path: '{{docker_build_dir.path}}' state: absent @@ -126,7 +149,8 @@ - '{{item.reverse_proxy}}' - '{{item.reverse_proxy_port}}' with_items: - - {service: frigate, port: 5000, domain: null, reverse_proxy: nginx_barad-dur, reverse_proxy_port: 443} + - {service: frigate, port: 10000, domain: null, reverse_proxy: nginx_barad-dur, reverse_proxy_port: 443} + - {service: frigate, port: 10001, domain: null, reverse_proxy: nginx_barad-dur, reverse_proxy_port: 443} - name: Insert into Postgres table service_data community.postgresql.postgresql_query: login_host: '{{homelab_config.database.host}}' diff --git a/notify/Dockerfile b/notify/Dockerfile index b116883..bda211a 100644 --- a/notify/Dockerfile +++ b/notify/Dockerfile @@ -6,6 +6,6 @@ ENTRYPOINT ["python3", "mqtt.py"] RUN pip3 install --upgrade pip COPY requirements.txt . -RUN pip install -r requirements.txt +RUN pip3 install -r requirements.txt COPY src . diff --git a/notify/src/mqtt.py b/notify/src/mqtt.py index dafc996..9e2cc9e 100644 --- a/notify/src/mqtt.py +++ b/notify/src/mqtt.py @@ -24,12 +24,12 @@ def on_message(client, userdata, message): def subscribe(): - client = mqtt.Client('frigate_notifications') + client = mqtt.Client() 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.connect(host='mqtt', port=1883) client.loop_forever() diff --git a/notify/src/ntfy.py b/notify/src/ntfy.py index 216df0f..9c0d295 100644 --- a/notify/src/ntfy.py +++ b/notify/src/ntfy.py @@ -1,7 +1,7 @@ import json -import requests from datetime import datetime, timedelta +import requests last_notification_time = {} @@ -30,8 +30,15 @@ def send_notification(event_id, camera, object_label, score, priority=3): { 'action': 'http', 'label': 'Disable (30m)', - 'url': 'https://www.google.com/', - 'method': 'GET', + 'url': f'https://frigate.homelab.net/webcontrol/camera/{camera}/detect', + 'method': 'POST', + 'headers': { + 'Content-Type': 'application/json' + }, + 'body': json.dumps({ + 'value': False, + 'duration': 30 + }), 'clear': True } ] @@ -49,9 +56,15 @@ def send_notification(event_id, camera, object_label, score, priority=3): { 'action': 'http', 'label': 'DBL (30m)', - 'url': 'https://www.google.com/', - 'method': 'GET', - 'clear': True + 'url': f'https://frigate.homelab.net/webcontrol/camera/{camera}/detect', + 'method': 'POST', + 'headers': { + 'Content-Type': 'application/json' + }, + 'body': json.dumps({ + 'value': False, + 'duration': 30 + }) } ] }) diff --git a/webcontrol/Dockerfile b/webcontrol/Dockerfile new file mode 100644 index 0000000..754f161 --- /dev/null +++ b/webcontrol/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.11 +WORKDIR /code + +ENTRYPOINT ["python3", "server.py"] + +RUN pip3 install --upgrade pip + +COPY requirements.txt . +RUN pip3 install -r requirements.txt + +COPY src . diff --git a/webcontrol/requirements.txt b/webcontrol/requirements.txt new file mode 100644 index 0000000..b1a39cb --- /dev/null +++ b/webcontrol/requirements.txt @@ -0,0 +1,2 @@ +flask==2.3.2 +paho-mqtt==1.6.1 diff --git a/webcontrol/src/detection.py b/webcontrol/src/detection.py new file mode 100644 index 0000000..2ec1e44 --- /dev/null +++ b/webcontrol/src/detection.py @@ -0,0 +1,37 @@ +import os +from time import sleep +from threading import Thread + +from flask import Blueprint, request, jsonify +import paho.mqtt.publish as mqtt_publish + + +blueprint = Blueprint('detection', __name__) + + +def set_camera_detection(camera: str, value: bool, delay: int = 0) -> None: + sleep(delay) + + mqtt_publish.single(f'frigate/{camera}/detect/set', 'ON' if value else 'OFF', hostname='mqtt', port=1883, auth={'username': os.environ['MQTT_USERNAME'], 'password': os.environ['MQTT_PASSWORD']}) + + +@blueprint.route('/camera//detect', methods=['POST']) +def camera_detect_POST(camera): + if not request.json: + return jsonify({ + 'status': 'failure', + 'description': 'Request body needs to be in JSON format' + }), 400 + + value = request.json.get('value', True) + set_camera_detection(camera, value) + + duration = request.json.get('duration', 0) + if duration > 0: + # Start sleeping thread to revert value after duration + thread = Thread(target=set_camera_detection, args=(camera, not value, duration * 60)) + thread.start() + + return jsonify({ + 'status': 'success' + }), 200 diff --git a/webcontrol/src/server.py b/webcontrol/src/server.py new file mode 100644 index 0000000..db2c3b4 --- /dev/null +++ b/webcontrol/src/server.py @@ -0,0 +1,20 @@ +from flask import Flask, jsonify + +import detection + + +app = Flask(__name__) +app.register_blueprint(detection.blueprint) + + +@app.route('/', methods=['GET']) +def home_GET(): + return jsonify({ + 'status': 'success', + 'name': 'webcontrol', + 'description': 'This is a custom webcontrol API for Frigate that allows minimal control over the NVR system through an HTTP API' + }), 200 + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=80, debug=False)