diff --git a/services/telegram-bot/smart_irrigation_advisor.py b/services/telegram-bot/smart_irrigation_advisor.py index d5baa11..46d64e3 100755 --- a/services/telegram-bot/smart_irrigation_advisor.py +++ b/services/telegram-bot/smart_irrigation_advisor.py @@ -1828,12 +1828,11 @@ def analyze_irrigation( if rain_veto: veto_lines.append(f"🌧️ **VETO PIOGGIA**: Ultime 24h ≥ {PRECIP_VETO_MM_24H:.0f} mm — non avviare irrigazione.") - # Colpo d'occhio (box informazioni chiave) + # Colpo d'occhio glance = [ status.strip(), f"📍 {location_name} · {now.strftime('%d/%m/%Y %H:%M')}", "", - "**Umidità (layer irrigazione)**", moisture_summary_line.strip(), "", ] @@ -1848,123 +1847,72 @@ def analyze_irrigation( # Costruisci report completo (strutturato) report_parts = [ "\n".join(glance), - "─"*30, - "", - "**📋 CONSIGLIO**", - "", + "─"*24, + "**Consiglio**", advice, "", ] if timing_advice: - report_parts.append("**⏰ Orario / Radiazione**\n") - report_parts.append("\n".join("• " + t for t in timing_advice)) + report_parts.append("**Orario** " + " · ".join(timing_advice)) report_parts.append("") if planning_8d_line: - report_parts.append(planning_8d_line) + report_parts.append(planning_8d_line.strip()) + report_parts.append("") + report_parts.append("─"*24) - # Dettagli tecnici (organizzati) + # Dettagli tecnici (compatti, at a glance) details = [] - - # Temperatura suolo: 0, 6, 18, 54 cm (ICON Seamless / EU) soil_temp_0cm = _at(current_idx, soil_temp_0cm_list) soil_temp_54cm = _at(current_idx, soil_temp_54cm_list) - temp_found = False - for label, val in [ - ("0cm", soil_temp_0cm), - ("6cm", soil_temp_6cm), - ("18cm", soil_temp_18cm), - ("54cm", soil_temp_54cm), - ]: + temp_parts = [] + for label, val in [("0cm", soil_temp_0cm), ("6cm", soil_temp_6cm), ("18cm", soil_temp_18cm), ("54cm", soil_temp_54cm)]: if val is not None: - temp_class = classify_soil_temp(val) - trend_str = f" | trend 7gg: {temp_trend}" if (temp_trend and label == "6cm") else "" - details.append(f"🌡️ T° suolo ({label}): {val:.1f}°C ({temp_class}){trend_str}") - temp_found = True - if not temp_found: - for i in range(current_idx, min(current_idx + 48, len(soil_temp_0cm_list or []))): - if i < len(soil_temp_0cm_list) and soil_temp_0cm_list[i] is not None: - details.append(f"🌡️ T° suolo (0cm): {float(soil_temp_0cm_list[i]):.1f}°C (prossime ore)") - temp_found = True - break - - # Umidità suolo: 0-1, 3-9, 9-27, 27-81 cm (ICON Seamless / EU). Mostriamo sempre tutti i layer. - moisture_found = False - for label, val in [ - ("0-1cm", soil_moisture_0_1cm), - ("3-9cm", soil_moisture_3_9cm), - ("9-27cm", soil_moisture_9_27cm), - ("27-81cm", soil_moisture_27_81cm), - ]: + temp_parts.append(f"{label} {val:.1f}°C") + if temp_parts: + trend_str = f" · trend 7gg: {temp_trend}" if temp_trend else "" + details.append("🌡️ T° suolo: " + " · ".join(temp_parts) + trend_str) + elif soil_temp_0cm_list and current_idx < len(soil_temp_0cm_list) and soil_temp_0cm_list[current_idx] is not None: + details.append(f"🌡️ T° suolo 0cm: {float(soil_temp_0cm_list[current_idx]):.1f}°C") + + moist_parts = [] + any_at_fc = False + for label, val in [("0-1", soil_moisture_0_1cm), ("3-9", soil_moisture_3_9cm), ("9-27", soil_moisture_9_27cm), ("27-81", soil_moisture_27_81cm)]: if val is not None: - moisture_class = classify_soil_moisture(val) - line = f"💧 Umidità ({label}): {val*100:.0f}% ({moisture_class})" + moist_parts.append(f"{label} {val*100:.0f}%") if val >= SOIL_MOISTURE_FIELD_CAPACITY: - line += " — terreno pieno, possibile fanghiglia/ristagno" - details.append(line) - moisture_found = True + any_at_fc = True else: - details.append(f"💧 Umidità ({label}): — (non disponibile)") - if not moisture_found: - for i in range(current_idx, min(current_idx + 48, len(soil_moisture_0_1_list or []))): - if i < len(soil_moisture_0_1_list) and soil_moisture_0_1_list[i] is not None: - v = float(soil_moisture_0_1_list[i]) - details.append(f"💧 Umidità (0-1cm): {v*100:.0f}% (prossime ore)") - moisture_found = True - break - - if not temp_found and not moisture_found: - details.append("ℹ️ Dati suolo non disponibili per questa località") - - # ET₀ e parametri evapotraspirazione + moist_parts.append(f"{label} —") + if moist_parts: + line = "💧 Umidità: " + " · ".join(moist_parts) + if any_at_fc: + line += " — terreno pieno" + details.append(line) + if not details: + details.append("ℹ️ Dati suolo non disponibili") + + # Una riga: ET₀, VPD, sole, umidità aria + meteo_parts = [] if et0_avg is not None: - et0_class = classify_et0(et0_avg) - details.append(f"☀️ ET₀ medio (24h): {et0_avg:.1f} mm/d ({et0_class})") - - # Vapour Pressure Deficit (stress idrico) + meteo_parts.append(f"ET₀ {et0_avg:.1f} mm/d") if vpd_avg is not None: - vpd_class = classify_vpd(vpd_avg) - # VPD alto = stress idrico alto - vpd_status = "" - if vpd_avg > 1.5: - vpd_status = " (stress idrico elevato)" - elif vpd_avg > 1.0: - vpd_status = " (stress moderato)" - details.append(f"💨 VPD medio (24h): {vpd_avg:.2f} kPa ({vpd_class}){vpd_status}") - - # Ore di sole previste + meteo_parts.append(f"VPD {vpd_avg:.2f} kPa") if sunshine_hours is not None: - sunshine_class = classify_sunshine(sunshine_hours) - details.append(f"☀️ Ore sole previste (24h): {sunshine_hours:.1f}h ({sunshine_class})") - - # Umidità relativa aria + meteo_parts.append(f"Sole {sunshine_hours:.1f}h") if humidity_avg is not None: - # Classifica umidità relativa (bassa < 40%, media 40-70%, alta > 70%) - if humidity_avg < 40: - humidity_class = "basso (secco)" - elif humidity_avg < 70: - humidity_class = "medio" - else: - humidity_class = "alto (umido)" - details.append(f"💨 Umidità relativa aria (24h): {humidity_avg:.0f}% ({humidity_class})") - - # Precipitazioni previste (include neve) + meteo_parts.append(f"UR {humidity_avg:.0f}%") + if meteo_parts: + details.append("☀️ " + " · ".join(meteo_parts)) + + # Precipitazioni: una riga if future_rain_total > 0: - # Classifica come totale su 5 giorni (media giornaliera approssimativa) - avg_daily = future_rain_total / 5.0 - precip_class = classify_precip_daily(avg_daily) - precip_str = f"🌧️ Precipitazioni previste (5gg): {future_rain_total:.1f}mm ({precip_class}, media ~{avg_daily:.1f}mm/giorno)" - if rainy_days: - precip_str += f"\n Giorni: {', '.join(rainy_days[:3])}" # Primi 3 giorni - if len(rainy_days) > 3: - precip_str += f" +{len(rainy_days)-3} altri" - details.append(precip_str) - elif len(rainy_days) == 0: - details.append("🌧️ Precipitazioni previste (5gg): 0mm (basso)") + days_short = ", ".join(rainy_days[:3]) if rainy_days else "" + details.append(f"🌧️ Precip 5gg: {future_rain_total:.1f} mm — {days_short}") + else: + details.append("🌧️ Precip 5gg: 0 mm") if details: - report_parts.append("─"*30) - report_parts.append("**📊 Dettagli tecnici**") - report_parts.append("") + report_parts.append("**Dettagli**") report_parts.append("\n".join(details)) # Salva stato