# Setup card generator

`scripts/generate-setup-card.py` produces printable 4x6 setup cards for fulfillment: drop one in the box with each ODS unit so the recipient can scan two QR codes without typing anything. The default card opens the first-run wizard; `--mode factory-owner` prints the owner-card handoff that opens ODS Talk.

## What it generates

A portrait card with:

1. **Top band** - "ODS" wordmark, the unit's mDNS name (e.g. `ods.local`), and a one-line tagline.
2. **Two QR codes side by side:**
   - **Left - JOIN WI-FI.** Encodes the standard `WIFI:T:WPA;S:<ssid>;P:<password>;;` format that iOS Camera and Android auto-recognize.
   - **Right - OPEN SETUP or OPEN ODS TALK.** In default setup mode this is a setup URL such as `http://192.168.7.1/setup`. In factory-owner mode this is the owner magic-link URL generated by Setup / Owner.
3. **Plain-text fallback block** - SSID, password, and URL printed verbatim for phones that will not scan the QR.
4. **Footer** - "ODS is open-source - light-heart-labs.com", plus an optional per-unit serial number.

Card is 1200x1800 px at 300 DPI = exactly 4x6 inches. PNG is the default output; PDF is available with `--format pdf` or a `.pdf` output path.

## Requirements

`pip install 'qrcode[pil]'`

This is an operator-side tool, not a runtime service, so the deps are not bundled with any ODS container.

## Usage

Setup wizard card:

```bash
python3 scripts/generate-setup-card.py \
  --ssid 'ODS-Setup-A4F2' \
  --password 'xxxxxxxx' \
  --setup-url 'http://192.168.7.1/setup' \
  --device-name 'ods-a4f2.local' \
  --serial 'DRM-2026-A4F2' \
  --output cards/card-A4F2.png
```

Factory owner card:

```bash
python3 scripts/generate-setup-card.py \
  --mode factory-owner \
  --ssid 'ODS-Setup-A4F2' \
  --password 'xxxxxxxx' \
  --owner-url 'http://auth.ods-a4f2.local/magic-link/OWNER_TOKEN' \
  --device-name 'ods-a4f2.local' \
  --serial 'DRM-2026-A4F2' \
  --output cards/owner-card-A4F2.pdf
```

Flags:

| Flag | Required | Notes |
|---|---|---|
| `--mode` | no | `setup` by default; use `factory-owner` for Wi-Fi + owner ODS Talk QR cards. |
| `--ssid` | yes | Wi-Fi SSID of the device's setup AP. |
| `--password` | no | Wi-Fi password. Omit for open networks; the Wi-Fi QR will use `T:nopass` automatically. |
| `--security` | no | `WPA` (default), `WEP`, or `nopass`. |
| `--setup-url` | setup mode | URL the QR opens, e.g. `http://192.168.7.1/setup`. |
| `--owner-url` | factory-owner mode | Owner magic-link URL created from Setup / Owner. Treat it as a physical credential. |
| `--device-name` | no | mDNS hostname printed on the card. Defaults to `ods.local`. |
| `--serial` | no | Optional serial / batch ID printed in the footer right corner. |
| `--format` | no | `png` or `pdf`. If omitted, `.pdf` output paths write PDF; everything else writes PNG. |
| `--output` / `-o` | yes | Output path. Parent directory is created if missing. |

Exit codes: `0` on success, `2` for missing dependencies or invalid flags.

## Batch generation

Looping the script over a list of pre-baked AP credentials is the recommended fulfillment flow:

```bash
while IFS=',' read -r ssid password serial owner_url; do
  python3 scripts/generate-setup-card.py \
    --mode factory-owner \
    --ssid "$ssid" \
    --password "$password" \
    --owner-url "$owner_url" \
    --device-name "ods-$(printf '%s' "$serial" | tr '[:upper:]' '[:lower:]').local" \
    --serial "$serial" \
    --output "cards/$serial.pdf"
done < unit-credentials.csv
```

## Security notes

- The plaintext AP password is on the card by design. Treat the card as a physical credential.
- A factory owner URL is a permanent credential until revoked. Print only the intended card, do not publish screenshots, and revoke/reprint from Setup / Owner when a card is lost or replaced.
- Factory owner cards open the mobile ODS Talk portal. It is backed by Hermes, but it avoids the full advanced Hermes interface for the recipient.
- The setup URL does not carry a credential. It points at the device's setup-mode IP; if the recipient has not joined the AP yet, the URL times out.
- Serial / batch identifiers are optional and intended for fulfillment, not authentication.

## Limitations / future work

- **No splash logo.** The wordmark is set in the system bold font.
- **No multi-language.** Text is English-only.
- **No Hidden SSID flag.** Standard Wi-Fi QR supports `H:true;`. Today the script hardcodes `H:false`.
