Initial commit — 2026-07-15
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# Device Identification — Support Overlay
|
||||
|
||||
## Problem
|
||||
When a customer calls support saying "my screen isn't working," we need to know WHICH of their devices we're talking to without relying on them to read a serial number off the back of a Pi.
|
||||
|
||||
## Solution
|
||||
Hidden overlay on the player that only appears when triggered. No customer sees it in normal operation.
|
||||
|
||||
## How it looks
|
||||
```
|
||||
┌──────────────────────────────────────┐
|
||||
│ │
|
||||
│ <normal content playing> │
|
||||
│ │
|
||||
│ │
|
||||
│ ┌────────────────────────────┐ │
|
||||
│ │ 🔍 DEVICE │ │
|
||||
│ │ Main Lobby Pi │ │
|
||||
│ │ Serial: 10000000a1b2c3d4 │ │
|
||||
│ │ IP: 192.168.1.50 │ │
|
||||
│ │ Playlist: Main Menu Loop │ │
|
||||
│ │ Last sync: 30s ago │ │
|
||||
│ │ Model: RPi 4 Model B 4GB │ │
|
||||
│ └────────────────────────────┘ │
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Trigger Methods (build all 3)
|
||||
|
||||
### 1. Keyboard shortcut — Ctrl+I pressed 3 times in 2s
|
||||
On-site tech can trigger it without any tools.
|
||||
|
||||
```javascript
|
||||
var keyCount = 0, keyTimer;
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.ctrlKey && e.key === 'i') {
|
||||
keyCount++;
|
||||
clearTimeout(keyTimer);
|
||||
keyTimer = setTimeout(function() { keyCount = 0; }, 2000);
|
||||
if (keyCount >= 3) { showOverlay(); keyCount = 0; }
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Remote API trigger
|
||||
I call this from the ops portal — device polls for the flag.
|
||||
|
||||
```javascript
|
||||
// Player polls every 10s
|
||||
setInterval(function() {
|
||||
fetch('/api/device/' + deviceId + '/status')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) { if (d.identify) showOverlay(); });
|
||||
}, 10000);
|
||||
```
|
||||
|
||||
API endpoint: `POST /api/devices/:id/identify` (returns `{identify: true}`, auto-clears after 30s)
|
||||
|
||||
### 3. Boot-time flag
|
||||
For when support asks customer to reboot with a specific trigger:
|
||||
|
||||
```bash
|
||||
# On the Pi
|
||||
curl -s https://signage.itpropartner.com/identify.sh | bash -s <device-id>
|
||||
# Rebooting with this arg causes the player to start in visibility mode
|
||||
./kiosk.sh --identify
|
||||
```
|
||||
|
||||
## Overlay Behavior
|
||||
- **Auto-hides** after 30 seconds
|
||||
- Can be dismissed early with another Ctrl+I
|
||||
- **No click-away** — intentionally no close button (onsite tech might fat-finger)
|
||||
- Refresh/DST resets it back to hidden
|
||||
- **No data exfiltration** — shows only device metadata, no passwords or tokens
|
||||
|
||||
## Overlay HTML (skeleton)
|
||||
```html
|
||||
<div id="deviceIdOverlay"
|
||||
style="display:none;position:fixed;bottom:16px;right:16px;
|
||||
background:rgba(0,0,0,0.88);color:#fff;padding:14px 18px;
|
||||
border-radius:10px;font-family:monospace;font-size:13px;
|
||||
line-height:1.5;z-index:99999;max-width:320px;
|
||||
backdrop-filter:blur(4px);">
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;
|
||||
font-size:15px;color:#f59e0b;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><circle cx="12" cy="8" r="1"/>
|
||||
</svg>
|
||||
Device Identity
|
||||
</div>
|
||||
<div id="devName" style="font-weight:700;margin-bottom:4px;"></div>
|
||||
<div id="devSerial" style="color:#94a3b8;font-size:12px;"></div>
|
||||
<div id="devIp" style="color:#94a3b8;font-size:12px;"></div>
|
||||
<div id="devPlaylist" style="color:#94a3b8;font-size:12px;"></div>
|
||||
<div id="devModel" style="color:#94a3b8;font-size:12px;"></div>
|
||||
<div id="devLastSync" style="color:#64748b;font-size:11px;margin-top:4px;"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Implementation Status
|
||||
📝 **Not yet built** — queued for player code phase
|
||||
@@ -0,0 +1,192 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user