828 lines
102 KiB
C#
828 lines
102 KiB
C#
using System.Text.Json;
|
||
using InnovEnergy.App.Backend.DataTypes;
|
||
using InnovEnergy.Lib.Mailer;
|
||
using MailKit.Net.Smtp;
|
||
using MailKit.Security;
|
||
using MimeKit;
|
||
|
||
namespace InnovEnergy.App.Backend.Services;
|
||
|
||
public static class ReportEmailService
|
||
{
|
||
// inesco logo (dark background variant, PNG) embedded as base64 for CID attachment in emails
|
||
private static readonly byte[] LogoPngBytes = Convert.FromBase64String("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");
|
||
private const string LogoCid = "inesco-logo";
|
||
private const string LogoDataUri = "data:image/png;base64,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";
|
||
|
||
// NOTE: old PNG constant removed — using SVG dark-bg variant (data:image/png;base64,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";
|
||
|
||
/// <summary>
|
||
/// Sends the weekly report as a nicely formatted HTML email in the user's language.
|
||
/// Uses MailKit directly (same config as existing Mailer library) but with HTML support.
|
||
/// </summary>
|
||
public static async Task SendReportEmailAsync(WeeklyReportResponse report, string recipientEmail, string language = "en", string customerName = null)
|
||
{
|
||
var strings = GetStrings(language);
|
||
var installSegment = !string.IsNullOrWhiteSpace(report.InstallationName) ? $" — {report.InstallationName}" : "";
|
||
var subject = $"{strings.Title}{installSegment} ({report.PeriodStart} to {report.PeriodEnd})";
|
||
var html = BuildHtmlEmail(report, strings, customerName, $"cid:{LogoCid}");
|
||
|
||
var config = await ReadMailerConfig();
|
||
|
||
var from = new MailboxAddress(config.SenderName, config.SenderAddress);
|
||
var to = new MailboxAddress(recipientEmail, recipientEmail);
|
||
|
||
var msg = new MimeMessage
|
||
{
|
||
From = { from },
|
||
To = { to },
|
||
Subject = subject,
|
||
};
|
||
|
||
msg.Body = BuildMultipartBodyWithLogo(html);
|
||
|
||
Console.WriteLine($"[ReportEmailService] SMTP: {config.SmtpUsername}@{config.SmtpServerUrl}:{config.SmtpPort}");
|
||
|
||
using var smtp = new SmtpClient();
|
||
await smtp.ConnectAsync(config.SmtpServerUrl, config.SmtpPort, SecureSocketOptions.StartTls);
|
||
await smtp.AuthenticateAsync(config.SmtpUsername, config.SmtpPassword);
|
||
await smtp.SendAsync(msg);
|
||
await smtp.DisconnectAsync(true);
|
||
|
||
Console.WriteLine($"[ReportEmailService] Report sent to {recipientEmail}");
|
||
}
|
||
|
||
private static async Task<MailerConfig> ReadMailerConfig()
|
||
{
|
||
await using var fileStream = File.OpenRead(MailerConfig.DefaultFile);
|
||
var config = await JsonSerializer.DeserializeAsync<MailerConfig>(fileStream);
|
||
return config ?? throw new InvalidOperationException("Failed to read MailerConfig.json");
|
||
}
|
||
|
||
// ── Translation strings ─────────────────────────────────────────────────
|
||
|
||
private record EmailStrings(
|
||
string Title,
|
||
string Insights,
|
||
string Summary,
|
||
string SavingsHeader,
|
||
string DailyBreakdown,
|
||
string Metric,
|
||
string ThisWeek,
|
||
string LastWeek,
|
||
string Change,
|
||
string PvProduction,
|
||
string Consumption,
|
||
string GridImport,
|
||
string GridExport,
|
||
string BatteryInOut,
|
||
string SolarEnergyUsed,
|
||
string StayedAtHome,
|
||
string EstMoneySaved,
|
||
string AtRate,
|
||
string SolarCoverage,
|
||
string FromSolar,
|
||
string BatteryEff,
|
||
string OutVsIn,
|
||
string Day,
|
||
string Load,
|
||
string GridIn,
|
||
string GridOut,
|
||
string BattInOut,
|
||
string Footer,
|
||
string FooterLink
|
||
);
|
||
|
||
private static EmailStrings GetStrings(string language) => language switch
|
||
{
|
||
"de" => new EmailStrings(
|
||
Title: "Wöchentlicher Leistungsbericht",
|
||
Insights: "Wöchentliche Erkenntnisse",
|
||
Summary: "Wöchentliche Zusammenfassung",
|
||
SavingsHeader: "Ihre Ersparnisse diese Woche",
|
||
DailyBreakdown: "Tägliche Aufschlüsselung (kWh)",
|
||
Metric: "Kennzahl",
|
||
ThisWeek: "Diese Woche",
|
||
LastWeek: "Letzte Woche",
|
||
Change: "Änderung",
|
||
PvProduction: "PV-Produktion",
|
||
Consumption: "Verbrauch",
|
||
GridImport: "Netzbezug",
|
||
GridExport: "Netzeinspeisung",
|
||
BatteryInOut: "Batterie Laden / Entladen",
|
||
SolarEnergyUsed: "Energie gespart",
|
||
StayedAtHome: "Solar + Batterie, nicht vom Netz",
|
||
EstMoneySaved: "Geschätzte Ersparnis",
|
||
AtRate: "bei 0.39 CHF/kWh",
|
||
SolarCoverage: "Energieunabhängigkeit",
|
||
FromSolar: "aus eigenem Solar + Batterie System",
|
||
BatteryEff: "Batterie-Eff.",
|
||
OutVsIn: "Entladung vs. Ladung",
|
||
Day: "Tag",
|
||
Load: "Last",
|
||
GridIn: "Netz Ein",
|
||
GridOut: "Netz Aus",
|
||
BattInOut: "Batt. Laden/Entl.",
|
||
Footer: "Erstellt von <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
FooterLink: "Detaillierte Berichte ansehen auf monitor.inesco.energy"
|
||
),
|
||
"fr" => new EmailStrings(
|
||
Title: "Rapport de performance hebdomadaire",
|
||
Insights: "Aperçus de la semaine",
|
||
Summary: "Résumé de la semaine",
|
||
SavingsHeader: "Vos économies cette semaine",
|
||
DailyBreakdown: "Détail quotidien (kWh)",
|
||
Metric: "Indicateur",
|
||
ThisWeek: "Cette semaine",
|
||
LastWeek: "Semaine dernière",
|
||
Change: "Variation",
|
||
PvProduction: "Production PV",
|
||
Consumption: "Consommation",
|
||
GridImport: "Import réseau",
|
||
GridExport: "Export réseau",
|
||
BatteryInOut: "Batterie Charge / Décharge",
|
||
SolarEnergyUsed: "Énergie économisée",
|
||
StayedAtHome: "solaire + batterie, non achetée au réseau",
|
||
EstMoneySaved: "Économies estimées",
|
||
AtRate: "à 0.39 CHF/kWh",
|
||
SolarCoverage: "Indépendance énergétique",
|
||
FromSolar: "de votre système solaire + batterie",
|
||
BatteryEff: "Eff. batterie",
|
||
OutVsIn: "décharge vs charge",
|
||
Day: "Jour",
|
||
Load: "Charge",
|
||
GridIn: "Réseau Ent.",
|
||
GridOut: "Réseau Sor.",
|
||
BattInOut: "Batt. Ch./Déch.",
|
||
Footer: "Généré par <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
FooterLink: "Consultez vos rapports détaillés sur monitor.inesco.energy"
|
||
),
|
||
"it" => new EmailStrings(
|
||
Title: "Rapporto settimanale delle prestazioni",
|
||
Insights: "Approfondimenti settimanali",
|
||
Summary: "Riepilogo settimanale",
|
||
SavingsHeader: "I tuoi risparmi questa settimana",
|
||
DailyBreakdown: "Dettaglio giornaliero (kWh)",
|
||
Metric: "Metrica",
|
||
ThisWeek: "Questa settimana",
|
||
LastWeek: "La settimana scorsa",
|
||
Change: "Variazione",
|
||
PvProduction: "Produzione PV",
|
||
Consumption: "Consumo",
|
||
GridImport: "Import dalla rete",
|
||
GridExport: "Export nella rete",
|
||
BatteryInOut: "Batteria Carica / Scarica",
|
||
SolarEnergyUsed: "Energia risparmiata",
|
||
StayedAtHome: "solare + batteria, non acquistata dalla rete",
|
||
EstMoneySaved: "Risparmio stimato",
|
||
AtRate: "a 0.39 CHF/kWh",
|
||
SolarCoverage: "Indipendenza energetica",
|
||
FromSolar: "dal proprio impianto solare + batteria",
|
||
BatteryEff: "Eff. batteria",
|
||
OutVsIn: "scarica vs carica",
|
||
Day: "Giorno",
|
||
Load: "Carico",
|
||
GridIn: "Rete Ent.",
|
||
GridOut: "Rete Usc.",
|
||
BattInOut: "Batt. Car./Sc.",
|
||
Footer: "Generato da <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
FooterLink: "Visualizza i tuoi report dettagliati su monitor.inesco.energy"
|
||
),
|
||
_ => new EmailStrings(
|
||
Title: "Weekly Performance Report",
|
||
Insights: "Weekly Insights",
|
||
Summary: "Weekly Summary",
|
||
SavingsHeader: "Your Savings This Week",
|
||
DailyBreakdown: "Daily Breakdown (kWh)",
|
||
Metric: "Metric",
|
||
ThisWeek: "This Week",
|
||
LastWeek: "Last Week",
|
||
Change: "Change",
|
||
PvProduction: "PV Production",
|
||
Consumption: "Consumption",
|
||
GridImport: "Grid Import",
|
||
GridExport: "Grid Export",
|
||
BatteryInOut: "Battery Charge / Discharge",
|
||
SolarEnergyUsed: "Energy Saved",
|
||
StayedAtHome: "solar + battery, not bought from grid",
|
||
EstMoneySaved: "Est. Money Saved",
|
||
AtRate: "at 0.39 CHF/kWh",
|
||
SolarCoverage: "Energy Independence",
|
||
FromSolar: "from your own solar + battery system",
|
||
BatteryEff: "Battery Eff.",
|
||
OutVsIn: "discharge vs charge",
|
||
Day: "Day",
|
||
Load: "Load",
|
||
GridIn: "Grid In",
|
||
GridOut: "Grid Out",
|
||
BattInOut: "Batt. Ch./Dis.",
|
||
Footer: "Generated by <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
FooterLink: "View your detailed reports at monitor.inesco.energy"
|
||
)
|
||
};
|
||
|
||
// ── HTML email template ─────────────────────────────────────────────
|
||
|
||
public static string BuildHtmlEmail(WeeklyReportResponse r, string language = "en", string customerName = null, string logoSrc = null, string source = "email")
|
||
=> BuildHtmlEmail(r, GetStrings(language), customerName, logoSrc, source);
|
||
|
||
private static string BuildHtmlEmail(WeeklyReportResponse r, EmailStrings s, string customerName = null, string logoSrc = null, string source = "email")
|
||
{
|
||
logoSrc ??= LogoDataUri;
|
||
var cur = r.CurrentWeek;
|
||
var prev = r.PreviousWeek;
|
||
|
||
// Parse AI insight into <li> bullet points (split on newlines, strip leading "- " or "1. ", strip ** markdown)
|
||
var insightLines = r.AiInsight
|
||
.Split('\n', StringSplitOptions.RemoveEmptyEntries)
|
||
.Select(l => System.Text.RegularExpressions.Regex.Replace(l.Trim(), @"^[\d]+[.)]\s*|^[-*]\s*", "").Replace("**", ""))
|
||
.Where(l => l.Length > 0)
|
||
.ToList();
|
||
|
||
var insightHtml = insightLines.Count > 1
|
||
? "<ul style=\"margin:0;padding-left:20px\">" +
|
||
string.Join("", insightLines.Select(l => $"<li style=\"margin-bottom:8px;line-height:1.6\">{FormatInsightLine(l)}</li>")) +
|
||
"</ul>"
|
||
: $"<p style=\"margin:0;line-height:1.6\">{FormatInsightLine(r.AiInsight)}</p>";
|
||
|
||
// Detect which components are present across all daily data
|
||
var showPv = r.DailyData.Any(d => d.PvProduction > 0.1);
|
||
var showGrid = r.DailyData.Any(d => d.GridImport > 0.1);
|
||
|
||
// Daily rows — colorful bar chart (pixel widths, email-safe)
|
||
// Scale each day's bars so their combined total always fills maxBarPx (right-edge aligned).
|
||
// This replicates the web page's CSS flexbox flex-shrink:1 behaviour.
|
||
const int maxBarPx = 400;
|
||
|
||
var dailyRows = "";
|
||
foreach (var d in r.DailyData)
|
||
{
|
||
var dayName = DateTime.Parse(d.Date).ToString("ddd dd.MM");
|
||
var isCurrentWeek = string.Compare(d.Date, r.PeriodStart, StringComparison.Ordinal) >= 0;
|
||
var opacity = isCurrentWeek ? "1" : "0.55";
|
||
var fontWeight = isCurrentWeek ? "bold" : "normal";
|
||
var dayTotal = (showPv ? d.PvProduction : 0) + d.LoadConsumption + (showGrid ? d.GridImport : 0);
|
||
if (dayTotal < 0.1) dayTotal = 0.1;
|
||
var pvPx = showPv ? (int)(d.PvProduction / dayTotal * maxBarPx) : 0;
|
||
var ldPx = (int)(d.LoadConsumption / dayTotal * maxBarPx);
|
||
var giPx = showGrid ? (int)(d.GridImport / dayTotal * maxBarPx) : 0;
|
||
|
||
var pvSpan = showPv ? $@"<span style=""display:inline-block;height:14px;background:#f39c12;width:{pvPx}px;border-radius:2px 0 0 2px""></span>" : "";
|
||
var gridSpan = showGrid ? $@"<span style=""display:inline-block;height:14px;background:#e74c3c;width:{giPx}px;border-radius:0 2px 2px 0;margin-left:2px""></span>" : "";
|
||
var ldRadius = (!showPv ? "border-radius:2px 0 0 2px;" : "") + (!showGrid ? "border-radius:0 2px 2px 0;" : "");
|
||
|
||
var valueText = (showPv ? $"PV {d.PvProduction:F1} | " : "")
|
||
+ $"{s.Load} {d.LoadConsumption:F1}"
|
||
+ (showGrid ? $" | {s.GridIn} {d.GridImport:F1}" : "")
|
||
+ " kWh";
|
||
|
||
dailyRows += $@"
|
||
<tr style=""opacity:{opacity};border-bottom:1px solid #f0f0f0"">
|
||
<td style=""padding:6px 8px;font-size:12px;font-weight:{fontWeight};white-space:nowrap;width:80px;vertical-align:top;padding-top:10px"">{dayName}</td>
|
||
<td style=""padding:4px 8px"">
|
||
<div style=""font-size:10px;color:#888;margin-bottom:3px;text-align:right"">{valueText}</div>
|
||
<div style=""height:14px;line-height:14px;font-size:0;white-space:nowrap;width:{maxBarPx}px"">{pvSpan}<span style=""display:inline-block;height:14px;background:#3498db;width:{ldPx}px;{ldRadius}margin-left:{(showPv ? 2 : 0)}px""></span>{gridSpan}</div>
|
||
</td>
|
||
</tr>";
|
||
}
|
||
|
||
// Week-over-week comparison rows
|
||
var comparisonHtml = prev != null
|
||
? $@"
|
||
<tr>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.PvProduction}</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{cur.TotalPvProduction:F1} kWh</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;color:#888"">{prev.TotalPvProduction:F1} kWh</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;color:{ChangeColor(r.PvChangePercent)}"">{FormatChange(r.PvChangePercent)}</td>
|
||
</tr>
|
||
<tr>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.Consumption}</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{cur.TotalConsumption:F1} kWh</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;color:#888"">{prev.TotalConsumption:F1} kWh</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;color:{ChangeColor(-r.ConsumptionChangePercent)}"">{FormatChange(r.ConsumptionChangePercent)}</td>
|
||
</tr>
|
||
<tr>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.GridImport}</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{cur.TotalGridImport:F1} kWh</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;color:#888"">{prev.TotalGridImport:F1} kWh</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;color:{ChangeColor(-r.GridImportChangePercent)}"">{FormatChange(r.GridImportChangePercent)}</td>
|
||
</tr>
|
||
<tr>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.GridExport}</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{cur.TotalGridExport:F1} kWh</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;color:#888"">{prev.TotalGridExport:F1} kWh</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right"">—</td>
|
||
</tr>
|
||
<tr>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.BatteryInOut}</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{cur.TotalBatteryCharged:F1}/{cur.TotalBatteryDischarged:F1} kWh</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;color:#888"">{prev.TotalBatteryCharged:F1}/{prev.TotalBatteryDischarged:F1} kWh</td>
|
||
<td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right"">—</td>
|
||
</tr>"
|
||
: $@"
|
||
<tr><td style=""padding:8px 12px"">{s.PvProduction}</td><td style=""padding:8px 12px;text-align:right;font-weight:bold"">{cur.TotalPvProduction:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px"">{s.Consumption}</td><td style=""padding:8px 12px;text-align:right;font-weight:bold"">{cur.TotalConsumption:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px"">{s.GridImport}</td><td style=""padding:8px 12px;text-align:right;font-weight:bold"">{cur.TotalGridImport:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px"">{s.GridExport}</td><td style=""padding:8px 12px;text-align:right;font-weight:bold"">{cur.TotalGridExport:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px"">{s.BatteryInOut}</td><td style=""padding:8px 12px;text-align:right;font-weight:bold"">{cur.TotalBatteryCharged:F1}/{cur.TotalBatteryDischarged:F1} kWh</td></tr>";
|
||
|
||
var comparisonHeaders = prev != null
|
||
? $@"<th style=""padding:8px 12px;text-align:right"">{s.ThisWeek}</th>
|
||
<th style=""padding:8px 12px;text-align:right"">{s.LastWeek}</th>
|
||
<th style=""padding:8px 12px;text-align:right"">{s.Change}</th>"
|
||
: $@"<th style=""padding:8px 12px;text-align:right"">{s.ThisWeek}</th>";
|
||
|
||
var footerHtml = source == "web" ? "" : $@"
|
||
<!-- Footer -->
|
||
<tr>
|
||
<td style=""background:#f8f9fa;padding:16px 30px;text-align:center;font-size:12px;color:#999;border-top:1px solid #eee"">
|
||
{s.Footer}
|
||
<div style=""margin-top:10px""><a href=""https://monitor.inesco.energy"" style=""color:#999;text-decoration:underline"">{s.FooterLink}</a></div>
|
||
</td>
|
||
</tr>";
|
||
|
||
var bgColor = source == "web" ? "transparent" : "#f4f4f4";
|
||
|
||
return $@"
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head><meta charset=""utf-8""><style>*{{-webkit-print-color-adjust:exact!important;color-adjust:exact!important;print-color-adjust:exact!important}}@page{{margin:10mm;size:A4}}</style></head>
|
||
<body style=""margin:0;padding:0;background:{bgColor};font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#333"">
|
||
<table width=""100%"" cellpadding=""0"" cellspacing=""0"" style=""background:{bgColor};padding:20px 0"">
|
||
<tr><td align=""center"">
|
||
<table width=""{(source == "web" ? "100%" : "600")}"" cellpadding=""0"" cellspacing=""0"" style=""background:#ffffff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.08){(source == "web" ? ";max-width:900px;width:100%" : "")}"">
|
||
|
||
<!-- Header -->
|
||
<tr>
|
||
<td style=""background:#2c3e50;padding:24px 30px;color:#ffffff"">
|
||
<img src=""{logoSrc}"" alt=""inesco energy"" style=""height:36px;margin-bottom:12px"" />
|
||
<div style=""font-size:20px;font-weight:bold"">{s.Title}</div>
|
||
<div style=""font-size:14px;margin-top:6px;opacity:0.9"">{r.InstallationName}</div>
|
||
<div style=""font-size:13px;margin-top:2px;opacity:0.7"">{r.PeriodStart} — {r.PeriodEnd}</div>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- Weekly Insights (top) -->
|
||
<tr>
|
||
<td style=""padding:24px 30px 0"">
|
||
<div style=""font-size:16px;font-weight:bold;margin-bottom:12px;color:#2c3e50"">{s.Insights}</div>
|
||
<div style=""background:#fef9e7;border-left:4px solid #f39c12;padding:14px 18px;border-radius:0 6px 6px 0;font-size:14px;color:#333"">
|
||
{insightHtml}
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- Weekly Totals -->
|
||
<tr>
|
||
<td style=""padding:24px 30px"">
|
||
<div style=""font-size:16px;font-weight:bold;margin-bottom:12px;color:#2c3e50"">{s.Summary}</div>
|
||
<table width=""100%"" cellpadding=""0"" cellspacing=""0"" style=""border:1px solid #eee;border-radius:4px"">
|
||
<tr style=""background:#f8f9fa"">
|
||
<th style=""padding:8px 12px;text-align:left"">{s.Metric}</th>
|
||
{comparisonHeaders}
|
||
</tr>
|
||
{comparisonHtml}
|
||
</table>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- Key Ratios -->
|
||
<tr>
|
||
<td style=""padding:0 30px 24px"">
|
||
<div style=""font-size:16px;font-weight:bold;margin-bottom:12px;color:#2c3e50"">{s.SavingsHeader}</div>
|
||
<table width=""100%"" cellpadding=""0"" cellspacing=""8"">
|
||
<tr>
|
||
{SavingsBox(s.SolarEnergyUsed, $"{r.TotalEnergySaved:F1} kWh", s.StayedAtHome, "#27ae60")}
|
||
{SavingsBox(s.EstMoneySaved, $"~{r.TotalSavingsCHF:F0} CHF", s.AtRate, "#2980b9")}
|
||
{SavingsBox(s.SolarCoverage, $"{r.SelfSufficiencyPercent:F0}%", s.FromSolar, "#8e44ad")}
|
||
{SavingsBox(s.BatteryEff, $"{r.BatteryEfficiencyPercent:F0}%", s.OutVsIn, "#e67e22")}
|
||
</tr>
|
||
</table>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- Daily Breakdown (bar chart) -->
|
||
<tr>
|
||
<td style=""padding:0 30px 24px"">
|
||
<div style=""font-size:16px;font-weight:bold;margin-bottom:8px;color:#2c3e50"">{s.DailyBreakdown}</div>
|
||
<table width=""100%"" cellpadding=""0"" cellspacing=""0"" style=""border:1px solid #eee;border-radius:4px;font-size:13px"">
|
||
<!-- Legend -->
|
||
<tr style=""background:#f8f9fa"">
|
||
<td colspan=""2"" style=""padding:8px 10px;font-size:12px"">
|
||
{(showPv ? @$"<span style=""display:inline-block;width:10px;height:10px;background:#f39c12;border-radius:2px;margin-right:4px""></span>PV " : "")}
|
||
<span style=""display:inline-block;width:10px;height:10px;background:#3498db;border-radius:2px;margin-right:4px""></span>{s.Load}
|
||
{(showGrid ? @$"<span style=""display:inline-block;width:10px;height:10px;background:#e74c3c;border-radius:2px;margin-right:4px""></span>{s.GridIn}" : "")}
|
||
</td>
|
||
</tr>
|
||
{dailyRows}
|
||
</table>
|
||
</td>
|
||
</tr>
|
||
|
||
{footerHtml}
|
||
|
||
</table>
|
||
</td></tr>
|
||
</table>
|
||
</body>
|
||
</html>";
|
||
}
|
||
|
||
private static string SavingsBox(string label, string value, string subtitle, string color) =>
|
||
$@"<td width=""25%"" style=""text-align:center"">
|
||
<div style=""background:#f8f9fa;border-radius:6px;padding:12px 4px"">
|
||
<div style=""font-size:22px;font-weight:bold;color:{color}"">{value}</div>
|
||
<div style=""font-size:12px;font-weight:bold;color:#444;margin-top:4px"">{label}</div>
|
||
<div style=""font-size:10px;color:#888;margin-top:2px"">{subtitle}</div>
|
||
</div>
|
||
</td>";
|
||
|
||
// Bolds "Title" before first colon, and numbers+units in the rest
|
||
private static string FormatInsightLine(string line)
|
||
{
|
||
var colonIdx = line.IndexOf(':');
|
||
string result;
|
||
if (colonIdx > 0)
|
||
{
|
||
var title = line[..colonIdx];
|
||
var rest = line[colonIdx..]; // includes the colon
|
||
result = $"<strong>{title}</strong>{rest}";
|
||
}
|
||
else
|
||
{
|
||
result = line;
|
||
}
|
||
// Bold all numbers: time ranges (14:00–18:00), times (09:00), decimals, integers
|
||
result = System.Text.RegularExpressions.Regex.Replace(
|
||
result,
|
||
@"(\d{1,2}:\d{2}(?:[–\-]\d{1,2}:\d{2})?|\d+[.,]\d+|\d+)",
|
||
"<strong>$1</strong>");
|
||
return result;
|
||
}
|
||
|
||
private static string FormatChange(double pct) =>
|
||
pct == 0 ? "—" : pct > 0 ? $"+{pct:F1}%" : $"{pct:F1}%";
|
||
|
||
private static string ChangeColor(double pct) =>
|
||
pct > 0 ? "#27ae60" : pct < 0 ? "#e74c3c" : "#888";
|
||
|
||
// ── Monthly / Yearly Report Emails ────────────────────────────────────
|
||
|
||
private static readonly string[] MonthNamesEn = { "", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
|
||
private static readonly string[] MonthNamesDe = { "", "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" };
|
||
private static readonly string[] MonthNamesFr = { "", "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre" };
|
||
private static readonly string[] MonthNamesIt = { "", "Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre" };
|
||
|
||
public static async Task SendMonthlyReportEmailAsync(
|
||
MonthlyReportSummary report,
|
||
string installationName,
|
||
string recipientEmail,
|
||
string language = "en",
|
||
string customerName = null)
|
||
{
|
||
var monthNames = language switch { "de" => MonthNamesDe, "fr" => MonthNamesFr, "it" => MonthNamesIt, _ => MonthNamesEn };
|
||
var monthName = report.Month >= 1 && report.Month <= 12 ? monthNames[report.Month] : report.Month.ToString();
|
||
var s = GetAggregatedStrings(language, "monthly");
|
||
var installSegment = !string.IsNullOrWhiteSpace(installationName) ? $" — {installationName}" : "";
|
||
var subject = $"{s.Title}{installSegment} ({monthName} {report.Year})";
|
||
var html = BuildAggregatedHtmlEmail(report.PeriodStart, report.PeriodEnd, installationName,
|
||
report.TotalPvProduction, report.TotalConsumption, report.TotalGridImport, report.TotalGridExport,
|
||
report.TotalBatteryCharged, report.TotalBatteryDischarged, report.TotalEnergySaved, report.TotalSavingsCHF,
|
||
report.SelfSufficiencyPercent, report.BatteryEfficiencyPercent, report.AiInsight,
|
||
$"{report.WeekCount} {s.CountLabel}", s, customerName, $"cid:{LogoCid}");
|
||
|
||
await SendHtmlEmailAsync(subject, html, recipientEmail);
|
||
}
|
||
|
||
public static async Task SendYearlyReportEmailAsync(
|
||
YearlyReportSummary report,
|
||
string installationName,
|
||
string recipientEmail,
|
||
string language = "en",
|
||
string customerName = null)
|
||
{
|
||
var s = GetAggregatedStrings(language, "yearly");
|
||
var installSegment = !string.IsNullOrWhiteSpace(installationName) ? $" — {installationName}" : "";
|
||
var subject = $"{s.Title}{installSegment} ({report.Year})";
|
||
var html = BuildAggregatedHtmlEmail(report.PeriodStart, report.PeriodEnd, installationName,
|
||
report.TotalPvProduction, report.TotalConsumption, report.TotalGridImport, report.TotalGridExport,
|
||
report.TotalBatteryCharged, report.TotalBatteryDischarged, report.TotalEnergySaved, report.TotalSavingsCHF,
|
||
report.SelfSufficiencyPercent, report.BatteryEfficiencyPercent, report.AiInsight,
|
||
$"{report.MonthCount} {s.CountLabel}", s, customerName, $"cid:{LogoCid}");
|
||
|
||
await SendHtmlEmailAsync(subject, html, recipientEmail);
|
||
}
|
||
|
||
private static async Task SendHtmlEmailAsync(string subject, string html, string recipientEmail)
|
||
{
|
||
var config = await ReadMailerConfig();
|
||
var from = new MailboxAddress(config.SenderName, config.SenderAddress);
|
||
var to = new MailboxAddress(recipientEmail, recipientEmail);
|
||
|
||
var msg = new MimeMessage
|
||
{
|
||
From = { from },
|
||
To = { to },
|
||
Subject = subject,
|
||
};
|
||
|
||
msg.Body = BuildMultipartBodyWithLogo(html);
|
||
|
||
using var smtp = new SmtpClient();
|
||
await smtp.ConnectAsync(config.SmtpServerUrl, config.SmtpPort, SecureSocketOptions.StartTls);
|
||
await smtp.AuthenticateAsync(config.SmtpUsername, config.SmtpPassword);
|
||
await smtp.SendAsync(msg);
|
||
await smtp.DisconnectAsync(true);
|
||
|
||
Console.WriteLine($"[ReportEmailService] Report sent to {recipientEmail}");
|
||
}
|
||
|
||
private static MimeEntity BuildMultipartBodyWithLogo(string html)
|
||
{
|
||
var htmlPart = new TextPart("html") { Text = html };
|
||
|
||
var logoPart = new MimePart("image", "png")
|
||
{
|
||
Content = new MimeContent(new MemoryStream(LogoPngBytes)),
|
||
ContentId = LogoCid,
|
||
ContentDisposition = new ContentDisposition(ContentDisposition.Inline),
|
||
ContentTransferEncoding = ContentEncoding.Base64,
|
||
};
|
||
|
||
var related = new MultipartRelated();
|
||
related.Add(htmlPart);
|
||
related.Add(logoPart);
|
||
|
||
return related;
|
||
}
|
||
|
||
// ── Aggregated report translation strings ─────────────────────────────
|
||
|
||
public record AggregatedEmailStrings(
|
||
string Title, string Insights, string Summary, string SavingsHeader,
|
||
string Metric, string Total, string PvProduction, string Consumption,
|
||
string GridImport, string GridExport, string BatteryInOut,
|
||
string SolarEnergyUsed, string StayedAtHome, string EstMoneySaved,
|
||
string AtRate, string SolarCoverage, string FromSolar,
|
||
string BatteryEff, string OutVsIn, string CountLabel, string Footer,
|
||
string FooterLink
|
||
);
|
||
|
||
public static AggregatedEmailStrings GetAggregatedStrings(string language, string type) => (language, type) switch
|
||
{
|
||
("de", "monthly") => new AggregatedEmailStrings(
|
||
"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",
|
||
"Energieunabhängigkeit", "aus eigenem Solar + Batterie System", "Batterie-Eff.", "Entladung vs. Ladung",
|
||
"Tage aggregiert", "Erstellt von <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
"Detaillierte Berichte ansehen auf monitor.inesco.energy"),
|
||
("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",
|
||
"Energieunabhängigkeit", "aus eigenem Solar + Batterie System", "Batterie-Eff.", "Entladung vs. Ladung",
|
||
"Monate aggregiert", "Erstellt von <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
"Detaillierte Berichte ansehen auf monitor.inesco.energy"),
|
||
("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",
|
||
"Indépendance énergétique", "de votre système solaire + batterie", "Eff. batterie", "décharge vs charge",
|
||
"jours agrégés", "Généré par <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
"Consultez vos rapports détaillés sur monitor.inesco.energy"),
|
||
("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",
|
||
"Indépendance énergétique", "de votre système solaire + batterie", "Eff. batterie", "décharge vs charge",
|
||
"mois agrégés", "Généré par <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
"Consultez vos rapports détaillés sur monitor.inesco.energy"),
|
||
("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",
|
||
"Indipendenza energetica", "dal proprio impianto solare + batteria", "Eff. batteria", "scarica vs carica",
|
||
"giorni aggregati", "Generato da <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
"Visualizza i tuoi report dettagliati su monitor.inesco.energy"),
|
||
("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",
|
||
"Indipendenza energetica", "dal proprio impianto solare + batteria", "Eff. batteria", "scarica vs carica",
|
||
"mesi aggregati", "Generato da <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
"Visualizza i tuoi report dettagliati su monitor.inesco.energy"),
|
||
(_, "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",
|
||
"Energy Independence", "from your own solar + battery system", "Battery Eff.", "discharge vs charge",
|
||
"days aggregated", "Generated by <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
"View your detailed reports at monitor.inesco.energy"),
|
||
_ => 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",
|
||
"Energy Independence", "from your own solar + battery system", "Battery Eff.", "discharge vs charge",
|
||
"months aggregated", "Generated by <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||
"View your detailed reports at monitor.inesco.energy")
|
||
};
|
||
|
||
// ── Aggregated HTML email template ────────────────────────────────────
|
||
|
||
// ── Daily Report HTML ────────────────────────────────────────────
|
||
|
||
public static string BuildDailyHtmlEmail(
|
||
DailyEnergyRecord record, string installationName, string language = "en", string logoSrc = null)
|
||
{
|
||
logoSrc ??= LogoDataUri;
|
||
var s = GetAggregatedStrings(language, "monthly"); // reuse monthly strings for metric labels
|
||
|
||
var dailyTitle = language switch
|
||
{
|
||
"de" => "Täglicher Energiebericht",
|
||
"fr" => "Rapport énergétique quotidien",
|
||
"it" => "Rapporto energetico giornaliero",
|
||
_ => "Daily Energy Report"
|
||
};
|
||
|
||
var selfSufficiency = record.LoadConsumption > 0
|
||
? Math.Max(0, (1 - record.GridImport / record.LoadConsumption)) * 100
|
||
: 0;
|
||
var batteryEfficiency = record.BatteryCharged > 0
|
||
? Math.Min(100, record.BatteryDischarged / record.BatteryCharged * 100)
|
||
: 0;
|
||
var energySaved = Math.Max(0, record.LoadConsumption - record.GridImport);
|
||
var savingsCHF = energySaved * 0.39;
|
||
|
||
return $@"
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head><meta charset=""utf-8""><style>*{{-webkit-print-color-adjust:exact!important;color-adjust:exact!important;print-color-adjust:exact!important}}@page{{margin:10mm;size:A4}}</style></head>
|
||
<body style=""margin:0;padding:0;background:#f4f4f4;font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#333"">
|
||
<table width=""100%"" cellpadding=""0"" cellspacing=""0"" style=""background:#f4f4f4;padding:20px 0"">
|
||
<tr><td align=""center"">
|
||
<table width=""600"" cellpadding=""0"" cellspacing=""0"" style=""background:#ffffff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.08)"">
|
||
|
||
<!-- Header -->
|
||
<tr>
|
||
<td style=""background:#2c3e50;padding:24px 30px;color:#ffffff"">
|
||
<img src=""{logoSrc}"" alt=""inesco energy"" style=""height:36px;margin-bottom:12px"" />
|
||
<div style=""font-size:20px;font-weight:bold"">{dailyTitle}</div>
|
||
<div style=""font-size:14px;margin-top:6px;opacity:0.9"">{installationName}</div>
|
||
<div style=""font-size:13px;margin-top:2px;opacity:0.7"">{record.Date}</div>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- Summary Table -->
|
||
<tr>
|
||
<td style=""padding:24px 30px"">
|
||
<div style=""font-size:16px;font-weight:bold;margin-bottom:12px;color:#2c3e50"">{s.Summary}</div>
|
||
<table width=""100%"" cellpadding=""0"" cellspacing=""0"" style=""border:1px solid #eee;border-radius:4px"">
|
||
<tr style=""background:#f8f9fa"">
|
||
<th style=""padding:8px 12px;text-align:left"">{s.Metric}</th>
|
||
<th style=""padding:8px 12px;text-align:right"">{s.Total}</th>
|
||
</tr>
|
||
<tr><td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.PvProduction}</td><td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{record.PvProduction:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.Consumption}</td><td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{record.LoadConsumption:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.GridImport}</td><td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{record.GridImport:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.GridExport}</td><td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{record.GridExport:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px"">{s.BatteryInOut}</td><td style=""padding:8px 12px;text-align:right;font-weight:bold"">{record.BatteryCharged:F1} / {record.BatteryDischarged:F1} kWh</td></tr>
|
||
</table>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- Key Ratios -->
|
||
<tr>
|
||
<td style=""padding:0 30px 24px"">
|
||
<div style=""font-size:16px;font-weight:bold;margin-bottom:12px;color:#2c3e50"">{s.SavingsHeader}</div>
|
||
<table width=""100%"" cellpadding=""0"" cellspacing=""8"">
|
||
<tr>
|
||
{SavingsBox(s.SolarEnergyUsed, $"{energySaved:F1} kWh", s.StayedAtHome, "#27ae60")}
|
||
{SavingsBox(s.EstMoneySaved, $"~{savingsCHF:F0} CHF", s.AtRate, "#2980b9")}
|
||
{SavingsBox(s.SolarCoverage, $"{selfSufficiency:F0}%", s.FromSolar, "#8e44ad")}
|
||
{SavingsBox(s.BatteryEff, $"{batteryEfficiency:F0}%", s.OutVsIn, "#e67e22")}
|
||
</tr>
|
||
</table>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- Footer -->
|
||
<tr>
|
||
<td style=""background:#f8f9fa;padding:16px 30px;text-align:center;font-size:12px;color:#999;border-top:1px solid #eee"">
|
||
{s.Footer}
|
||
<div style=""margin-top:10px""><a href=""https://monitor.inesco.energy"" style=""color:#999;text-decoration:underline"">{s.FooterLink}</a></div>
|
||
</td>
|
||
</tr>
|
||
|
||
</table>
|
||
</td></tr>
|
||
</table>
|
||
</body>
|
||
</html>";
|
||
}
|
||
|
||
public static string BuildAggregatedHtmlEmail(
|
||
string periodStart, string periodEnd, string installationName,
|
||
double pvProduction, double consumption, double gridImport, double gridExport,
|
||
double batteryCharged, double batteryDischarged, double energySaved, double savingsCHF,
|
||
double selfSufficiency, double batteryEfficiency, string aiInsight,
|
||
string countLabel, AggregatedEmailStrings s, string customerName = null, string logoSrc = null, string source = "email")
|
||
{
|
||
logoSrc ??= LogoDataUri;
|
||
var insightLines = aiInsight
|
||
.Split('\n', StringSplitOptions.RemoveEmptyEntries)
|
||
.Select(l => System.Text.RegularExpressions.Regex.Replace(l.Trim(), @"^[\d]+[.)]\s*|^[-*]\s*", "").Replace("**", ""))
|
||
.Where(l => l.Length > 0)
|
||
.ToList();
|
||
|
||
var insightHtml = insightLines.Count > 1
|
||
? "<ul style=\"margin:0;padding-left:20px\">" +
|
||
string.Join("", insightLines.Select(l => $"<li style=\"margin-bottom:8px;line-height:1.6\">{FormatInsightLine(l)}</li>")) +
|
||
"</ul>"
|
||
: $"<p style=\"margin:0;line-height:1.6\">{FormatInsightLine(aiInsight)}</p>";
|
||
|
||
var footerHtml = source == "web" ? "" : $@"
|
||
<!-- Footer -->
|
||
<tr>
|
||
<td style=""background:#f8f9fa;padding:16px 30px;text-align:center;font-size:12px;color:#999;border-top:1px solid #eee"">
|
||
{s.Footer}
|
||
<div style=""margin-top:10px""><a href=""https://monitor.inesco.energy"" style=""color:#999;text-decoration:underline"">{s.FooterLink}</a></div>
|
||
</td>
|
||
</tr>";
|
||
|
||
var bgColor = source == "web" ? "transparent" : "#f4f4f4";
|
||
|
||
return $@"
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head><meta charset=""utf-8""><style>*{{-webkit-print-color-adjust:exact!important;color-adjust:exact!important;print-color-adjust:exact!important}}@page{{margin:10mm;size:A4}}</style></head>
|
||
<body style=""margin:0;padding:0;background:{bgColor};font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#333"">
|
||
<table width=""100%"" cellpadding=""0"" cellspacing=""0"" style=""background:{bgColor};padding:20px 0"">
|
||
<tr><td align=""center"">
|
||
<table width=""{(source == "web" ? "100%" : "600")}"" cellpadding=""0"" cellspacing=""0"" style=""background:#ffffff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.08){(source == "web" ? ";max-width:900px;width:100%" : "")}"">
|
||
|
||
<!-- Header -->
|
||
<tr>
|
||
<td style=""background:#2c3e50;padding:24px 30px;color:#ffffff"">
|
||
<img src=""{logoSrc}"" alt=""inesco energy"" style=""height:36px;margin-bottom:12px"" />
|
||
<div style=""font-size:20px;font-weight:bold"">{s.Title}</div>
|
||
<div style=""font-size:14px;margin-top:6px;opacity:0.9"">{installationName}</div>
|
||
<div style=""font-size:13px;margin-top:2px;opacity:0.7"">{periodStart} — {periodEnd}</div>
|
||
<div style=""font-size:12px;margin-top:2px;opacity:0.5"">{countLabel}</div>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- Insights -->
|
||
<tr>
|
||
<td style=""padding:24px 30px 0"">
|
||
<div style=""font-size:16px;font-weight:bold;margin-bottom:12px;color:#2c3e50"">{s.Insights}</div>
|
||
<div style=""background:#fef9e7;border-left:4px solid #f39c12;padding:14px 18px;border-radius:0 6px 6px 0;font-size:14px;color:#333"">
|
||
{insightHtml}
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- Summary Table -->
|
||
<tr>
|
||
<td style=""padding:24px 30px"">
|
||
<div style=""font-size:16px;font-weight:bold;margin-bottom:12px;color:#2c3e50"">{s.Summary}</div>
|
||
<table width=""100%"" cellpadding=""0"" cellspacing=""0"" style=""border:1px solid #eee;border-radius:4px"">
|
||
<tr style=""background:#f8f9fa"">
|
||
<th style=""padding:8px 12px;text-align:left"">{s.Metric}</th>
|
||
<th style=""padding:8px 12px;text-align:right"">{s.Total}</th>
|
||
</tr>
|
||
<tr><td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.PvProduction}</td><td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{pvProduction:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.Consumption}</td><td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{consumption:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.GridImport}</td><td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{gridImport:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px;border-bottom:1px solid #eee"">{s.GridExport}</td><td style=""padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold"">{gridExport:F1} kWh</td></tr>
|
||
<tr><td style=""padding:8px 12px"">{s.BatteryInOut}</td><td style=""padding:8px 12px;text-align:right;font-weight:bold"">{batteryCharged:F1} / {batteryDischarged:F1} kWh</td></tr>
|
||
</table>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- Key Ratios -->
|
||
<tr>
|
||
<td style=""padding:0 30px 24px"">
|
||
<div style=""font-size:16px;font-weight:bold;margin-bottom:12px;color:#2c3e50"">{s.SavingsHeader}</div>
|
||
<table width=""100%"" cellpadding=""0"" cellspacing=""8"">
|
||
<tr>
|
||
{SavingsBox(s.SolarEnergyUsed, $"{energySaved:F1} kWh", s.StayedAtHome, "#27ae60")}
|
||
{SavingsBox(s.EstMoneySaved, $"~{savingsCHF:F0} CHF", s.AtRate, "#2980b9")}
|
||
{SavingsBox(s.SolarCoverage, $"{selfSufficiency:F0}%", s.FromSolar, "#8e44ad")}
|
||
{SavingsBox(s.BatteryEff, $"{batteryEfficiency:F0}%", s.OutVsIn, "#e67e22")}
|
||
</tr>
|
||
</table>
|
||
</td>
|
||
</tr>
|
||
|
||
{footerHtml}
|
||
|
||
</table>
|
||
</td></tr>
|
||
</table>
|
||
</body>
|
||
</html>";
|
||
}
|
||
}
|