Framebuffer dashboard stack for a 320x240 SPI TFT on Raspberry Pi.
- Direct rendering to
/dev/fb1 - No X11, Wayland, SDL, or browser runtime
PLAN.mdis the active source of truth for the current shell slice
| Unit | Purpose | Entrypoint |
|---|---|---|
boot-selector.service |
Primary shell runtime, themed menu/router, child-app lifecycle owner | boot/boot_selector.py |
display.service |
Manual compatibility wrapper for the Dashboards child | display_rotator.py |
night.service |
Manual/night compatibility wrapper | modules/blackout/blackout.py |
shell-mode-switch@.service |
Shell mode request bridge for timers and operators | boot/boot_selector.py --request-mode <mode> |
currency-update.service |
Independent refresh job for the GBP/PLN image | modules/currency/currency-rate.py |
weather.service |
Independent refresh job for the weather image | modules/weather/weather_refresh.py |
tram.service |
Independent refresh job for the cached Firswood tram timetable | modules/trams/tram_gtfs_refresh.py |
tram-alerts.service |
Independent refresh job for the cached Bee Network tram alerts | modules/trams/tram_alerts_refresh.py |
boot/boot_selector.pyis now the themed shell runtimedisplay_rotator.pyremains the Dashboards child entrypointmodules/photos/slideshow.pyremains the Photos child entrypoint- Verified installed theme ids are
carbon,comic,frosty, andsteele - Theme selection is persisted only; root-page return state is session-only
rotator/touch.pyrestores long-pressMAIN_MENUreturn behavior- The shell baseline is working on the Pi, including corrected Themes routing, a right-side back strip, and code-tunable Settings text layout
- The current slice is complete and accepted on hardware
- NASA app work is deferred to a later slice
Theme assets live under themes/, one directory per theme.
Current theme IDs:
carboncomicfrostysteele
Each theme directory must provide:
mainmenu1.pngmainmenu2.pngday-night.pngthemes.pngsettings.pngstats.pngyes-no.pngkeypad.pnggranted.gifdenied.gif
keypad.png uses a 4x3 layout:
1 2 3 tick4 5 6 07 8 9 red X
Shared non-theme assets remain under boot/:
startup.gifcredits.gif
menudashboardsphotosnight
| Scope | Command | Notes |
|---|---|---|
| Shell contracts | python3 boot/boot_selector.py --dump-contracts --no-framebuffer --skip-gif |
Confirms the shell registry and mode surface. |
| Shell smoke | python3 boot/boot_selector.py --no-framebuffer --skip-gif |
Verifies the shell boots without hardware writes. |
| Touch probe | python3 boot/boot_selector.py --probe-touch |
Prints the chosen touch device and probe reason. |
| Touch calibration | python3 boot/boot_selector.py --calibrate-touch |
Captures four corner taps and prints suggested touch env values. |
| Rotator slice | python3 -m unittest tests.test_display_rotator |
Covers the extracted touch and screen-power paths. |
| Framebuffer slice | python3 -m unittest tests.test_framebuffer |
Covers RGB565 conversion and framebuffer writes. |
| Tram render | python3 modules/trams/display.py --self-test |
Checks a representative image-producing module. |
Current limitation:
tests/test_boot_selector.pyis not currently checked in, so shell acceptance for this slice is based on device validation plus targeted runtime checks.
zero2dash/
├── PLAN.md
├── AGENTS.md
├── README.md
├── boot/
│ └── boot_selector.py
├── coordination/
├── docs/
│ └── wiki/
├── display_rotator.py
├── framebuffer.py
├── modules/
├── rotator/
├── systemd/
├── tests/
└── themes/
modules/photos/slideshow.pyis the long-running Photos app.display_rotator.pyis the supported Dashboards entrypoint.- The shell’s menu contract is theme-backed, not the old paged tile UI.
- The shell strip/back action now lives on the rightmost
20pxfor stripe-based screens. pin_keypadfollows the real keypad asset: green tick submits, red X cancels, and only uninterrupted failed keypad submissions count toward shutdown.- Settings/status text layout is code-tunable near the top of
boot/boot_selector.pyvia explicit title/body position, size, font, and spacing constants. - Touch calibration is env-driven. Use
TOUCH_SWAP_AXES,TOUCH_RAW_X_MIN,TOUCH_RAW_X_MAX,TOUCH_RAW_Y_MIN, andTOUCH_RAW_Y_MAXafter capturing values with--calibrate-touch.