Two separate imbalances:
- Cover art was top-aligned when width-constrained (landscape, cover_size
capped by width*0.5), leaving dead space below it instead of centering
in the available height.
- The text column always started drawing at the very top and let the
source line's bottom-pin create a big empty gap for tracks with
short/sparse metadata -- looked top-heavy and unbalanced.
Text column is now built as (height, draw_fn) pairs first so the total
height is known before committing to a starting y, then centered in the
space above the reserved source-line area.
- scheduler: "Show X now" was a true one-shot -- reverted on the very next
5s tick, causing the flash-to-X-then-flash-back-to-clock behavior. Now
pins for 5 minutes (still preemptable by Navidrome priority, which stays
authoritative). Added a "Resume automatic" action/button and status
display showing what's pinned and until when.
- weather: daily forecast strip (3/5/7 days, selectable in settings) below
current conditions, via Open-Meteo's daily= params in the same request.
Fixed a bad symbol choice along the way -- DejaVu Sans has no glyph for
the partly-cloudy emoji, was rendering as a tofu box; swapped to the
plain cloud symbol, verified glyph coverage for the whole WMO code table.
New screen alongside clock/weather/navidrome. Upload goes through the web
UI (validated with PIL before accepting, stored under web/static/uploads/,
gitignored), or point it at a URL re-fetched every 30 minutes. Cover/contain
fit modes (new fit_cover/fit_contain helpers in render_utils.py, generalized
from the square-only cover-art cropping already used by the Navidrome
screen), plus the same contrast-boost/grayscale options that screen has.
- Panel is mounted sideways: render landscape (600x400), rotate to the
panel's native portrait orientation right before writing to hardware.
- Web UI: card-based layout, dark mode, active nav highlighting, save
confirmation toasts, error banners.
- Rotation: separate enabled/disabled toggle (off pins to the first screen
in the list) and the interval is now in minutes instead of raw seconds.
Migrates existing config.json from the old interval_seconds field.
- Weather: type a city name instead of hunting down coordinates, resolved
via Open-Meteo's free geocoding API. Manual lat/long still available as
a fallback.
Was importing 'waveshare_epd' as a top-level package, but it's nested
under display/waveshare_epd/ -- caused ModuleNotFoundError on the Pi
since only display/ is on sys.path via main.py's imports, not
display/waveshare_epd/ itself. Verified the corrected import resolves
by exercising it directly; the only error left is the driver's own
platform check falling through to its Jetson Nano path on this non-Pi
dev machine, which is expected here and won't happen on the real
Raspberry Pi (it checks /proc/cpuinfo for "Raspberry").