139 lines
3.4 KiB
Markdown
139 lines
3.4 KiB
Markdown
# Pi
|
|
|
|
Rust service and NixOS config for the Raspberry Pi at Noisebridge. Reads a magnetic door sensor via GPIO, serves the current state over HTTP, and pushes changes to the cache service.
|
|
|
|
Runs NixOS with Tailscale for remote access and agenix for secrets.
|
|
|
|
## How it works
|
|
|
|
The service watches a GPIO pin for rising/falling edges with a configurable debounce. When the door state changes, it:
|
|
|
|
1. Updates in-memory state (atomics)
|
|
2. POSTs `{"status": "open", "timestamp": ...}` to the cache service with a Bearer token
|
|
3. Retries with exponential backoff on failure
|
|
|
|
On startup it also syncs the initial state.
|
|
|
|
## Setup
|
|
|
|
### Prerequisites
|
|
|
|
If you're building on an x86_64 machine, you need binfmt emulation for aarch64. On NixOS, add this to your system config and rebuild:
|
|
|
|
```nix
|
|
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
|
|
```
|
|
|
|
### 1. Flash the SD card
|
|
|
|
```sh
|
|
nix build .#nixosConfigurations.bootstrap.config.system.build.sdImage
|
|
dd if=result/sd-image/*.img of=/dev/sdX bs=4M status=progress
|
|
```
|
|
|
|
Boot the Pi. It connects to the Noisebridge WiFi automatically and is discoverable via mDNS as `noisebridge-pi.local`.
|
|
|
|
### 2. Find the Pi
|
|
|
|
```sh
|
|
ping noisebridge-pi.local
|
|
```
|
|
|
|
### 3. SSH host key
|
|
|
|
Grab the key and add it to `secrets/secrets.nix`:
|
|
|
|
```sh
|
|
ssh-keyscan noisebridge-pi.local 2>/dev/null | grep ed25519
|
|
```
|
|
|
|
```nix
|
|
# secrets/secrets.nix
|
|
let
|
|
pi = "ssh-ed25519 AAAA...";
|
|
in
|
|
{
|
|
"api-key.age".publicKeys = [ pi ];
|
|
"inbound-api-key.age".publicKeys = [ pi ];
|
|
"tailscale-auth-key.age".publicKeys = [ pi ];
|
|
}
|
|
```
|
|
|
|
### 4. Create secrets
|
|
|
|
```sh
|
|
cd secrets
|
|
agenix -e api-key.age # key for POSTing to the cache
|
|
agenix -e inbound-api-key.age # key the cache uses to poll us
|
|
agenix -e tailscale-auth-key.age # tailscale auth key
|
|
```
|
|
|
|
### 5. SSH access
|
|
|
|
Add your public key to `configuration.nix`:
|
|
|
|
```nix
|
|
users.users.root.openssh.authorizedKeys.keys = [
|
|
"ssh-ed25519 AAAA..."
|
|
];
|
|
```
|
|
|
|
### 6. Deploy
|
|
|
|
```sh
|
|
nixos-rebuild switch --flake .#pi --target-host root@noisebell
|
|
```
|
|
|
|
## Configuration
|
|
|
|
Options under `services.noisebell` in `flake.nix`:
|
|
|
|
| Option | Default | Description |
|
|
|---|---|---|
|
|
| `endpointUrl` | required | Webhook URL to POST state changes to |
|
|
| `apiKeyFile` | required | Outbound API key file (agenix secret) |
|
|
| `inboundApiKeyFile` | required | Inbound API key file for GET auth |
|
|
| `gpioPin` | `17` | GPIO pin number |
|
|
| `debounceSecs` | `5` | Debounce delay in seconds |
|
|
| `port` | `8080` | HTTP server port |
|
|
| `retryAttempts` | `3` | Webhook retry count |
|
|
| `retryBaseDelaySecs` | `1` | Exponential backoff base delay |
|
|
| `httpTimeoutSecs` | `10` | Outbound request timeout |
|
|
| `bindAddress` | `0.0.0.0` | HTTP bind address |
|
|
| `activeLow` | `true` | Low GPIO = door open (depends on wiring) |
|
|
| `restartDelaySecs` | `5` | systemd restart delay on failure |
|
|
| `watchdogSecs` | `30` | systemd watchdog timeout |
|
|
|
|
## API
|
|
|
|
All endpoints require `Authorization: Bearer <token>`.
|
|
|
|
**`GET /`** — door state
|
|
|
|
```json
|
|
{"status": "open", "timestamp": 1710000000}
|
|
```
|
|
|
|
**`GET /info`** — system health + GPIO config
|
|
|
|
```json
|
|
{
|
|
"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"
|
|
}
|
|
}
|
|
```
|