Files
Paperwall/web/app.py
T
Honey 61348be674 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.
2026-07-21 22:51:16 +02:00

99 lines
4.2 KiB
Python

"""Flask app: status/preview page + one settings form per screen + rotation settings."""
import requests
from flask import Flask, redirect, render_template, request, url_for
def create_app(config, screens: dict, scheduler) -> Flask:
app = Flask(__name__)
@app.route("/")
def index():
status = scheduler.get_status()
return render_template(
"index.html",
status=status,
screens=screens,
rotation=config.get_rotation(),
navidrome_priority=config.get_navidrome_priority(),
)
@app.route("/actions/refresh", methods=["POST"])
def refresh_now():
scheduler.request_manual_refresh()
return redirect(url_for("index"))
@app.route("/actions/switch/<screen_id>", methods=["POST"])
def switch_now(screen_id):
if screen_id in screens:
scheduler.request_switch(screen_id)
return redirect(url_for("index"))
@app.route("/settings/navidrome", methods=["GET", "POST"])
def settings_navidrome():
if request.method == "POST":
config.update_screen_settings("navidrome", {
"url": request.form.get("url", "").strip(),
"username": request.form.get("username", "").strip(),
"password": request.form.get("password", ""),
"filter_user": request.form.get("filter_user", "").strip(),
"show_progress": "show_progress" in request.form,
"show_genre": "show_genre" in request.form,
"show_source": "show_source" in request.form,
"boost_cover": "boost_cover" in request.form,
"grayscale_cover": "grayscale_cover" in request.form,
})
return redirect(url_for("settings_navidrome", saved=1))
return render_template("settings_navidrome.html", settings=config.get_screen_settings("navidrome"))
@app.route("/settings/weather", methods=["GET", "POST"])
def settings_weather():
error = None
if request.method == "POST":
city = request.form.get("city", "").strip()
lat_raw = request.form.get("latitude", "").strip()
lon_raw = request.form.get("longitude", "").strip()
if city:
try:
result = screens["weather"].geocode(city)
except requests.exceptions.RequestException:
result = None
if result is None:
error = f'Could not find "{city}" -- try a different spelling, or fill in coordinates manually below instead.'
else:
config.update_screen_settings("weather", {
"city": result["label"],
"latitude": result["latitude"],
"longitude": result["longitude"],
})
return redirect(url_for("settings_weather", saved=1))
else:
config.update_screen_settings("weather", {
"city": "",
"latitude": float(lat_raw) if lat_raw else None,
"longitude": float(lon_raw) if lon_raw else None,
})
return redirect(url_for("settings_weather", saved=1))
return render_template("settings_weather.html", settings=config.get_screen_settings("weather"), error=error)
@app.route("/settings/rotation", methods=["GET", "POST"])
def settings_rotation():
if request.method == "POST":
raw = request.form.get("screens", "")
selected = [s.strip() for s in raw.split(",") if s.strip() in screens]
interval_minutes = int(request.form.get("interval_minutes", 5) or 5)
enabled = "rotation_enabled" in request.form
config.set_rotation(selected, interval_minutes, enabled)
config.set_navidrome_priority("navidrome_priority" in request.form)
return redirect(url_for("settings_rotation", saved=1))
return render_template(
"settings_rotation.html",
screens=screens,
rotation=config.get_rotation(),
navidrome_priority=config.get_navidrome_priority(),
)
return app