Backup automatico script del 2026-07-12 07:00
This commit is contained in:
+107
-12
@@ -6,20 +6,29 @@ import sys
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from typing import Optional, List
|
||||
import json
|
||||
from typing import Optional, List, Dict, Any, Union
|
||||
from zoneinfo import ZoneInfo
|
||||
from dateutil import parser as date_parser
|
||||
from open_meteo_client import open_meteo_get
|
||||
from open_meteo_precip import (
|
||||
CASA_LAT,
|
||||
CASA_LON,
|
||||
CASA_TZ,
|
||||
daily_precip_from_hourly,
|
||||
hourly_precip_mm,
|
||||
is_casa,
|
||||
)
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# --- CONFIGURAZIONE METEO ---
|
||||
HOME_LAT = 43.9356
|
||||
HOME_LON = 12.4296
|
||||
HOME_LAT = CASA_LAT
|
||||
HOME_LON = CASA_LON
|
||||
HOME_NAME = "🏠 Casa"
|
||||
TZ = "Europe/Berlin"
|
||||
TZ = CASA_TZ
|
||||
TZINFO = ZoneInfo(TZ)
|
||||
|
||||
OPEN_METEO_URL = "https://api.open-meteo.com/v1/forecast"
|
||||
@@ -300,10 +309,10 @@ def get_visibility_forecast(lat, lon):
|
||||
logger.error("Visibility request error: %s elapsed=%.2fs", e, time.time() - t0)
|
||||
return None
|
||||
|
||||
def generate_weather_report(lat, lon, location_name, debug_mode=False, cc="IT", timezone=None) -> str:
|
||||
def generate_weather_report(lat, lon, location_name, debug_mode=False, cc="IT", timezone=None, as_json=False) -> Union[str, Dict[str, Any]]:
|
||||
t_total = time.time()
|
||||
# Determina se è Casa
|
||||
is_home = (abs(lat - HOME_LAT) < 0.01 and abs(lon - HOME_LON) < 0.01)
|
||||
is_home = is_casa(lat, lon)
|
||||
|
||||
# Fuso per l'API: Casa = TZ; località = timezone esplicito/geocoding, altrimenti "auto" (Open-Meteo risolve da lat/lon)
|
||||
tz_for_api = timezone if timezone else (TZ if is_home else "auto")
|
||||
@@ -446,6 +455,8 @@ def generate_weather_report(lat, lon, location_name, debug_mode=False, cc="IT",
|
||||
|
||||
# Separa in blocchi per giorno: cambia intestazione quando passa da 23 a 00
|
||||
blocks = []
|
||||
json_blocks: List[Dict[str, Any]] = []
|
||||
json_block_map: Dict[str, Dict[str, Any]] = {}
|
||||
header = f"{'LT':<2} {'T°':>4} {'h%':>3} {'mm':<3} {'Vento':<5} {'Nv%':>5} {'Sk':<2} {'Sx':<2}"
|
||||
separator = "-" * 31
|
||||
|
||||
@@ -506,8 +517,7 @@ def generate_weather_report(lat, lon, location_name, debug_mode=False, cc="IT",
|
||||
Code = int(get_val(l_code[idx], 0))
|
||||
Rain = get_val(l_rain[idx], 0)
|
||||
Showers = get_val(l_showers[idx], 0) if idx < len(l_showers) else 0
|
||||
# Per modelli che espongono rain+showers (es. ICON Italia), usa il totale se precipitation è assente/zero
|
||||
Pr_display = max(Pr, Rain + Showers)
|
||||
Pr_display = hourly_precip_mm(Pr, Rain, Showers)
|
||||
|
||||
# Determina se è neve
|
||||
is_snowing = Sn > 0 or (Code in [71, 73, 75, 77, 85, 86])
|
||||
@@ -582,6 +592,39 @@ def generate_weather_report(lat, lon, location_name, debug_mode=False, cc="IT",
|
||||
sky_fmt = f"{sky}{uv_suffix}"
|
||||
|
||||
current_block_lines.append(f"{dt.strftime('%H'):<2} {t_s:>4} {Rh:>3} {p_s:>3} {w_fmt} {cl_str:>5} {sky_fmt:<2} {sgx:<2}")
|
||||
|
||||
day_key = day_date.isoformat()
|
||||
if day_key not in json_block_map:
|
||||
day_label = f"{['Lun','Mar','Mer','Gio','Ven','Sab','Dom'][day_date.weekday()]} {day_date.day}"
|
||||
json_block_map[day_key] = {"day_label": day_label, "date": day_key, "rows": []}
|
||||
json_blocks.append(json_block_map[day_key])
|
||||
json_block_map[day_key]["rows"].append({
|
||||
"hour": dt.strftime("%H"),
|
||||
"datetime": dt.isoformat(),
|
||||
"temp_c": round(T, 1),
|
||||
"temp_display": t_s,
|
||||
"feels_offset": t_suffix,
|
||||
"humidity": Rh,
|
||||
"precip_mm": round(Pr_display, 1),
|
||||
"precip_display": p_s,
|
||||
"snow_cm": round(Sn, 1),
|
||||
"weathercode": Code,
|
||||
"wind_speed_kmh": round(Wspd, 0),
|
||||
"wind_gust_kmh": round(Gust, 0),
|
||||
"wind_display": w_txt.strip(),
|
||||
"wind_cardinal": card,
|
||||
"cloud_pct": cl_str,
|
||||
"cloud_type": dominant_type,
|
||||
"visibility_m": round(Vis, 0),
|
||||
"uv_index": round(UV, 1),
|
||||
"uv_suffix": uv_suffix,
|
||||
"cape": round(Cape, 0),
|
||||
"is_day": bool(IsDay),
|
||||
"sky_icon": sky_fmt,
|
||||
"side_icon": sgx,
|
||||
"is_fog": is_fog,
|
||||
"is_snow": is_snowing,
|
||||
})
|
||||
|
||||
hours_from_start += 1
|
||||
|
||||
@@ -593,7 +636,49 @@ def generate_weather_report(lat, lon, location_name, debug_mode=False, cc="IT",
|
||||
if not blocks:
|
||||
return f"❌ Nessun dato da mostrare nelle prossime 48 ore (da {current_hour.strftime('%H:%M')})."
|
||||
|
||||
report = f"🌤️ *METEO REPORT*\n📍 {location_name}\n🧠 Fonte: {model_name}\n\n" + "\n\n".join(blocks)
|
||||
daily_totals = daily_precip_from_hourly(hourly_c)
|
||||
today_str = datetime.datetime.now(tz_to_use_info).date().isoformat()
|
||||
tomorrow_str = (datetime.datetime.now(tz_to_use_info).date() + datetime.timedelta(days=1)).isoformat()
|
||||
today_mm = daily_totals.get(today_str, 0.0)
|
||||
tomorrow_mm = daily_totals.get(tomorrow_str, 0.0)
|
||||
totals_line = (
|
||||
f"\n\n💧 *Precip cumulata:* oggi {today_mm:.1f} mm | domani {tomorrow_mm:.1f} mm"
|
||||
)
|
||||
|
||||
legend = {
|
||||
"temp": "W=wind chill, H=heat index",
|
||||
"precip": "G=grandine, Z=ghiacciato, N=neve",
|
||||
"cloud": "FOG=nebbia",
|
||||
"sky": "Icona condizioni (☀️🌧️⛈️…)",
|
||||
"sx": "☃️ neve · 🧊 ghiaccio · ⚡/🌪️ temporali · 🥵 caldo · ☔️ pioggia · 💨 vento forte",
|
||||
"uv": "E=UV estremo, H=UV alto",
|
||||
}
|
||||
|
||||
if as_json:
|
||||
return {
|
||||
"location": location_name,
|
||||
"model": model_name,
|
||||
"timezone": tz_to_use,
|
||||
"blocks": json_blocks,
|
||||
"precip_totals": {"today_mm": round(today_mm, 1), "tomorrow_mm": round(tomorrow_mm, 1)},
|
||||
"legend": legend,
|
||||
"columns": [
|
||||
{"key": "hour", "label": "LT", "title": "Ora locale"},
|
||||
{"key": "temp_display", "label": "T°", "title": "Temperatura"},
|
||||
{"key": "humidity", "label": "h%", "title": "Umidità"},
|
||||
{"key": "precip_display", "label": "mm", "title": "Precipitazioni orarie"},
|
||||
{"key": "wind_display", "label": "Vento", "title": "Direzione e intensità (km/h)"},
|
||||
{"key": "cloud_pct", "label": "Nv%", "title": "Copertura nuvolosa"},
|
||||
{"key": "sky_icon", "label": "Sk", "title": "Cielo"},
|
||||
{"key": "side_icon", "label": "Sx", "title": "Indicatori secondari"},
|
||||
],
|
||||
}
|
||||
|
||||
report = (
|
||||
f"🌤️ *METEO REPORT*\n📍 {location_name}\n🧠 Fonte: {model_name}\n\n"
|
||||
+ "\n\n".join(blocks)
|
||||
+ totals_line
|
||||
)
|
||||
logger.info("generate_weather_report ok elapsed=%.2fs", time.time() - t_total)
|
||||
return report
|
||||
|
||||
@@ -604,6 +689,7 @@ if __name__ == "__main__":
|
||||
args_parser.add_argument("--debug", action="store_true", help="Mostra dettaglio debug (nuvole, neve)")
|
||||
args_parser.add_argument("--chat_id", help="Chat ID Telegram per invio diretto (opzionale, può essere multiplo separato da virgola)")
|
||||
args_parser.add_argument("--timezone", help="Timezone IANA (es: Europe/Rome, America/New_York)")
|
||||
args_parser.add_argument("--json", action="store_true", help="Output JSON strutturato (WebApp)")
|
||||
args = args_parser.parse_args()
|
||||
|
||||
# Determina chat_ids se specificato
|
||||
@@ -613,14 +699,21 @@ if __name__ == "__main__":
|
||||
|
||||
# Genera report
|
||||
report = None
|
||||
json_out = None
|
||||
if args.home:
|
||||
report = generate_weather_report(HOME_LAT, HOME_LON, HOME_NAME, args.debug, "SM")
|
||||
if args.json:
|
||||
json_out = generate_weather_report(HOME_LAT, HOME_LON, HOME_NAME, args.debug, "SM", as_json=True)
|
||||
else:
|
||||
report = generate_weather_report(HOME_LAT, HOME_LON, HOME_NAME, args.debug, "SM")
|
||||
elif args.query:
|
||||
coords = get_coordinates(args.query)
|
||||
if coords:
|
||||
lat, lon, name, cc, geo_tz = coords
|
||||
tz = args.timezone or geo_tz
|
||||
report = generate_weather_report(lat, lon, name, args.debug, cc, timezone=tz)
|
||||
if args.json:
|
||||
json_out = generate_weather_report(lat, lon, name, args.debug, cc, timezone=tz, as_json=True)
|
||||
else:
|
||||
report = generate_weather_report(lat, lon, name, args.debug, cc, timezone=tz)
|
||||
else:
|
||||
error_msg = f"❌ Città '{args.query}' non trovata."
|
||||
if chat_ids:
|
||||
@@ -637,7 +730,9 @@ if __name__ == "__main__":
|
||||
sys.exit(1)
|
||||
|
||||
# Invia o stampa
|
||||
if chat_ids:
|
||||
if args.json and json_out:
|
||||
print(json.dumps(json_out, ensure_ascii=False))
|
||||
elif chat_ids:
|
||||
telegram_send_markdown(report, chat_ids)
|
||||
else:
|
||||
print(report)
|
||||
Reference in New Issue
Block a user