feat: hourly weather forecast + steel141 migration notes

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mortdecai
2026-06-07 13:15:02 -04:00
parent c7b114da7a
commit f19c04bdc1
3 changed files with 54 additions and 19 deletions
+42 -13
View File
@@ -10,6 +10,7 @@ import socket
import requests
import yfinance as yf
from datetime import datetime
from zoneinfo import ZoneInfo
# ── Config ────────────────────────────────────────────────────────────────────
CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.json")
@@ -30,6 +31,7 @@ REMOTE_HOST = config["remote_host"]
REMOTE_PASS = config["remote_pass"]
PRINTER_IP = config["printer_ip"]
PRINTER_PORT = int(config.get("printer_port", 9100))
EASTERN_TZ = ZoneInfo("America/New_York")
# TM-m30 80mm paper, Font B — column width measured from test print
COLS = 57
@@ -246,11 +248,19 @@ def get_weather():
r = requests.get(
"https://api.open-meteo.com/v1/forecast"
"?latitude=38.8421&longitude=-77.2683"
"&daily=weathercode,temperature_2m_max,temperature_2m_min,precipitation_probability_max"
"&temperature_unit=fahrenheit&timezone=auto&forecast_days=4",
"&hourly=weathercode,temperature_2m,precipitation_probability"
"&temperature_unit=fahrenheit&timezone=America%2FNew_York&forecast_days=2",
timeout=15
).json()
daily = r.get("daily", {})
hourly = r.get("hourly", {})
times = hourly.get("time", [])
temps = hourly.get("temperature_2m", [])
pops = hourly.get("precipitation_probability", [])
codes = hourly.get("weathercode", [])
if not times:
return None
now = datetime.now(EASTERN_TZ).replace(tzinfo=None)
wmo = {
0: "Sun", 1: "Sun", 2: "PCld", 3: "Ovcst",
45: "Fog", 48: "Fog",
@@ -261,15 +271,29 @@ def get_weather():
85: "SnShwr", 86: "HvSnShwr",
95: "Strm", 96: "Strm", 99: "Strm",
}
slots = []
for i, t in enumerate(times):
dt = datetime.strptime(t, "%Y-%m-%dT%H:%M")
if dt >= now:
slots.append((dt, i))
if len(slots) >= 24:
break
if not slots:
return None
parts = []
for i in range(len(daily.get("time", []))):
d = datetime.strptime(daily["time"][i], "%Y-%m-%d").strftime("%a")
cond = wmo.get(daily["weathercode"][i], "Unk")
hi = daily["temperature_2m_max"][i]
lo = daily["temperature_2m_min"][i]
pop = daily["precipitation_probability_max"][i]
parts.append(f"{d}:{cond} {hi:.0f}/{lo:.0f}({pop}%)")
return "|".join(parts)
for idx in range(0, len(slots), 3):
dt, i = slots[idx]
cond = wmo.get(codes[i], "Unk")
temp = temps[i]
pop = pops[i]
parts.append(f"{dt.strftime('%H:%M')}:{cond} {temp:.0f}F({pop:.0f}%)")
lines = []
for i in range(0, len(parts), 4):
lines.append(" | ".join(parts[i:i + 4]))
return lines
except Exception as e:
print(f"[!] Weather error: {e}")
return None
@@ -349,7 +373,7 @@ def build_receipt(articles, zfs, reddit, grafana, weather, finance):
s(align='center', bold=True)
p.text("Daily Briefing\n")
s(align='center', bold=False)
p.text(datetime.now().strftime('%A, %B %d %I:%M %p') + "\n")
p.text(datetime.now(EASTERN_TZ).strftime('%A, %B %d %H:%M ET') + "\n")
p.text("=" * COLS + "\n")
# Dashboard
@@ -357,7 +381,12 @@ def build_receipt(articles, zfs, reddit, grafana, weather, finance):
p.text("SYSTEM & WEATHER\n")
s(align='left', bold=False)
if weather:
p.text(f"Wx: {weather}\n")
if isinstance(weather, list):
p.text("Wx24:\n")
for line in weather:
p.text(f" {line}\n")
else:
p.text(f"Wx: {weather}\n")
if zfs:
p.text(f"ZFS: {zfs['alloc']}/{zfs['size']} ({zfs['cap']}) {zfs['health']}\n")
if grafana: