193 lines
6.1 KiB
Markdown
193 lines
6.1 KiB
Markdown
# Raspberry Pi Provisioning — Zero-Touch Player Setup
|
|
|
|
## Goal
|
|
Customer unboxes Pi → plugs in power → content plays. That's it. No keyboard, no monitor, no SSH.
|
|
|
|
## Provisioning Flow
|
|
|
|
### Step 1: Image the SD Card (We Do This)
|
|
Using Raspberry Pi Imager (CLI version for batch flashing):
|
|
|
|
```bash
|
|
# One-time image prep
|
|
cat << 'EOF' > /tmp/signage-firstboot.sh
|
|
#!/bin/bash
|
|
# Runs on first boot only — sets up the player
|
|
|
|
# 1. Set hostname
|
|
hostnamectl set-hostname signage-player
|
|
|
|
# 2. Enable auto-login to console (required for kiosk mode)
|
|
raspi-config nonint do_boot_behaviour B2
|
|
|
|
# 3. Install dependencies
|
|
apt-get update && apt-get install -y chromium-browser unclutter x11-xserver-utils
|
|
|
|
# 4. Create player service
|
|
cat > /etc/systemd/system/signage-player.service << 'SRVEOF'
|
|
[Unit]
|
|
Description=Digital Signage Player
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
User=pi
|
|
Environment=DISPLAY=:0
|
|
ExecStartPre=/usr/bin/sleep 5
|
|
ExecStart=/usr/bin/bash /home/pi/player.sh
|
|
Restart=always
|
|
RestartSec=10
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
SRVEOF
|
|
|
|
# 5. Create player script
|
|
cat > /home/pi/player.sh << 'PLAYEOF'
|
|
#!/bin/bash
|
|
# Kiosk player — registers if new, plays if registered
|
|
|
|
DEVICE_ID_FILE="/home/pi/.device-id"
|
|
TOKEN_FILE="/home/pi/.device-token"
|
|
API="https://signage.itpropartner.com/api"
|
|
REG_CODE="${1:-}"
|
|
|
|
# Hide mouse cursor
|
|
unclutter -idle 0.1 &
|
|
|
|
# Turn off screen blanking
|
|
xset s off
|
|
xset s noblank
|
|
xset -dpms
|
|
|
|
# Register if not yet registered
|
|
if [ ! -f "$DEVICE_ID_FILE" ] && [ -n "$REG_CODE" ]; then
|
|
SERIAL=$(cat /proc/cpuinfo | grep Serial | head -1 | awk '{print $3}')
|
|
RESPONSE=$(curl -s -X POST "$API/devices/register" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"code\":\"$REG_CODE\",\"serial\":\"$SERIAL\"}")
|
|
echo "$RESPONSE" | python3 -c "import sys,json;d=json.load(sys.stdin);open('$DEVICE_ID_FILE','w').write(str(d['id']));open('$TOKEN_FILE','w').write(d['token'])" 2>/dev/null
|
|
fi
|
|
|
|
# Read device ID
|
|
DEVICE_ID=$(cat "$DEVICE_ID_FILE" 2>/dev/null || echo "")
|
|
|
|
# Launch kiosk browser
|
|
if [ -n "$DEVICE_ID" ]; then
|
|
chromium-browser --kiosk \
|
|
--no-sandbox \
|
|
--disable-infobars \
|
|
--disable-session-crashed-bubble \
|
|
--disable-features=Translate \
|
|
--noerrdialogs \
|
|
"$API/player/$DEVICE_ID"
|
|
else
|
|
# Show registration screen with QR code
|
|
chromium-browser --kiosk \
|
|
--no-sandbox \
|
|
"$API/player/register?code=$REG_CODE"
|
|
fi
|
|
PLAYEOF
|
|
|
|
chmod +x /home/pi/player.sh
|
|
chown pi:pi /home/pi/player.sh
|
|
|
|
# 6. Enable service
|
|
systemctl enable signage-player.service
|
|
|
|
# 7. Clean up (remove self)
|
|
rm /boot/signage-firstboot.sh
|
|
EOF
|
|
```
|
|
|
|
### Step 2: Flash + Inject Registration (We Do)
|
|
```bash
|
|
# Using Raspberry Pi Imager CLI on your machine
|
|
rpi-imager-cli --write raspios-lite.img --device /dev/sdX
|
|
|
|
# Mount boot partition and inject first-boot script
|
|
mount /dev/sdX1 /mnt
|
|
cp /tmp/signage-firstboot.sh /mnt/
|
|
umount /mnt
|
|
```
|
|
|
|
### Step 3: Ship to Customer
|
|
Pi arrives at customer location. They plug in:
|
|
1. Power (USB-C)
|
|
2. HDMI to TV
|
|
3. Ethernet (or WiFi pre-configured)
|
|
|
|
**That's it.** No keyboard needed. No monitor for setup.
|
|
|
|
## What the Customer Sees
|
|
|
|
### First Boot (Unregistered)
|
|
```
|
|
┌──────────────────────────────────────┐
|
|
│ │
|
|
│ DIGITAL SIGNAGE │
|
|
│ │
|
|
│ ┌────────────────────────────┐ │
|
|
│ │ This screen needs to be │ │
|
|
│ │ activated. │ │
|
|
│ │ │ │
|
|
│ │ Registration Code: │ │
|
|
│ │ SIGN-4K7P-M9X2 │ │
|
|
│ │ │ │
|
|
│ │ [████████████░░░░░░] │ │
|
|
│ │ Waiting for activation...│ │
|
|
│ └────────────────────────────┘ │
|
|
│ │
|
|
└──────────────────────────────────────┘
|
|
```
|
|
|
|
### After Registration (Normal Operation)
|
|
```
|
|
┌──────────────────────────────────────┐
|
|
│ │
|
|
│ <content plays fullscreen> │
|
|
│ │
|
|
│ │
|
|
│ │
|
|
│ │
|
|
│ │
|
|
│ │
|
|
└──────────────────────────────────────┘
|
|
```
|
|
|
|
## Recovery / Re-provisioning
|
|
|
|
### If Pi needs to be re-registered (different customer):
|
|
```bash
|
|
# On Pi via SSH or USB keyboard:
|
|
rm /home/pi/.device-id /home/pi/.device-token
|
|
sudo reboot
|
|
```
|
|
Then register via the CMS with a new code.
|
|
|
|
### If customer wants to move Pi to a different TV:
|
|
Just plug HDMI into the new TV. Pi auto-detects resolution via EDID. No config needed.
|
|
|
|
### If SD card dies:
|
|
Flash a new one (same image) → insert → Pi boots → waiting for registration → activate from CMS. 5 minutes total.
|
|
|
|
## Startup Behavior Summary
|
|
| Event | What Happens |
|
|
|---|---|
|
|
| Power on | Boot → auto-login → start player service |
|
|
| Network connected | Register or poll for playlist |
|
|
| Content fetched | Fullscreen Chromium kiosk |
|
|
| Signal loss | Content cached locally, keeps playing |
|
|
| Power loss | Auto-restart on power return |
|
|
| Reboot | Same as power on |
|
|
| SD card failure | Replace card, re-register |
|
|
| TV resolution change | Auto-detected via HDMI EDID |
|
|
|
|
## Provisioning Checklist (For Us)
|
|
- [ ] Prepare base Raspberry Pi OS lite image
|
|
- [ ] Add first-boot script (auto-login, dependencies, service)
|
|
- [ ] Create player kiosk launch script
|
|
- [ ] Configure Chromium kiosk flags
|
|
- [ ] Test: flash → boot → play → power loss → reboot → play again
|
|
- [ ] Package for batch flashing (10+ Pis at once)
|