From f24b9582f71754681a94ee1f27690f0611a89453 Mon Sep 17 00:00:00 2001 From: arcnmx Date: Sun, 9 Jun 2024 11:21:15 -0700 Subject: [PATCH] chore(motion): clean up settings --- modules/nixos/motion.nix | 95 ++++++++++++++++++++++++++----- modules/system/exports/motion.nix | 14 +++++ nixos/kitchencam.nix | 63 ++++++++++---------- systems/logistics/nixos.nix | 9 +++ 4 files changed, 134 insertions(+), 47 deletions(-) diff --git a/modules/nixos/motion.nix b/modules/nixos/motion.nix index 3bf3ff3f..7af6caa7 100644 --- a/modules/nixos/motion.nix +++ b/modules/nixos/motion.nix @@ -1,4 +1,51 @@ -{ +let + cameraModule = { + pkgs, + config, + gensokyo-zone, + name, + lib, + lib'motion, + ... + }: let + inherit (lib.options) mkOption mkEnableOption; + inherit (lib.modules) mkIf mkMerge mkOptionDefault; + inherit (lib.strings) hasPrefix; + in { + options = with lib.types; { + enable = mkEnableOption "camera" // { + default = true; + }; + settings = mkOption { + type = attrsOf (oneOf [str int bool]); + description = "https://motion-project.github.io/motion_config.html"; + }; + extraConfig = mkOption { + type = lines; + default = ""; + }; + configText = mkOption { + type = lines; + internal = true; + }; + configFile = mkOption { + type = path; + }; + }; + config = let + configFile = pkgs.writeText "motion.conf" config.configText; + in { + settings = { + videodevice = mkIf (hasPrefix "/" name) (mkOptionDefault name); + }; + configFile = mkOptionDefault "${configFile}"; + configText = mkMerge ( + (lib'motion.mkMotionSettings config.settings) + ++ [config.extraConfig] + ); + }; + }; +in { pkgs, config, gensokyo-zone, @@ -6,23 +53,26 @@ lib, ... }: let - inherit (gensokyo-zone.lib) mapOptionDefaults; inherit (lib.options) mkOption mkPackageOption mkEnableOption; inherit (lib.modules) mkIf mkMerge mkAfter mkOptionDefault; - inherit (lib.attrsets) mapAttrsToList; + inherit (lib.attrsets) attrValues mapAttrsToList; + inherit (lib.lists) filter; inherit (lib.meta) getExe; cfg = config.services.motion; - mkMotionValue = value: - if value == true - then "on" - else if value == false - then "off" - else toString value; - mkMotionSetting = key: value: "${key} ${mkMotionValue value}"; + lib'motion = config.lib.motion; in { options.services.motion = with lib.types; { enable = mkEnableOption "motion"; package = mkPackageOption pkgs "motion" {}; + cameras = mkOption { + type = attrsOf (submoduleWith { + modules = [ cameraModule ]; + specialArgs = { + inherit pkgs gensokyo-zone lib'motion; + nixosConfig = config; + }; + }); + }; dataDir = mkOption { type = path; default = "/var/lib/motion"; @@ -37,7 +87,7 @@ in { }; settings = mkOption { type = attrsOf (oneOf [str int bool]); - description = "https://linux.die.net/man/1/motion"; + description = "https://motion-project.github.io/motion_config.html"; }; extraArgs = mkOption { type = listOf str; @@ -57,14 +107,19 @@ in { }; config.services.motion = let configFile = pkgs.writeText "motion.conf" cfg.configText; + enableIPv6 = mkIf config.networking.enableIPv6 (mkOptionDefault true); + enabledCameras = filter (camera: camera.enable) (attrValues cfg.cameras); in { - settings = mapOptionDefaults { - target_dir = cfg.dataDir; + settings = { + target_dir = mkOptionDefault cfg.dataDir; + ipv6_enabled = enableIPv6; + webcontrol_ipv6 = enableIPv6; }; configFile = mkOptionDefault "${configFile}"; configText = mkMerge ( - (mapAttrsToList mkMotionSetting cfg.settings) - ++ [(mkAfter cfg.extraConfig)] + (lib'motion.mkMotionSettings cfg.settings) + ++ [cfg.extraConfig] + ++ map (camera: mkAfter "camera ${camera.configFile}") enabledCameras ); }; config.users = mkIf cfg.enable { @@ -101,4 +156,14 @@ in { ]; }; }; + config.lib.motion = { + mkMotionValue = value: + if value == true + then "on" + else if value == false + then "off" + else toString value; + mkMotionSetting = key: value: "${key} ${lib'motion.mkMotionValue value}"; + mkMotionSettings = mapAttrsToList lib'motion.mkMotionSetting; + }; } diff --git a/modules/system/exports/motion.nix b/modules/system/exports/motion.nix index e803cd96..2fc71159 100644 --- a/modules/system/exports/motion.nix +++ b/modules/system/exports/motion.nix @@ -4,11 +4,25 @@ ... }: let inherit (gensokyo-zone.lib) mkAlmostOptionDefault; + inherit (lib.modules) mkIf; in { config.exports.services.motion = {config, ...}: { displayName = mkAlmostOptionDefault "Motion"; nixos = { serviceAttr = "motion"; + assertions = let + # in motion.conf, `0` represents the port being disabled + configPort = port: if port.enable then port.port else 0; + in mkIf config.enable [ + (nixosConfig: { + assertion = configPort config.ports.default == nixosConfig.services.motion.settings.webcontrol_port or 0; + message = "webcontrol port mismatch"; + }) + (nixosConfig: { + assertion = configPort config.ports.stream == nixosConfig.services.motion.settings.stream_port or 0; + message = "stream port mismatch"; + }) + ]; }; defaults.port.listen = mkAlmostOptionDefault "lan"; ports = { diff --git a/nixos/kitchencam.nix b/nixos/kitchencam.nix index a968ca48..b16793ef 100644 --- a/nixos/kitchencam.nix +++ b/nixos/kitchencam.nix @@ -1,8 +1,10 @@ { config, + gensokyo-zone, lib, ... }: let + inherit (gensokyo-zone.lib) mapDefaults; inherit (lib.modules) mkIf mkDefault; cfg = config.services.motion; streamPort = 41081; @@ -10,39 +12,36 @@ in { services.motion = { enable = mkDefault true; - extraConfig = '' - videodevice /dev/kitchencam - v4l2_palette 8 - width 640 - height 480 - framerate 5 + settings = mapDefaults { + picture_output = false; + movie_output = false; + picture_filename = "%Y%m%d%H%M%S-%q"; + movie_filename = "%t-%v-%Y%m%d%H%M%S"; - text_left kitchen - text_right %Y-%m-%d\n%T-%q - emulate_motion off - threshold 1500 - despeckle_filter EedDl - minimum_motion_frames 1 - event_gap 60 - pre_capture 3 - post_capture 0 + text_right = "%Y-%m-%d\\n%T-%q"; + emulate_motion = false; + threshold = 1500; + despeckle_filter = "EedDl"; + minimum_motion_frames = 1; + event_gap = 60; + pre_capture = 3; + post_capture = 0; - picture_output off - picture_filename %Y%m%d%H%M%S-%q - - movie_output off - movie_max_time 60 - movie_quality 45 - movie_codec mkv - movie_filename %t-%v-%Y%m%d%H%M%S - - webcontrol_port ${toString webPort} - webcontrol_localhost off - webcontrol_parms 0 - stream_port ${toString streamPort} - stream_localhost off - ipv6_enabled on - ''; + webcontrol_localhost = false; + stream_localhost = false; + webcontrol_parms = 0; + webcontrol_port = webPort; + stream_port = streamPort; + }; + cameras.kitchencam.settings = mapDefaults { + videodevice = "/dev/kitchencam"; + v4l2_palette = 8; + width = 640; + height = 480; + framerate = 5; + camera_id = 1; + text_left = "kitchen"; + }; }; services.udev.extraRules = let inherit (lib.strings) concatStringsSep; @@ -61,6 +60,6 @@ in { in mkIf cfg.enable rulesLine; networking.firewall.interfaces.local = mkIf cfg.enable { - allowedTCPPorts = [streamPort webPort]; + allowedTCPPorts = [cfg.settings.stream_port cfg.settings.webcontrol_port]; }; } diff --git a/systems/logistics/nixos.nix b/systems/logistics/nixos.nix index 12946121..6f1c2bde 100644 --- a/systems/logistics/nixos.nix +++ b/systems/logistics/nixos.nix @@ -39,6 +39,15 @@ in { #jack.enable = true; }; + services.motion.cameras.webcam = { + #enable = false; + settings = { + videodevice = "/dev/video0"; + camera_id = 2; + text_left = "logistics"; + }; + }; + users.users.logistics = { uid = 1000; isNormalUser = true;