diff --git a/csharp/App/Backend/Services/ReportAggregationService.cs b/csharp/App/Backend/Services/ReportAggregationService.cs index e29265c5c..824b9848b 100644 --- a/csharp/App/Backend/Services/ReportAggregationService.cs +++ b/csharp/App/Backend/Services/ReportAggregationService.cs @@ -739,22 +739,23 @@ Exactly 4 bullet points. Each starts with ""- Title: description"" format."; var prompt = $@"You are an energy advisor for a sodistore home installation: ""{installationName}"". -Write a concise monthly performance summary in {langName} (4 bullet points, plain text, no markdown). +Write a concise monthly performance summary in {langName} (5 bullet points, plain text, no markdown). MONTHLY FACTS for {monthName} ({weekCount} days of data): - PV production: {totalPv:F1} kWh - Total consumption: {totalConsump:F1} kWh -- Self-sufficiency: {selfSufficiency:F1}% (share of energy covered by solar, without drawing from grid) +- Self-sufficiency: {selfSufficiency:F1}% (share of energy covered by solar + battery, not bought from grid) - Battery: {totalBattChg:F1} kWh charged, {totalBattDis:F1} kWh discharged, efficiency {batteryEff:F1}% - Energy saved: {energySaved:F1} kWh = ~{savingsCHF:F0} CHF saved (at {ElectricityPriceCHF} CHF/kWh) {weatherBlock} INSTRUCTIONS: 1. Savings: state exactly how much energy and money was saved this month. Positive framing. -2. Solar performance: how much the solar system produced and what self-sufficiency % means for the homeowner (e.g. ""Your solar system covered X% of your home's energy needs""). Do NOT mention battery here. Do NOT mention raw grid import kWh. -3. Battery: comment on battery utilization and efficiency. If efficiency < 80%, note it may need attention. -4. Tip: one specific actionable suggestion based on the weakest metric: {weakMetric}.{weatherTipHint} If general, suggest the most impactful habit change based on the numbers above. +2. Energy independence: state the self-sufficiency percentage and what it means — X% of the home's energy came from the combined solar and battery system, only Y% was purchased from the grid. Do NOT repeat raw grid import kWh. +3. Solar production: state how much the solar system produced this month and the daily average. Keep it factual. Do NOT repeat self-sufficiency percentage here. +4. Battery: comment on battery utilization and efficiency. If efficiency < 80%, note it may need attention. Do NOT repeat self-sufficiency percentage here. +5. Tip: one specific actionable suggestion based on the weakest metric: {weakMetric}.{weatherTipHint} If general, suggest the most impactful habit change based on the numbers above. -Rules: Write in {langName}. Write for a homeowner, not a technician. No asterisks or formatting marks. No closing remarks. Exactly 4 bullet points starting with ""- "". Each bullet must have a short title followed by colon then description."; +Rules: Write in {langName}. Write for a homeowner, not a technician. No asterisks or formatting marks. No closing remarks. Exactly 5 bullet points starting with ""- "". Each bullet must have a short title followed by colon then description."; return await CallMistralAsync(apiKey, prompt); } @@ -774,7 +775,7 @@ Rules: Write in {langName}. Write for a homeowner, not a technician. No asterisk var langName = GetLanguageName(language); var prompt = $@"You are an energy advisor for a sodistore home installation: ""{installationName}"". -Write a concise annual performance summary in {langName} (4 bullet points, plain text, no markdown). +Write a concise annual performance summary in {langName} (5 bullet points, plain text, no markdown). ANNUAL FACTS for {year} ({monthCount} months of data): - Total PV production: {totalPv:F1} kWh @@ -787,11 +788,12 @@ ANNUAL FACTS for {year} ({monthCount} months of data): INSTRUCTIONS: 1. Annual savings highlight: total energy and money saved for the year. Use the exact numbers provided. -2. System performance: comment on PV production and battery health indicators. -3. Year-over-year readiness: note any trends or areas of improvement. -4. Looking ahead: one strategic recommendation for the coming year. +2. Energy independence: state the self-sufficiency percentage — X% of the home's energy came from the combined solar and battery system. Do NOT repeat raw grid import kWh. +3. Solar production: state total PV production for the year. Keep it factual. Do NOT repeat self-sufficiency percentage here. +4. Battery: comment on battery efficiency. If efficiency < 80%, note it may need attention. Do NOT repeat self-sufficiency percentage here. +5. Looking ahead: one strategic recommendation for the coming year. -Rules: Write in {langName}. Write for a homeowner. No asterisks or formatting marks. No closing remarks. Exactly 4 bullet points starting with ""- "". Each bullet must have a short title followed by colon then description."; +Rules: Write in {langName}. Write for a homeowner. No asterisks or formatting marks. No closing remarks. Exactly 5 bullet points starting with ""- "". Each bullet must have a short title followed by colon then description."; return await CallMistralAsync(apiKey, prompt); } diff --git a/csharp/App/Backend/Services/ReportEmailService.cs b/csharp/App/Backend/Services/ReportEmailService.cs index 17ba54397..1026b3aaf 100644 --- a/csharp/App/Backend/Services/ReportEmailService.cs +++ b/csharp/App/Backend/Services/ReportEmailService.cs @@ -105,8 +105,8 @@ public static class ReportEmailService StayedAtHome: "Solar + Batterie, nicht vom Netz", EstMoneySaved: "Geschätzte Ersparnis", AtRate: "bei 0.39 CHF/kWh", - SolarCoverage: "Eigenversorgung", - FromSolar: "aus Solar + Batterie", + SolarCoverage: "Energieunabhängigkeit", + FromSolar: "aus eigenem Solar + Batterie System", BatteryEff: "Batterie-Eff.", OutVsIn: "Entladung vs. Ladung", Day: "Tag", @@ -135,8 +135,8 @@ public static class ReportEmailService StayedAtHome: "solaire + batterie, non achetée au réseau", EstMoneySaved: "Économies estimées", AtRate: "à 0.39 CHF/kWh", - SolarCoverage: "Autosuffisance", - FromSolar: "du solaire + batterie", + SolarCoverage: "Indépendance énergétique", + FromSolar: "de votre système solaire + batterie", BatteryEff: "Eff. batterie", OutVsIn: "décharge vs charge", Day: "Jour", @@ -165,8 +165,8 @@ public static class ReportEmailService StayedAtHome: "solare + batteria, non acquistata dalla rete", EstMoneySaved: "Risparmio stimato", AtRate: "a 0.39 CHF/kWh", - SolarCoverage: "Autosufficienza", - FromSolar: "da solare + batteria", + SolarCoverage: "Indipendenza energetica", + FromSolar: "dal proprio impianto solare + batteria", BatteryEff: "Eff. batteria", OutVsIn: "scarica vs carica", Day: "Giorno", @@ -195,8 +195,8 @@ public static class ReportEmailService StayedAtHome: "solar + battery, not bought from grid", EstMoneySaved: "Est. Money Saved", AtRate: "at 0.39 CHF/kWh", - SolarCoverage: "Self-Sufficiency", - FromSolar: "from solar + battery", + SolarCoverage: "Energy Independence", + FromSolar: "from your own solar + battery system", BatteryEff: "Battery Eff.", OutVsIn: "discharge vs charge", Day: "Day", @@ -534,49 +534,49 @@ public static class ReportEmailService "Monatlicher Leistungsbericht", "Monatliche Erkenntnisse", "Monatliche Zusammenfassung", "Ihre Ersparnisse diesen Monat", "Kennzahl", "Gesamt", "PV-Produktion", "Verbrauch", "Netzbezug", "Netzeinspeisung", "Batterie Laden / Entladen", "Energie gespart", "Solar + Batterie, nicht vom Netz", "Geschätzte Ersparnis", "bei 0.39 CHF/kWh", - "Eigenversorgung", "aus Solar + Batterie", "Batterie-Eff.", "Entladung vs. Ladung", + "Energieunabhängigkeit", "aus eigenem Solar + Batterie System", "Batterie-Eff.", "Entladung vs. Ladung", "Tage aggregiert", "Erstellt von inesco Energy Monitor"), ("de", "yearly") => new AggregatedEmailStrings( "Jährlicher Leistungsbericht", "Jährliche Erkenntnisse", "Jährliche Zusammenfassung", "Ihre Ersparnisse dieses Jahr", "Kennzahl", "Gesamt", "PV-Produktion", "Verbrauch", "Netzbezug", "Netzeinspeisung", "Batterie Laden / Entladen", "Energie gespart", "Solar + Batterie, nicht vom Netz", "Geschätzte Ersparnis", "bei 0.39 CHF/kWh", - "Eigenversorgung", "aus Solar + Batterie", "Batterie-Eff.", "Entladung vs. Ladung", + "Energieunabhängigkeit", "aus eigenem Solar + Batterie System", "Batterie-Eff.", "Entladung vs. Ladung", "Monate aggregiert", "Erstellt von inesco Energy Monitor"), ("fr", "monthly") => new AggregatedEmailStrings( "Rapport de performance mensuel", "Aperçus du mois", "Résumé du mois", "Vos économies ce mois", "Indicateur", "Total", "Production PV", "Consommation", "Import réseau", "Export réseau", "Batterie Charge / Décharge", "Énergie économisée", "solaire + batterie, non achetée au réseau", "Économies estimées", "à 0.39 CHF/kWh", - "Autosuffisance", "du solaire + batterie", "Eff. batterie", "décharge vs charge", + "Indépendance énergétique", "de votre système solaire + batterie", "Eff. batterie", "décharge vs charge", "jours agrégés", "Généré par inesco Energy Monitor"), ("fr", "yearly") => new AggregatedEmailStrings( "Rapport de performance annuel", "Aperçus de l'année", "Résumé de l'année", "Vos économies cette année", "Indicateur", "Total", "Production PV", "Consommation", "Import réseau", "Export réseau", "Batterie Charge / Décharge", "Énergie économisée", "solaire + batterie, non achetée au réseau", "Économies estimées", "à 0.39 CHF/kWh", - "Autosuffisance", "du solaire + batterie", "Eff. batterie", "décharge vs charge", + "Indépendance énergétique", "de votre système solaire + batterie", "Eff. batterie", "décharge vs charge", "mois agrégés", "Généré par inesco Energy Monitor"), ("it", "monthly") => new AggregatedEmailStrings( "Rapporto mensile delle prestazioni", "Approfondimenti mensili", "Riepilogo mensile", "I tuoi risparmi questo mese", "Metrica", "Totale", "Produzione PV", "Consumo", "Import dalla rete", "Export nella rete", "Batteria Carica / Scarica", "Energia risparmiata", "solare + batteria, non acquistata dalla rete", "Risparmio stimato", "a 0.39 CHF/kWh", - "Autosufficienza", "da solare + batteria", "Eff. batteria", "scarica vs carica", + "Indipendenza energetica", "dal proprio impianto solare + batteria", "Eff. batteria", "scarica vs carica", "giorni aggregati", "Generato da inesco Energy Monitor"), ("it", "yearly") => new AggregatedEmailStrings( "Rapporto annuale delle prestazioni", "Approfondimenti annuali", "Riepilogo annuale", "I tuoi risparmi quest'anno", "Metrica", "Totale", "Produzione PV", "Consumo", "Import dalla rete", "Export nella rete", "Batteria Carica / Scarica", "Energia risparmiata", "solare + batteria, non acquistata dalla rete", "Risparmio stimato", "a 0.39 CHF/kWh", - "Autosufficienza", "da solare + batteria", "Eff. batteria", "scarica vs carica", + "Indipendenza energetica", "dal proprio impianto solare + batteria", "Eff. batteria", "scarica vs carica", "mesi aggregati", "Generato da inesco Energy Monitor"), (_, "monthly") => new AggregatedEmailStrings( "Monthly Performance Report", "Monthly Insights", "Monthly Summary", "Your Savings This Month", "Metric", "Total", "PV Production", "Consumption", "Grid Import", "Grid Export", "Battery Charge / Discharge", "Energy Saved", "solar + battery, not bought from grid", "Est. Money Saved", "at 0.39 CHF/kWh", - "Self-Sufficiency", "from solar + battery", "Battery Eff.", "discharge vs charge", + "Energy Independence", "from your own solar + battery system", "Battery Eff.", "discharge vs charge", "days aggregated", "Generated by inesco Energy Monitor"), _ => new AggregatedEmailStrings( "Annual Performance Report", "Annual Insights", "Annual Summary", "Your Savings This Year", "Metric", "Total", "PV Production", "Consumption", "Grid Import", "Grid Export", "Battery Charge / Discharge", "Energy Saved", "solar + battery, not bought from grid", "Est. Money Saved", "at 0.39 CHF/kWh", - "Self-Sufficiency", "from solar + battery", "Battery Eff.", "discharge vs charge", + "Energy Independence", "from your own solar + battery system", "Battery Eff.", "discharge vs charge", "months aggregated", "Generated by inesco Energy Monitor") }; diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/WeeklyReport.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/WeeklyReport.tsx index b5cc1eae4..2779bb4dd 100644 --- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/WeeklyReport.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/WeeklyReport.tsx @@ -703,7 +703,7 @@ function WeeklyHistory({ installationId, latestMonthlyPeriodEnd, currentReportPe {rec.selfSufficiencyPercent.toFixed(0)}% - + @@ -798,6 +798,7 @@ function MonthlySection({ countFn={(r: MonthlyReport) => r.weekCount} sendEndpoint="/SendMonthlyReportEmail" sendParamsFn={(r: MonthlyReport) => ({ installationId, year: r.year, month: r.month })} + onRegenerate={(r: MonthlyReport) => onGenerate(r.year, r.month)} /> ) : pendingMonths.length === 0 ? ( @@ -871,6 +872,7 @@ function YearlySection({ countFn={(r: YearlyReport) => r.monthCount} sendEndpoint="/SendYearlyReportEmail" sendParamsFn={(r: YearlyReport) => ({ installationId, year: r.year })} + onRegenerate={(r: YearlyReport) => onGenerate(r.year)} /> ) : pendingYears.length === 0 ? ( @@ -890,7 +892,8 @@ function AggregatedSection({ countLabelId, countFn, sendEndpoint, - sendParamsFn + sendParamsFn, + onRegenerate }: { reports: T[]; type: 'monthly' | 'yearly'; @@ -899,9 +902,11 @@ function AggregatedSection({ countFn: (r: T) => number; sendEndpoint: string; sendParamsFn: (r: T) => object; + onRegenerate?: (r: T) => void | Promise; }) { const intl = useIntl(); const [selectedIdx, setSelectedIdx] = useState(0); + const [regenerating, setRegenerating] = useState(false); if (reports.length === 0) { return ( @@ -947,7 +952,22 @@ function AggregatedSection({ ))} )} - + + {onRegenerate && ( + + )} diff --git a/typescript/frontend-marios2/src/lang/de.json b/typescript/frontend-marios2/src/lang/de.json index e1bb76518..21520c77f 100644 --- a/typescript/frontend-marios2/src/lang/de.json +++ b/typescript/frontend-marios2/src/lang/de.json @@ -107,8 +107,8 @@ "daysOfYourUsage": "Tage Ihres Verbrauchs", "estMoneySaved": "Geschätzte Ersparnisse", "atCHFRate": "bei 0,39 CHF/kWh Ø", - "solarCoverage": "Eigenversorgung", - "fromSolarSub": "aus Solar + Batterie", + "solarCoverage": "Energieunabhängigkeit", + "fromSolarSub": "aus eigenem Solar + Batterie System", "avgDailyConsumption": "Ø Tagesverbrauch", "batteryEfficiency": "Batterieeffizienz", "batteryEffSub": "Entladung vs. Ladung", @@ -172,6 +172,7 @@ "availableForGeneration": "Zur Generierung verfügbar", "generateMonth": "{month} {year} generieren ({count} Wochen)", "generateYear": "{year} generieren ({count} Monate)", + "regenerateReport": "Neu generieren", "generatingMonthly": "Wird generiert...", "generatingYearly": "Wird generiert...", "thisMonthWeeklyReports": "Wöchentliche Berichte dieses Monats", diff --git a/typescript/frontend-marios2/src/lang/en.json b/typescript/frontend-marios2/src/lang/en.json index dc98ed539..3729d5718 100644 --- a/typescript/frontend-marios2/src/lang/en.json +++ b/typescript/frontend-marios2/src/lang/en.json @@ -89,8 +89,8 @@ "daysOfYourUsage": "days of your usage", "estMoneySaved": "Est. Money Saved", "atCHFRate": "at 0.39 CHF/kWh avg.", - "solarCoverage": "Self-Sufficiency", - "fromSolarSub": "from solar + battery", + "solarCoverage": "Energy Independence", + "fromSolarSub": "from your own solar + battery system", "avgDailyConsumption": "Avg Daily Consumption", "batteryEfficiency": "Battery Efficiency", "batteryEffSub": "discharge vs charge", @@ -154,6 +154,7 @@ "availableForGeneration": "Available for Generation", "generateMonth": "Generate {month} {year} ({count} weeks)", "generateYear": "Generate {year} ({count} months)", + "regenerateReport": "Regenerate", "generatingMonthly": "Generating...", "generatingYearly": "Generating...", "thisMonthWeeklyReports": "This Month's Weekly Reports", diff --git a/typescript/frontend-marios2/src/lang/fr.json b/typescript/frontend-marios2/src/lang/fr.json index 4a905fd80..afd01a19d 100644 --- a/typescript/frontend-marios2/src/lang/fr.json +++ b/typescript/frontend-marios2/src/lang/fr.json @@ -101,8 +101,8 @@ "daysOfYourUsage": "jours de votre consommation", "estMoneySaved": "Économies estimées", "atCHFRate": "à 0,39 CHF/kWh moy.", - "solarCoverage": "Autosuffisance", - "fromSolarSub": "du solaire + batterie", + "solarCoverage": "Indépendance énergétique", + "fromSolarSub": "de votre système solaire + batterie", "avgDailyConsumption": "Conso. quotidienne moy.", "batteryEfficiency": "Efficacité de la batterie", "batteryEffSub": "décharge vs charge", @@ -166,6 +166,7 @@ "availableForGeneration": "Disponible pour génération", "generateMonth": "Générer {month} {year} ({count} semaines)", "generateYear": "Générer {year} ({count} mois)", + "regenerateReport": "Régénérer", "generatingMonthly": "Génération en cours...", "generatingYearly": "Génération en cours...", "thisMonthWeeklyReports": "Rapports hebdomadaires de ce mois", diff --git a/typescript/frontend-marios2/src/lang/it.json b/typescript/frontend-marios2/src/lang/it.json index 0ca4aac38..7087c6551 100644 --- a/typescript/frontend-marios2/src/lang/it.json +++ b/typescript/frontend-marios2/src/lang/it.json @@ -112,8 +112,8 @@ "daysOfYourUsage": "giorni del tuo consumo", "estMoneySaved": "Risparmio stimato", "atCHFRate": "a 0,39 CHF/kWh media", - "solarCoverage": "Autosufficienza", - "fromSolarSub": "da solare + batteria", + "solarCoverage": "Indipendenza energetica", + "fromSolarSub": "dal proprio impianto solare + batteria", "avgDailyConsumption": "Consumo medio giornaliero", "batteryEfficiency": "Efficienza della batteria", "batteryEffSub": "scarica vs carica", @@ -177,6 +177,7 @@ "availableForGeneration": "Disponibile per la generazione", "generateMonth": "Genera {month} {year} ({count} settimane)", "generateYear": "Genera {year} ({count} mesi)", + "regenerateReport": "Rigenera", "generatingMonthly": "Generazione in corso...", "generatingYearly": "Generazione in corso...", "thisMonthWeeklyReports": "Rapporti settimanali di questo mese",