DEV Community

Sasha Devol
Sasha Devol

Posted on

Circles of Truth: Overcomplicating simple commands

While fixing my air conditioner, I needed to frequently debug what HTTP requests my ESP32 was sending to my hue bridge's API when it activates or deactivates my 3d printed dremel-based pump to prevent excess condensate from flooding my flat and jeopardizing my housing situation. If you use nix/home-manager, then my approach could be of perverse interest to you. If not, then feel free to gawk at how complicated I can implement a single alias command.

When my ESP32 detects a pulled-down voltage at GPIO4, this means that the water level has reached the upper limit in my condensation collection container. This is because i have GND ran out to the bottom of the bin, and GPIO4 is set to INPUT_PULLUP. GPIO3 is set to the same but I do not turn on the pump for this pin. Instead, i use that pin to know when to turn off the pump once the pump has been turned on at the upper limit.

When the ESP32 sends a request to my local hue bridge to activate the smartplug where I have my dremel pump plugged in, and nothing happens, I need to figure out what HTTP request is actually going out. I use requestbin for this. The publicly available service became sporadically available many years back, but you can still run it via docker. Once its running, it gives me a URL i can substitute for the Hue bridge API endpoint, I ca inspect

The command for this is

docker run --rm -it -p 8000:8000 weshigbee/requestbin
Enter fullscreen mode Exit fullscreen mode

That's just a little too unwieldy to remember, so I'd like to make a permanent utility to run it. While writing this, i realize i could have just aliased it and added it to my zsh config inside my home-manager config under programs.zsh.shellAliases However, in my early-morning sleepy haze, I opted to create a solution as arguably over-engineered as it may be forgivingly considered succinctly practical. I have ~/.local/bin in my home.sessionPath. I have slowly been evolving how I manage these utilities for the last few months. The primary difference between files in ~/.local/bin and a shell alias is that files in that directory can be shell scripts. For example ~/.local/bin/update used to be a shell alias bound to NIXPKGS\_ALLOW\_INSECURE=1 darwin-rebuild switch --flake "$HOME/.nixpkgs#$(hostname)" --impure Today, it's a shell script:

cd ~/.nixpkgs
# capture local binary utilities
for file in `find ~/.local/bin/ -maxdepth 1 -type f`; do 
    mv $file ~/.nixpkgs/user-specific/sdevol/.local/bin/
done
git add user-specific/sdevol/.local/bin

# capture the espanso configuration
cp -R ~/.config/espanso/* ~/.nixpkgs/user-specific/sdevol/.config/espanso/
git add ~/.nixpkgs/user-specific/sdevol/.config/espanso

# do the thing
NIXPKGS_ALLOW_INSECURE=1 darwin-rebuild switch --flake "$HOME/.nixpkgs#$(hostname)" --impure 
Enter fullscreen mode Exit fullscreen mode

My nix configuration lives in ~/.nixpkgs. Notice that we are moving files from ~/.local/bin into the nix repo under ~/.nixpkgs/user-specific/sdevol/.local/bin. As you might have began to suspect, there's some plumbing inside ~/.nixpkgs/user-specific/sdevol/home.nix that connects some more dots.

# let 
  localBinDir = ./.local/bin; # this is relative to the current file, so this is ~/.nixpkgs/user-specific/sdevol/.local/bin
  localBinFiles = builtins.attrNames (builtins.readDir localBinDir);
  mkLocalBin = name: {
    source = ./.local/bin/${name};
    recursive = true;
  };
in rec {
  manual.manpages.enable = false;
  home = {
    username = "tastycode";
    file =
      builtins.listToAttrs (map (name: {
          name = ".local/bin/${name}";
          value = mkLocalBin name;
        })
        localBinFiles)
      // {
        ".config/zed/settings.json" = {
          source = ./zed.settings.json;
          recursive = true;
        };
        ".config/espanso" = {
          source = config.lib.file.mkOutOfStoreSymlink .config/espanso;
          recursive = true;
        };
Enter fullscreen mode Exit fullscreen mode

So, when I type update.

  1. Any non-symlinks are moved into the nix configuration
  2. My nix configuration is rebuilt, within which the nix configuration iterates through all files in its local directory user-specific/sdevol/.local/bin
  3. A home.file.\* reference is generated with a symlink configuration for each.

This sequence thus replaces non-symlinked files in ~/.local/bin with symlinks to version-controlled references to the same. Therefore, once i have decided i want to make a permanent command for docker run --rm -it -p 8000:8000 weshigbee/requestbin. All I need to do is , press up on my keyboard so that the docker command is at the prompt and modify the command to read. echo docker run --rm -it -p 8000:8000 weshigbee/requestbin > ~/.local/bin/requestbin then ls ~/.local/bin reads

ls -l ~/.local/bin                          ✔  26ms   18.1.0   10:24:11 
lrwxr-xr-x  - tastycode 16 Aug 09:49 readlinks -> /nix/store/m2aiz6gszyk3i4hbqn93dpwpsi6f805p-home-manager-files/.local/bin/readlinks
.rw-r--r-- 54 tastycode 16 Aug 10:24 requestbin
lrwxr-xr-x  - tastycode 16 Aug 09:49 screensnap -> /nix/store/m2aiz6gszyk3i4hbqn93dpwpsi6f805p-home-manager-files/.local/bin/screensnap
lrwxr-xr-x  - tastycode 16 Aug 09:49 tastyspace -> /nix/store/m2aiz6gszyk3i4hbqn93dpwpsi6f805p-home-manager-files/.local/bin/tastyspace
lrwxr-xr-x  - tastycode 16 Aug 09:49 update -> /nix/store/m2aiz6gszyk3i4hbqn93dpwpsi6f805p-home-manager-files/.local/bin/update
Enter fullscreen mode Exit fullscreen mode

Now, i just run update Now, requestbin is in ~/.local/bin/requestbin but it has been swapped out with a symlink to my nix config's manifested version of the script.

lrwxr-xr-x - tastycode 16 Aug 10:26 requestbin -> /nix/store/vgrdrcnjyhykkx1xq29kg6g2jhbqg0hx-home-manager-files/.local/bin/requestbin
Enter fullscreen mode Exit fullscreen mode

Honestly, that's less keystrokes than adding a shellAlias. If you aren't sold on using nix to manage your system's configuration, this seems overcomplicated. If you use nix, then you are already probably frustrated at keeping your nix configuration in sync with quick little optimizations you do on a regular basis. With nix, everything is source controlled. If you are a dotfiler, then you would still have to commit your changes. I guess that's true in my solution as well. The git add in my update is probably the most dubious element of this entire schrade. That is unless, you are using jj.

Top comments (0)