DEVELOPMENT ENVIRONMENT

~liljamo/install-garmin-watch-face

3732fc05916cfc48f832c6d8cc21536723483e31 — Jonni Liljamo 2 days ago master
feat: init
6 files changed, 146 insertions(+), 0 deletions(-)

A .envrc
A .gitignore
A README.md
A flake.lock
A flake.nix
A main.ha
A  => .envrc +1 -0
@@ 1,1 @@
use_flake

A  => .gitignore +2 -0
@@ 1,2 @@
/.direnv/
/test/

A  => README.md +11 -0
@@ 1,11 @@
# install-garmin-watch-face

A small program to install a Garmin watch face.

Like, really small, because it's actually just moving a file to a mounted
directory. Yeah...

Depends on [simple-mtpfs](https://github.com/phatina/simple-mtpfs).

Maybe make hare bindings for https://github.com/libmtp/libmtp and do it
directly, instead of depending on runtime external binaries.

A  => flake.lock +27 -0
@@ 1,27 @@
{
  "nodes": {
    "nixpkgs": {
      "locked": {
        "lastModified": 1752517496,
        "narHash": "sha256-nHo/Gle6z6lMZc5kGtMOBKkVw1/Z9ilYdV2e14+Q6bU=",
        "owner": "liljamo",
        "repo": "nixpkgs",
        "rev": "98bc1dc9a61dc6350ab81625410bab51f28d36ac",
        "type": "github"
      },
      "original": {
        "owner": "liljamo",
        "ref": "hare-0.25.2",
        "repo": "nixpkgs",
        "type": "github"
      }
    },
    "root": {
      "inputs": {
        "nixpkgs": "nixpkgs"
      }
    }
  },
  "root": "root",
  "version": 7
}

A  => flake.nix +19 -0
@@ 1,19 @@
{
  inputs = {
    #nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
    nixpkgs.url = "github:liljamo/nixpkgs/hare-0.25.2";
  };

  outputs = inputs @ {...}: let
    pkgs = inputs.nixpkgs.legacyPackages."x86_64-linux";
  in {
      devShells.x86_64-linux.default = pkgs.mkShell {
        buildInputs = with pkgs;
          [
            hare

            simple-mtpfs
          ];
      };
    };
}

A  => main.ha +86 -0
@@ 1,86 @@
use fs;
use getopt;
use log;
use os;
use os::exec;
use path;
use strings;
use temp;

const GARMIN_APPS = "/GARMIN/APPS/";

export fn main() void = {
	const cmd = getopt::parse(os::args,
		"garmin watch face installer",
		('s', "skip MTP mount (testing)"),
		"file",
	);
	defer getopt::finish(&cmd);

	let skip_mount = false;
	for (let opt .. cmd.opts) {
		switch (opt.0) {
		case 's' =>
			skip_mount = true;
		case => abort();
		};
	};

	let file = "";
	if (len(cmd.args) < 1) {
		log::fatal("provide a file");
	} else {
		file = os::resolve(cmd.args[0]);
		if (!os::exists(file)) {
			log::fatal("provided file doesn't exist");
		};
		let parts = strings::split(file, ".")!;
		defer free(parts);
		if (parts[len(parts) - 1] != "prg") {
			log::fatal("file does not seem to be a .prg file");
		};
	};

	const temp_dir = temp::dir();
	defer os::rmdirall(temp_dir)!;
	log::printfln("created temporary directory: {}", temp_dir);

	if (skip_mount) {
		os::mkdirs(strings::concat(temp_dir, GARMIN_APPS)!, fs::mode::USER_RWX)!;
	} else {
		mount_mtp(temp_dir);
	};

	const apps_dir = strings::concat(temp_dir, GARMIN_APPS)!;
	if (!os::exists(apps_dir)) {
		log::fatal("/GARMIN/APPS doesn't exist in mount");
	};

	const file_dest = strings::concat(apps_dir, "face.prg")!;
	log::printfln("moving {} to {}", file, file_dest);
	os::move(file, file_dest)!;

	// Remember to unmount the MTP fs.
	if (!skip_mount) {
		umount_mtp(temp_dir);
	};

	log::println("your new watch face should now show up");
};

fn mount_mtp(temp_dir: str) void = {
	log::println("mounting MTP");
	let cmd = exec::cmd("simple-mtpfs", temp_dir)!;
	let proc = exec::start(&cmd)!;
	let status = exec::wait(&proc)!;
	if (status.status != 0) {
		log::fatal("simple-mtpfs failed, most likely a permission issue");
	};
};

fn umount_mtp(temp_dir: str) void = {
	log::println("umounting MTP");
	let cmd = exec::cmd("fusermount", "-u", temp_dir)!;
	let proc = exec::start(&cmd)!;
	let status = exec::wait(&proc)!;
};