diff --git a/modules/nixos/adb.nix b/modules/nixos/adb.nix new file mode 100644 index 00000000..ebbc2b07 --- /dev/null +++ b/modules/nixos/adb.nix @@ -0,0 +1,137 @@ +let + deviceModule = {config, lib, ...}: let + inherit (lib.options) mkOption mkEnableOption; + in { + options = with lib.types; { + enable = mkEnableOption "adb device" // { + default = true; + }; + uphold = mkOption { + type = bool; + default = true; + }; + serial = mkOption { + type = str; + }; + }; + }; +in { + config, + gensokyo-zone, + utils, + pkgs, + lib, + ... +}: let + inherit (lib.options) mkOption mkPackageOption mkEnableOption; + inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault; + inherit (lib.attrsets) filterAttrs mapAttrsToList; + inherit (lib.cli) toGNUCommandLine; + inherit (utils) escapeSystemdExecArgs escapeSystemdPath; + inherit (gensokyo-zone.lib) mapOptionDefaults; + cfg = config.services.adb; + enabledDevices = filterAttrs (_: device: device.enable) cfg.devices; +in { + options.services.adb = with lib.types; { + enable = mkEnableOption "adb server"; + package = mkPackageOption pkgs "android-tools" {}; + rulesPackage = mkPackageOption pkgs "android-udev-rules" {}; + user = mkOption { + type = str; + default = "adb"; + }; + port = mkOption { + type = port; + default = 5037; + }; + extraArguments = mkOption { + type = listOf str; + default = []; + }; + settings = mkOption { + type = attrsOf (oneOf [ str int (nullOr bool) ]); + }; + devices = mkOption { + type = attrsOf (submoduleWith { + modules = [deviceModule]; + specialArgs = { + inherit gensokyo-zone; + nixosConfig = config; + }; + }); + default = {}; + }; + }; + config = let + confService.services.adb = { + settings = mapOptionDefaults { + H = config.networking.hostName; + P = cfg.port; + }; + }; + conf.services.udev.packages = [ cfg.rulesPackage ]; + conf.environment.systemPackages = [ cfg.package ]; + conf.users.groups.adbusers = {}; + conf.systemd.services.adb = { + upholds = let + upheldDevices = filterAttrs (_: device: device.uphold) enabledDevices; + in mapAttrsToList (_: device: "adb-device@${escapeSystemdPath device.serial}.service") upheldDevices; + after = ["network.target"]; + wantedBy = ["multi-user.target"]; + serviceConfig = { + Type = mkOptionDefault "forking"; + ExecStart = let + args = toGNUCommandLine { } cfg.settings ++ cfg.extraArguments; + in [ + "${cfg.package}/bin/adb start-server ${escapeSystemdExecArgs args}" + ]; + ExecStop = [ + "${cfg.package}/bin/adb kill-server" + ]; + WorkingDirectory = "/var/lib/adb"; + StateDirectory = "adb"; + RuntimeDirectory = "adb"; + User = cfg.user; + }; + }; + conf.systemd.services."adb-device@" = rec { + requisite = [ "adb.service" ]; + partOf = requisite; + after = requisite; + environment = mapOptionDefaults { + ANDROID_SERIAL = "%I"; + }; + path = [cfg.package pkgs.coreutils]; + serviceConfig = mapOptionDefaults { + User = cfg.user; + }; + script = '' + set -eu + + while true; do + sleep 1 + DEVICE_ONLINE= + if ADB_STATE=$(adb get-state 2>/dev/null); then + if [[ $ADB_STATE == device ]]; then + DEVICE_ONLINE=1 + fi + fi + if [[ -n $DEVICE_ONLINE ]] || timeout 5 adb connect $ANDROID_SERIAL; then + sleep 10 + else + sleep 4 + fi + done + ''; + }; + conf.users.users.adb = mkIf (cfg.user == "adb") { + isSystemUser = true; + group = mkDefault "adbusers"; + home = mkDefault "/var/lib/adb"; + }; + in + mkMerge [ + confService + (mkIf cfg.enable conf) + ]; +} diff --git a/modules/nixos/home-assistant.nix b/modules/nixos/home-assistant.nix index 0b4eaab8..905f3565 100644 --- a/modules/nixos/home-assistant.nix +++ b/modules/nixos/home-assistant.nix @@ -317,4 +317,9 @@ in { ) ]; }; + config.users.users.hass = mkIf cfg.enable { + extraGroups = mkIf (elem "androidtv" cfg.extraComponents && (config.programs.adb.enable || config.services.adb.enable)) [ + "adbusers" + ]; + }; } diff --git a/modules/system/exports/adb.nix b/modules/system/exports/adb.nix new file mode 100644 index 00000000..264f6107 --- /dev/null +++ b/modules/system/exports/adb.nix @@ -0,0 +1,26 @@ +{ + lib, + gensokyo-zone, + ... +}: let + inherit (gensokyo-zone.lib) mkAlmostOptionDefault; + inherit (lib.modules) mkIf; +in { + config.exports.services.adb = {config, ...}: { + displayName = mkAlmostOptionDefault "ADB"; + nixos = { + serviceAttr = "adb"; + assertions = mkIf config.enable [ + (nixosConfig: { + assertion = config.ports.default.port == nixosConfig.services.adb.port; + message = "port mismatch"; + }) + ]; + }; + defaults.port.listen = mkAlmostOptionDefault "localhost"; + ports.default = { + port = mkAlmostOptionDefault 5037; + transport = "tcp"; + }; + }; +} diff --git a/nixos/adb.nix b/nixos/adb.nix new file mode 100644 index 00000000..946f8f4f --- /dev/null +++ b/nixos/adb.nix @@ -0,0 +1,26 @@ +{ + config, + lib, + ... +}: let + inherit (lib.modules) mkIf mkDefault; + cfg = config.services.adb; +in { + services.adb = { + enable = mkDefault true; + settings = { + #a = mkDefault true; + }; + devices = { + bedroom-tv.serial = "10.1.1.67:5555"; + }; + }; + systemd.services = mkIf cfg.enable { + adb = { + environment.ADB_TRACE = mkDefault (toString [ "adb" ]); + }; + }; + networking.firewall.interfaces.lan = mkIf (cfg.enable && cfg.settings.a or false == true) { + allowedTCPPorts = [ cfg.port ]; + }; +} diff --git a/systems/tei/default.nix b/systems/tei/default.nix index 1386a750..70b4e62f 100644 --- a/systems/tei/default.nix +++ b/systems/tei/default.nix @@ -18,6 +18,7 @@ _: { zigbee2mqtt.enable = true; barcodebuddy.enable = true; postgresql.enable = true; + adb.enable = true; }; }; } diff --git a/systems/tei/nixos.nix b/systems/tei/nixos.nix index 1c2dfc6f..cc1d9a94 100644 --- a/systems/tei/nixos.nix +++ b/systems/tei/nixos.nix @@ -15,6 +15,7 @@ in { nixos.cloudflared nixos.postgres nixos.nginx + nixos.adb nixos.access.home-assistant nixos.access.zigbee2mqtt nixos.access.grocy