diff --git a/screens/clock.py b/screens/clock.py index 8972c60..153632e 100644 --- a/screens/clock.py +++ b/screens/clock.py @@ -5,16 +5,10 @@ import time from PIL import Image, ImageDraw from screens.base import Screen -from screens.render_utils import load_font, text_width +from screens.render_utils import format_date_de, load_font, text_width LINE_GAP = 0.04 # fraction of min(width, height) between the time and date lines -WEEKDAYS_DE = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"] -MONTHS_DE = [ - "Januar", "Februar", "März", "April", "Mai", "Juni", - "Juli", "August", "September", "Oktober", "November", "Dezember", -] - class ClockScreen(Screen): id = "clock" @@ -31,7 +25,7 @@ class ClockScreen(Screen): now = time.localtime() time_str = state or time.strftime("%H:%M", now) - date_str = f"{WEEKDAYS_DE[now.tm_wday]}, {now.tm_mday}. {MONTHS_DE[now.tm_mon - 1]}" + date_str = format_date_de(now) scale = min(width, height) time_font = load_font(size=int(scale * 0.32), bold=True) diff --git a/screens/render_utils.py b/screens/render_utils.py index d292fdc..44e2000 100644 --- a/screens/render_utils.py +++ b/screens/render_utils.py @@ -6,6 +6,7 @@ dithering by default) -- so screens can draw with normal anti-aliased RGB text and full-color cover art without worrying about the palette themselves. """ +import time from pathlib import Path from PIL import Image, ImageFont @@ -14,6 +15,17 @@ FONTS_DIR = Path(__file__).resolve().parent.parent / "fonts" _font_cache: dict[tuple[str, int], ImageFont.FreeTypeFont] = {} +WEEKDAYS_DE = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"] +MONTHS_DE = [ + "Januar", "Februar", "März", "April", "Mai", "Juni", + "Juli", "August", "September", "Oktober", "November", "Dezember", +] + + +def format_date_de(t: time.struct_time | None = None) -> str: + t = t or time.localtime() + return f"{WEEKDAYS_DE[t.tm_wday]}, {t.tm_mday}. {MONTHS_DE[t.tm_mon - 1]}" + def load_font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont: key = ("bold" if bold else "regular", size) diff --git a/screens/weather.py b/screens/weather.py index a8e8de1..20408c7 100644 --- a/screens/weather.py +++ b/screens/weather.py @@ -8,7 +8,7 @@ import requests from PIL import Image, ImageDraw from screens.base import Screen -from screens.render_utils import load_font, text_width, truncate_to_width +from screens.render_utils import format_date_de, load_font, text_width, truncate_to_width from screens.weather_icons import WMO_CODES, draw_weather_icon BLACK = (0, 0, 0) @@ -31,12 +31,13 @@ class WeatherScreen(Screen): def default_settings(self) -> dict: return { - "city": "", "latitude": None, "longitude": None, "forecast_days": 3, "update_minutes": 30, + "city": "", "latitude": None, "longitude": None, "forecast_days": 5, "update_minutes": 30, "show_feels_like": True, "show_high_low": True, "show_rain_chance": True, "show_hourly_graph": True, "show_city": True, + "show_date": True, } def get_poll_interval(self, settings: dict) -> int: @@ -79,9 +80,9 @@ class WeatherScreen(Screen): logger.warning("Weather screen has no latitude/longitude configured") return None - forecast_days = settings.get("forecast_days", 3) + forecast_days = settings.get("forecast_days", 5) if forecast_days not in FORECAST_DAY_CHOICES: - forecast_days = 3 + forecast_days = 5 response = requests.get( "https://api.open-meteo.com/v1/forecast", @@ -214,26 +215,32 @@ class WeatherScreen(Screen): text_block_h = temp_h + text_gap + desc_h + ((text_gap + extra_h) if extra_line else 0) current_h = max(icon_size, text_block_h) - # City name, above the icon/temp block -- just the city itself, not - # the full "City, State, Country" geocode label (too long/cluttered - # for a dashboard header). Nothing to show for manually-entered - # coordinates, since there's no resolved place name for those. - city_font = load_font(size=int(scale * 0.045), bold=True) - city_label = "" - city_h = city_gap = city_top = 0 + # Header line above the icon/temp block: city name (just the city + # itself, not the full "City, State, Country" geocode label -- too + # cluttered for a dashboard) and/or today's weekday + date, same + # format as the Clock screen. Nothing to show for city specifically + # with manually-entered coordinates, since there's no resolved place + # name for those. + header_font = load_font(size=int(scale * 0.045), bold=True) + header_parts = [] if settings.get("show_city", True) and settings.get("city"): - city_label = truncate_to_width(draw, settings["city"].split(",")[0].strip(), city_font, width - 2 * pad) - if city_label: - city_left, city_top, city_right, city_bottom = draw.textbbox((0, 0), city_label, font=city_font) - city_h = city_bottom - city_top - city_gap = height * 0.025 + header_parts.append(settings["city"].split(",")[0].strip()) + if settings.get("show_date", True): + header_parts.append(format_date_de()) + header_label = truncate_to_width(draw, " · ".join(header_parts), header_font, width - 2 * pad) if header_parts else "" - total_top_h = city_h + city_gap + current_h + header_h = header_gap = header_top = 0 + if header_label: + header_left, header_top, header_right, header_bottom = draw.textbbox((0, 0), header_label, font=header_font) + header_h = header_bottom - header_top + header_gap = height * 0.025 + + total_top_h = header_h + header_gap + current_h top_pad = (height - total_top_h) / 2 if lower_sections == 0 else height * 0.05 - if city_label: - draw.text((pad, top_pad - city_top), city_label, font=city_font, fill="black") - block_top = top_pad + city_h + city_gap + if header_label: + draw.text((pad, top_pad - header_top), header_label, font=header_font, fill="black") + block_top = top_pad + header_h + header_gap icon_cx = pad + icon_size / 2 icon_cy = block_top + current_h / 2 diff --git a/web/app.py b/web/app.py index 78916da..a16a0bb 100644 --- a/web/app.py +++ b/web/app.py @@ -66,12 +66,13 @@ def create_app(config, screens: dict, scheduler) -> Flask: city = request.form.get("city", "").strip() lat_raw = request.form.get("latitude", "").strip() lon_raw = request.form.get("longitude", "").strip() - forecast_days = int(request.form.get("forecast_days", 3) or 3) + forecast_days = int(request.form.get("forecast_days", 5) or 5) if forecast_days not in (3, 5, 7): - forecast_days = 3 + forecast_days = 5 update_minutes = max(1, int(request.form.get("update_minutes", 30) or 30)) display_toggles = { "show_city": "show_city" in request.form, + "show_date": "show_date" in request.form, "show_hourly_graph": "show_hourly_graph" in request.form, "show_feels_like": "show_feels_like" in request.form, "show_high_low": "show_high_low" in request.form, diff --git a/web/templates/settings_weather.html b/web/templates/settings_weather.html index 85a9f53..f74c0a5 100644 --- a/web/templates/settings_weather.html +++ b/web/templates/settings_weather.html @@ -24,8 +24,8 @@
@@ -39,6 +39,10 @@ City name +