Add module.nix
This commit is contained in:
parent
ff0822d1bc
commit
08f8d721fa
|
@ -17,7 +17,7 @@
|
||||||
packages = rec {
|
packages = rec {
|
||||||
default = snooper-server;
|
default = snooper-server;
|
||||||
snooper-server = helpers.packages."${system}".mkClojureBin {
|
snooper-server = helpers.packages."${system}".mkClojureBin {
|
||||||
name = "org.fudo/snooper";
|
name = "org.fudo/snooper-server";
|
||||||
primaryNamespace = "snooper.cli";
|
primaryNamespace = "snooper.cli";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
packages:
|
||||||
|
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
snooper-server = packages."${pkgs.system}.snooper-server";
|
||||||
|
cfg = config.services.snooper;
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.services.snooper = with types; {
|
||||||
|
enable = mkEnableOption "Enable Snooper notifiaction server.";
|
||||||
|
|
||||||
|
verbose = mkEnableOption "Generate verbose logs and output.";
|
||||||
|
|
||||||
|
event-topics = mkOption {
|
||||||
|
type = listOf str;
|
||||||
|
description = "MQTT topics on which to listen for detection events.";
|
||||||
|
};
|
||||||
|
|
||||||
|
notification-topic = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "MQTT topic on which to send notifications.";
|
||||||
|
};
|
||||||
|
|
||||||
|
mqtt-client = {
|
||||||
|
host = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Hostname of the MQTT server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = port;
|
||||||
|
description = "Port on which the MQTT server is listening.";
|
||||||
|
default = 1883;
|
||||||
|
};
|
||||||
|
|
||||||
|
username = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "User as which to connect to the MQTT server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
password-file = mkOption {
|
||||||
|
type = str;
|
||||||
|
description =
|
||||||
|
"File (on the local host) containing the password for the MQTT server.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
systemd.services.snooper = {
|
||||||
|
path = [ snooper-server ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
DynamicUser = true;
|
||||||
|
LoadCredential = [ "mqtt.passwd:${cfg.mqtt-client.password-file}" ];
|
||||||
|
ExecStart = pkgs.writeShellScript "snooper-server.sh"
|
||||||
|
(concatStringsSep " " ([
|
||||||
|
"snooper-server"
|
||||||
|
"--mqtt-host=${cfg.mqtt.host}"
|
||||||
|
"--mqtt-port=${toString cfg.mqtt.port}"
|
||||||
|
"--mqtt-user=${cfg.mqtt.username}"
|
||||||
|
"--mqtt-password-file=$CREDENTIALS_DIRECTORY/mqtt.passwd"
|
||||||
|
"--notification-topic=${cfg.notification-topic}"
|
||||||
|
] ++ (map (topic: "--event-topic=${topic}") cfg.event-topics)
|
||||||
|
++ (optional cfg.verbose "--verbose")));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -5,10 +5,12 @@
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[snooper.core :as snooper]
|
[snooper.core :as snooper]
|
||||||
[milquetoast.client :as mqtt]
|
[milquetoast.client :as mqtt]
|
||||||
[fudo-clojure.logging :as log]))
|
[fudo-clojure.logging :as log])
|
||||||
|
(:gen-class))
|
||||||
|
|
||||||
(def cli-opts
|
(def cli-opts
|
||||||
[["-v" "--verbose" "Provide verbose output."]
|
[["-v" "--verbose" "Provide verbose output."]
|
||||||
|
["-h" "--help" "Print this message."]
|
||||||
|
|
||||||
[nil "--mqtt-host HOSTNAME" "Hostname of MQTT server."]
|
[nil "--mqtt-host HOSTNAME" "Hostname of MQTT server."]
|
||||||
[nil "--mqtt-port PORT" "Port on which to connect to the MQTT server."
|
[nil "--mqtt-port PORT" "Port on which to connect to the MQTT server."
|
||||||
|
@ -44,13 +46,15 @@
|
||||||
(defn -main [& args]
|
(defn -main [& args]
|
||||||
(let [required-args #{:mqtt-host :mqtt-port :mqtt-user :mqtt-password-file :event-topic :notification-topic}
|
(let [required-args #{:mqtt-host :mqtt-port :mqtt-user :mqtt-password-file :event-topic :notification-topic}
|
||||||
{:keys [options _ errors summary]} (parse-opts args required-args cli-opts)]
|
{:keys [options _ errors summary]} (parse-opts args required-args cli-opts)]
|
||||||
|
(when (:help options) (msg-quit 0 (usage summary)))
|
||||||
(when (seq errors) (msg-quit 1 (usage summary errors)))
|
(when (seq errors) (msg-quit 1 (usage summary errors)))
|
||||||
(let [{:keys [mqtt-host
|
(let [{:keys [mqtt-host
|
||||||
mqtt-port
|
mqtt-port
|
||||||
mqtt-user
|
mqtt-user
|
||||||
mqtt-password-file
|
mqtt-password-file
|
||||||
notification-topic
|
notification-topic
|
||||||
event-topic]} options
|
event-topic
|
||||||
|
verbose]} options
|
||||||
catch-shutdown (async/chan)
|
catch-shutdown (async/chan)
|
||||||
mqtt-client (mqtt/connect-json! :host mqtt-host
|
mqtt-client (mqtt/connect-json! :host mqtt-host
|
||||||
:port mqtt-port
|
:port mqtt-port
|
||||||
|
@ -59,13 +63,18 @@
|
||||||
(slurp)
|
(slurp)
|
||||||
(str/trim)))
|
(str/trim)))
|
||||||
logger (log/print-logger)]
|
logger (log/print-logger)]
|
||||||
(snooper/listen! :mqtt-client mqtt-client
|
(when verbose
|
||||||
|
(println (format "launching snooper server to listen on %s and report events on %s"
|
||||||
|
event-topic notification-topic)))
|
||||||
|
(snooper/listen! :mqtt-client mqtt-client
|
||||||
:notification-topic notification-topic
|
:notification-topic notification-topic
|
||||||
:event-topics event-topic
|
:event-topics event-topic
|
||||||
:logger logger)
|
:logger logger
|
||||||
|
:verbose verbose)
|
||||||
(.addShutdownHook (Runtime/getRuntime)
|
(.addShutdownHook (Runtime/getRuntime)
|
||||||
(Thread. (fn [] (>!! catch-shutdown true))))
|
(Thread. (fn [] (>!! catch-shutdown true))))
|
||||||
(<!! catch-shutdown)
|
(<!! catch-shutdown)
|
||||||
|
(println "stopping snooper server")
|
||||||
;; Stopping the MQTT will stop tattler
|
;; Stopping the MQTT will stop tattler
|
||||||
(mqtt/stop! mqtt-client)
|
(mqtt/stop! mqtt-client)
|
||||||
(System/exit 0))))
|
(System/exit 0))))
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
(ns snooper.core
|
(ns snooper.core
|
||||||
(:require [clojure.core.async :refer [go-loop alts! chan]]
|
(:require [clojure.core.async :refer [go-loop alts!]]
|
||||||
|
[clojure.pprint :refer [pprint]]
|
||||||
[fudo-clojure.logging :as log]
|
[fudo-clojure.logging :as log]
|
||||||
[milquetoast.client :as mqtt]
|
[milquetoast.client :as mqtt]
|
||||||
[malli.core :as t]))
|
[malli.core :as t]))
|
||||||
|
|
||||||
(defn pthru [o] (clojure.pprint/pprint o) o)
|
|
||||||
|
|
||||||
(def critical-objects [:person :bear])
|
(def critical-objects [:person :bear])
|
||||||
(def normal-objects [:cat :dog])
|
(def normal-objects [:cat :dog])
|
||||||
|
|
||||||
|
(defn- verbose-pthru [verbose obj]
|
||||||
|
(when verbose (pprint obj))
|
||||||
|
obj)
|
||||||
|
|
||||||
(defn- objects-criticality [objs]
|
(defn- objects-criticality [objs]
|
||||||
(cond (some (partial contains? objs) critical-objects) :high
|
(cond (some (partial contains? objs) critical-objects) :high
|
||||||
(some (partial contains? objs) normal-objects) :medium
|
(some (partial contains? objs) normal-objects) :medium
|
||||||
:else nil))
|
:else :low))
|
||||||
|
|
||||||
(defn- objects-probability [objs]
|
(defn- objects-probability [objs]
|
||||||
(let [prob (apply max (vals objs))]
|
(let [prob (apply max (vals objs))]
|
||||||
|
@ -58,7 +61,7 @@
|
||||||
(defmethod event-summary :possibly [{:keys [description location]}]
|
(defmethod event-summary :possibly [{:keys [description location]}]
|
||||||
(format "There could possibly be %s at the %s" description location))
|
(format "There could possibly be %s at the %s" description location))
|
||||||
(defmethod event-summary :likely [{:keys [description location]}]
|
(defmethod event-summary :likely [{:keys [description location]}]
|
||||||
(format "There's might %s at the %s" description location))
|
(format "There might be %s at the %s" description location))
|
||||||
(defmethod event-summary :probably [{:keys [description location]}]
|
(defmethod event-summary :probably [{:keys [description location]}]
|
||||||
(format "There's probably %s at the %s" description location))
|
(format "There's probably %s at the %s" description location))
|
||||||
(defmethod event-summary :definitely [{:keys [description location]}]
|
(defmethod event-summary :definitely [{:keys [description location]}]
|
||||||
|
@ -84,17 +87,19 @@
|
||||||
[& {mqtt-client :mqtt-client
|
[& {mqtt-client :mqtt-client
|
||||||
notification-topic :notification-topic
|
notification-topic :notification-topic
|
||||||
event-topics :event-topics
|
event-topics :event-topics
|
||||||
logger :logger}]
|
logger :logger
|
||||||
|
verbose :verbose}]
|
||||||
(let [incoming (map (partial mqtt/subscribe! mqtt-client) event-topics)
|
(let [incoming (map (partial mqtt/subscribe! mqtt-client) event-topics)
|
||||||
valid-evt? (t/validator MotionEvent)]
|
valid-evt? (t/validator MotionEvent)]
|
||||||
(go-loop [evts (alts! incoming)]
|
(go-loop [evts (alts! incoming)]
|
||||||
(let [evt (first evts)]
|
(let [evt (first evts)]
|
||||||
(clojure.pprint/pprint evt)
|
(when verbose (pprint evt))
|
||||||
(cond (nil? evt) (log/info! logger "stopping")
|
(cond (nil? evt) (log/info! logger "stopping")
|
||||||
(valid-evt? evt) (do (log/info! logger (format "received motion event id %s from %s"
|
(valid-evt? evt) (do (log/info! logger (format "received motion event id %s from %s"
|
||||||
(:id evt)
|
(:id evt)
|
||||||
(:topic evt)))
|
(:topic evt)))
|
||||||
(mqtt/send! mqtt-client notification-topic (pthru (translate-event (:payload evt))))
|
(mqtt/send! mqtt-client notification-topic
|
||||||
|
(verbose-pthru verbose (translate-event (:payload evt))))
|
||||||
(recur (alts! incoming)))
|
(recur (alts! incoming)))
|
||||||
:else (do (log/error! logger (format "invalid motion event: %s" evt))
|
:else (do (log/error! logger (format "invalid motion event: %s" evt))
|
||||||
(recur (alts! incoming))))))))
|
(recur (alts! incoming))))))))
|
||||||
|
|
Loading…
Reference in New Issue