Nightscout Display
A Stream Deck plugin that shows live Nightscout CGM glucose readings on a Stream Deck key. Supports numeric display and a scrolling sparkline graph, with configurable time ranges and color-coded alert levels.


A Stream Deck plugin that surfaces live Nightscout continuous-glucose-monitor data directly on a Stream Deck key, polling the Nightscout API on an interval and rendering either a plain numeric readout or a small scrolling sparkline of recent readings on the key's tiny display. Alert thresholds are color-coded directly on the key — low, in-range, and high glucose render in visually distinct colors — so a glance at the Stream Deck during a stream or at a desk surfaces an out-of-range reading without needing to unlock a phone or check a dashboard. Time range and thresholds are configurable per key through the Stream Deck plugin settings UI.
Architecture
graph TD
StreamDeck["Stream Deck key"] -->|poll every ~30s| Nightscout[("Nightscout API
/api/v2/properties, /api/v1/entries")]
Nightscout -->|bgnow, delta, direction, entries| Action["NightscoutAction"]
Action -->|threshold colors| RenderNumber["renderNumber()
value + arrow + delta"]
Action -->|entries history| RenderGraph["renderGraph()
SVG trend line"]
RenderNumber --> Key["SVG -> setImage on key"]
RenderGraph --> KeyUnder the hood
The key face is an SVG built fresh on every poll, not a bitmap — color, value, trend arrow and a relative-time label are all string-interpolated based on configurable glucose thresholds.
let color = settings.inRangeColor;
if (last >= settings.urgentHigh || last <= settings.urgentLow) {
color = settings.urgentColor;
} else if (last >= settings.normalHigh || last <= settings.normalLow) {
color = settings.normalColor;
}
const directionText = data.direction?.label || "Flat";
const arrow = DIRECTION_ARROWS[directionText] || directionText;
return `<svg width="${this.WIDTH}" height="${this.HEIGHT}" xmlns="http://www.w3.org/2000/svg">
<rect width="${this.WIDTH}" height="${this.HEIGHT}" fill="#000000"/>
<text x="${this.WIDTH / 2}" y="${this.HEIGHT / 2 - 18}" font-size="52" font-weight="bold" fill="${color}" text-anchor="middle">${last}</text>
<text x="${this.WIDTH / 2}" y="${this.HEIGHT / 2 + 28}" font-size="32" font-weight="bold" fill="${color}" text-anchor="middle">${arrow}</text>
</svg>`;