noisebell/pi/README.md

3.3 KiB

noisebell

Monitors a GPIO pin on a Raspberry Pi to detect door open/close events. State changes get POSTed to a webhook endpoint. Current state is available over HTTP.

Runs on NixOS with Tailscale for networking and agenix for secrets.

Setup

1. Bootstrap

Build the SD image, flash it, and boot the Pi:

nix build .#nixosConfigurations.bootstrap.config.system.build.sdImage
dd if=result/sd-image/*.img of=/dev/sdX bs=4M status=progress

Insert the SD card into the Pi and power it on. It will connect to the Noisebridge WiFi network automatically.

2. Find the Pi

Once booted, find the Pi on the network:

# Scan the local subnet
nmap -sn 192.168.1.0/24

# Or check ARP table
arp -a

# Or check your router's DHCP leases

3. Get SSH host key

Grab the Pi's ed25519 host key and put it in secrets/secrets.nix:

ssh-keyscan <pi-ip> | grep ed25519
# secrets/secrets.nix
let
  pi = "ssh-ed25519 AAAA..."; # paste the key here
in
{
  "api-key.age".publicKeys = [ pi ];
  "inbound-api-key.age".publicKeys = [ pi ];
  "tailscale-auth-key.age".publicKeys = [ pi ];
}

4. Secrets

Create the encrypted secret files:

cd secrets
agenix -e api-key.age            # paste API key for the cache endpoint
agenix -e inbound-api-key.age    # paste API key that the cache uses to poll the Pi
agenix -e tailscale-auth-key.age # paste Tailscale auth key

5. Add SSH key

Add your SSH public key to configuration.nix:

users.users.root.openssh.authorizedKeys.keys = [
  "ssh-ed25519 AAAA..."
];

6. Deploy

nixos-rebuild switch --flake .#pi --target-host root@noisebell

Configuration

Options under services.noisebell in flake.nix:

Option Default Description
endpointUrl Webhook endpoint URL to POST state changes to
apiKeyFile Path to file containing outbound API key (agenix secret)
inboundApiKeyFile Path to file containing inbound API key for GET endpoint auth (agenix secret)
gpioPin 17 GPIO pin to monitor
debounceSecs 5 Debounce delay
port 8080 HTTP status port
retryAttempts 3 Webhook retry count
retryBaseDelaySecs 1 Base delay for exponential backoff
httpTimeoutSecs 10 Timeout for outbound webhook requests
bindAddress 0.0.0.0 Address to bind the HTTP server to
activeLow true Whether low GPIO level means open (depends on wiring)
restartDelaySecs 5 Seconds before systemd restarts on failure
watchdogSecs 30 Watchdog timeout — service is restarted if unresponsive

API

GET / — current door state:

{"status": "open", "timestamp": 1710000000}

GET /info — system health and GPIO config:

{
  "uptime_secs": 3600,
  "started_at": 1710000000,
  "cpu_temp_celsius": 42.3,
  "memory_available_kb": 350000,
  "memory_total_kb": 512000,
  "disk_total_bytes": 16000000000,
  "disk_available_bytes": 12000000000,
  "load_average": [0.01, 0.05, 0.10],
  "nixos_version": "24.11.20240308.9dcb002",
  "commit": "c6e726c",
  "gpio": {
    "pin": 17,
    "active_low": true,
    "pull": "up",
    "open_level": "low",
    "current_raw_level": "low"
  }
}

State changes (and initial state on startup) are POSTed to the configured endpoint in the same format as GET /, with an Authorization: Bearer <api-key> header.