From f72f1eb0bc389298f2c8226e1d14917ca923f8bd Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Sun, 10 Aug 2025 18:18:37 -0600 Subject: [PATCH] nixos/klipper: Expose firmware and flasher packages in config This makes it easy to refer to the firmware and flasher packages from other parts of the config. Currently, if `enableKlipperFlash` is false, simply adding the firmware package to `systemPackages` isn't very useful. Also makes the assertion message more helpful. --- nixos/modules/services/misc/klipper.nix | 151 ++++++++++++++---------- 1 file changed, 88 insertions(+), 63 deletions(-) diff --git a/nixos/modules/services/misc/klipper.nix b/nixos/modules/services/misc/klipper.nix index 9322ad5a74cc..223bc8125915 100644 --- a/nixos/modules/services/misc/klipper.nix +++ b/nixos/modules/services/misc/klipper.nix @@ -16,6 +16,68 @@ let lib.concatMapStrings (s: "\n ${lib.generators.mkValueStringDefault { } s}") l; mkKeyValue = lib.generators.mkKeyValueDefault { } ":"; }; + firmwareSubmodule = lib.types.submodule ( + { name, ... }@local: + let + mcu = local.name; + subcfg = local.config; + in + { + options = { + enable = lib.mkEnableOption '' + building of firmware for manual flashing + ''; + enableKlipperFlash = lib.mkEnableOption '' + flashings scripts for firmware. This will add `klipper-flash-$mcu` scripts to your environment which can be called to flash the firmware. + Please check the configs at [klipper](https://github.com/Klipper3d/klipper/tree/master/config) whether your board supports flashing via `make flash` + ''; + serial = lib.mkOption { + type = lib.types.nullOr lib.types.path; + description = "Path to serial port this printer is connected to. Derived from `service.klipper.settings` by default."; + defaultText = lib.literalExpression "config.services.klipper.settings..serial"; + default = + if lib.hasAttrByPath [ "${mcu}" "serial" ] cfg.settings then cfg.settings."${mcu}".serial else null; + }; + configFile = lib.mkOption { + type = lib.types.path; + description = "Path to firmware config which is generated using `klipper-genconf`"; + }; + package = lib.mkOption { + type = lib.types.nullOr lib.types.package; + readOnly = true; + description = "Path to the built firmware package."; + }; + klipperFlashPackage = lib.mkOption { + type = lib.types.nullOr lib.types.package; + readOnly = true; + description = "Path to the built klipper-flash package."; + }; + }; + config = { + package = + if subcfg.enable then + pkgs.klipper-firmware.override { + klipper = cfg.package; + mcu = lib.strings.sanitizeDerivationName mcu; + firmwareConfig = subcfg.configFile; + } + else + null; + + klipperFlashPackage = + if subcfg.enable && subcfg.enableKlipperFlash then + pkgs.klipper-flash.override { + klipper = cfg.package; + klipper-firmware = subcfg.package; + mcu = lib.strings.sanitizeDerivationName mcu; + flashDevice = subcfg.serial; + firmwareConfig = subcfg.configFile; + } + else + null; + }; + } + ); in { imports = [ @@ -119,30 +181,17 @@ in }; firmwares = lib.mkOption { - description = "Firmwares klipper should manage"; + description = "Firmwares Klipper should manage"; default = { }; - type = - with lib.types; - attrsOf (submodule { - options = { - enable = lib.mkEnableOption '' - building of firmware for manual flashing - ''; - enableKlipperFlash = lib.mkEnableOption '' - flashings scripts for firmware. This will add `klipper-flash-$mcu` scripts to your environment which can be called to flash the firmware. - Please check the configs at [klipper](https://github.com/Klipper3d/klipper/tree/master/config) whether your board supports flashing via `make flash` - ''; - serial = lib.mkOption { - type = lib.types.nullOr path; - default = null; - description = "Path to serial port this printer is connected to. Leave `null` to derive it from `service.klipper.settings`."; - }; - configFile = lib.mkOption { - type = path; - description = "Path to firmware config which is generated using `klipper-genconf`"; - }; - }; - }); + type = lib.types.attrsOf firmwareSubmodule; + example = { + mcu = { + enable = true; + enableKlipperFlash = true; + serial = "/dev/serial/by-id/usb-Klipper_123ABCDEF"; + configFile = lib.literalExpression "./klipper-firmware.cfg"; + }; + }; }; }; }; @@ -158,16 +207,6 @@ in assertion = cfg.user != null -> cfg.group != null; message = "Option services.klipper.group is not set when services.klipper.user is specified."; } - { - assertion = - cfg.settings != null - -> lib.foldl (a: b: a && b) true ( - lib.mapAttrsToList ( - mcu: _: mcu != null -> (lib.hasAttrByPath [ "${mcu}" "serial" ] cfg.settings) - ) cfg.firmwares - ); - message = "Option services.klipper.settings.$mcu.serial must be set when settings.klipper.firmware.$mcu is specified"; - } { assertion = (cfg.configFile != null) != (cfg.settings != null); message = "You need to either specify services.klipper.settings or services.klipper.configFile."; @@ -176,7 +215,16 @@ in assertion = (cfg.configFile != null) -> (cfg.extraSettings == ""); message = "You can't use services.klipper.extraSettings with services.klipper.configFile."; } - ]; + ] + ++ lib.mapAttrsToList (mcu: firmware: { + assertion = firmware.enable -> firmware.serial != null; + message = '' + Unable to determine the serial connection for services.klipper.firmwares."${mcu}". Please set one of the following: + + - services.klipper.firmwares."${mcu}".serial + - services.klipper.settings."${mcu}".serial + ''; + }) cfg.firmwares; services.klipper = lib.mkIf cfg.octoprintIntegration { user = config.services.octoprint.user; @@ -257,37 +305,14 @@ in genconf = pkgs.klipper-genconf.override { klipper = cfg.package; }; - firmwares = lib.filterAttrs (n: v: v != null) ( - lib.mapAttrs ( - mcu: - { - enable, - enableKlipperFlash, - configFile, - serial, - }: - if enable then - pkgs.klipper-firmware.override { - klipper = cfg.package; - mcu = lib.strings.sanitizeDerivationName mcu; - firmwareConfig = configFile; - } - else - null - ) cfg.firmwares + firmwares = lib.filter (v: v != null) ( + lib.mapAttrsToList (mcu: firmware: firmware.package) cfg.firmwares + ); + firmwareFlashers = lib.filter (v: v != null) ( + lib.mapAttrsToList (mcu: firmware: firmware.klipperFlashPackage) cfg.firmwares ); - firmwareFlasher = lib.mapAttrsToList ( - mcu: firmware: - pkgs.klipper-flash.override { - klipper = cfg.package; - klipper-firmware = firmware; - mcu = lib.strings.sanitizeDerivationName mcu; - flashDevice = default cfg.firmwares."${mcu}".serial cfg.settings."${mcu}".serial; - firmwareConfig = cfg.firmwares."${mcu}".configFile; - } - ) (lib.filterAttrs (mcu: firmware: cfg.firmwares."${mcu}".enableKlipperFlash) firmwares); in - [ genconf ] ++ firmwareFlasher ++ lib.attrValues firmwares; + [ genconf ] ++ firmwares ++ firmwareFlashers; }; meta.maintainers = [ lib.maintainers.cab404