Landscape orientation, redesigned web UI, rotation on/off + minutes, weather by city name

- 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.
This commit is contained in:
Honey committed 2026-07-21 22:51:16 +02:00
1 parent 7c197e20a5
commit 61348be674
11 files changed
+393 -104

No files matched your search

+13 -2
View File
@@ -13,7 +13,11 @@ from display import Display
logger = logging.getLogger(__name__)
RESOLUTION = (400, 600)
# The panel is physically native portrait (400x600), but mounted sideways --
# screens render in this landscape resolution, and show() rotates the frame
# to match the panel's native orientation right before sending it over.
RESOLUTION = (600, 400)
PANEL_NATIVE_RESOLUTION = (400, 600)
class EPD4in0eDisplay(Display):
@@ -46,7 +50,14 @@ class EPD4in0eDisplay(Display):
# full flash, there's no way around it on this panel.
self._epd.Clear()
self._epd.display(self._epd.getbuffer(image.convert("RGB")))
# Rotate landscape -> the panel's native portrait orientation. If this
# comes out upside-down for how it's physically mounted, flip the
# sign here (rotate(-90) instead of rotate(90)).
rotated = image.convert("RGB").rotate(90, expand=True)
if rotated.size != PANEL_NATIVE_RESOLUTION:
raise ValueError(f"Rotated image {rotated.size} doesn't match panel {PANEL_NATIVE_RESOLUTION}")
self._epd.display(self._epd.getbuffer(rotated))
def sleep(self) -> None:
if self._epd is not None: