Status: Current Last Updated: 2025-01-09 Protocol Version: 3.0
v2.0 (Old): Server returned single highest-priority content. Driver managed priority queue and interruptions.
v3.0 (New): Server returns complete ordered playlist. Driver plays sequentially (simple FIFO).
| Feature | v2.0 | v3.0 |
|---|---|---|
| Response field | content: Content | playlist: Content[] |
| Status values | "updated" | "no_change" | "clear" | "updated" | "clear" |
| Priority handling | Driver-side queue | Server-side ordering |
| Interruptions | Driver pauses/resumes | None (server controls order) |
| PlaybackMode fields | loop, loop_count, priority, interruptible | loop, loop_count only |
Purpose: Driver polls for complete playlist
Method: GET
Headers:
User-Agent: FlipDot-Driver/3.0
Content-Type: application/json
Authorization: Bearer {token}
Status Codes:
200 OK - Playlist returned401 Unauthorized - Auth failed500 Internal Server Error - Server errorBody:
interface ContentResponse {
status: "updated" | "clear";
playlist: Content[]; // Ordered list, first plays immediately
poll_interval_ms: number; // >= 1000
}
Example (Multiple items):
{ "status": "updated", "playlist": [ { "content_id": "urgent-alert", "frames": [{ "data_b64": "...", "width": 28, "height": 14, "duration_ms": 5000 }], "playback": { "loop": false } }, { "content_id": "clock-12:00", "frames": [{ "data_b64": "...", "width": 28, "height": 14, "duration_ms": null }], "playback": { "loop": false } } ], "poll_interval_ms": 30000 }
Example (Clear):
{ "status": "clear", "playlist": [], "poll_interval_ms": 30000 }
interface ContentResponse {
status: "updated" | "clear";
playlist: Content[];
poll_interval_ms: number;
}
Rules:
status is "updated", playlist SHOULD be non-empty (can be empty if server has no content)status is "clear", playlist MUST be emptypoll_interval_ms MUST be >= 1000interface Content {
content_id: string;
frames: Frame[];
playback?: PlaybackMode;
metadata?: object;
}
Rules:
frames MUST contain 1-1000 framesinterface Frame {
data_b64: string;
width: number;
height: number;
duration_ms?: number | null;
metadata?: object;
}
Rules:
data_b64 MUST be valid base64 (little-endian packed bits)width and height MUST match display (validated by driver)duration_ms of null or 0 means display indefinitelyinterface PlaybackMode {
loop?: boolean;
loop_count?: number | null;
}
Rules:
loop_count can only be set if loop is truepriority and interruptible removedpoll_interval_ms intervalsduration_msOn status: "updated":
playlist[0].content_id matches current content ID, preserve frame timingplaylist[0]On status: "clear":
// Server maintains content sources with priorities
const sources = [
{ id: "clock", priority: 10, generate: () => generateClock() },
{ id: "alert", priority: 90, generate: () => generateAlert() },
];
// Generate playlist
async function generatePlaylist(): Promise<Content[]> {
// Get all active content
const content = await Promise.all(
sources.map(s => s.generate())
);
// Sort by priority (highest first)
content.sort((a, b) => b.priority - a.priority);
// Return ordered list
return content;
}
content: Content to playlist: Content[]status: "no_change" logicpriority and interruptible from Content objectsplaylist: Content[] instead of content: Contentplaylist[0] has same IDSame as v2.0: Little-endian packed bits, row-major order, base64 encoded.
See v2.0 spec Section 4 for details.
Same as v2.0: Bearer token or API key authentication required.
Valid minimal response:
{ "status": "updated", "playlist": [ { "content_id": "test", "frames": [{ "data_b64": "AAAA", "width": 28, "height": 14 }] } ], "poll_interval_ms": 30000 }
Test server compliance:
playlist array (not content object)"updated" or "clear" (not "no_change")priority or interruptibleDocument Status: Ready for Implementation