Compare commits

..

8 Commits

29 changed files with 12775 additions and 510 deletions

View File

@ -44,6 +44,12 @@
<None Update="Resources/s3cmd.py">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources/AlarmTranslations.*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources/AlarmNames.de.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update=".env">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@ -827,28 +827,28 @@ public class Controller : ControllerBase
/// Usage: GET /api/TestDiagnoseError?errorDescription=SomeAlarm
/// </summary>
[HttpGet(nameof(TestDiagnoseError))]
public async Task<ActionResult> TestDiagnoseError(string errorDescription = "AbnormalGridVoltage")
public async Task<ActionResult> TestDiagnoseError(string errorDescription = "AbnormalGridVoltage", string language = "en")
{
// 1. Try knowledge base first
var kbResult = AlarmKnowledgeBase.TryGetDiagnosis(errorDescription);
if (kbResult is not null)
// 1. Try static lookup (KB for English, pre-generated translations for others)
var staticResult = DiagnosticService.TryGetTranslation(errorDescription, language);
if (staticResult is not null)
{
return Ok(new
{
Source = "KnowledgeBase",
Alarm = errorDescription,
Source = "KnowledgeBase",
Alarm = errorDescription,
MistralEnabled = DiagnosticService.IsEnabled,
kbResult.Explanation,
kbResult.Causes,
kbResult.NextSteps
staticResult.Explanation,
staticResult.Causes,
staticResult.NextSteps
});
}
// 2. If not in KB, try Mistral directly with a test prompt
// 2. If not found, try Mistral with the correct language
if (!DiagnosticService.IsEnabled)
return Ok(new { Source = "None", Alarm = errorDescription, Message = "Not in knowledge base and Mistral API key not configured." });
var aiResult = await DiagnosticService.TestCallMistralAsync(errorDescription);
var aiResult = await DiagnosticService.TestCallMistralAsync(errorDescription, language);
if (aiResult is null)
return Ok(new { Source = "MistralFailed", Alarm = errorDescription, Message = "Mistral API call failed or returned empty." });
@ -1255,6 +1255,104 @@ public class Controller : ControllerBase
return Redirect($"https://monitor.inesco.energy/?username={user.Email}&reset=true"); // TODO: move to settings file
}
// ── Alarm Review Campaign ────────────────────────────────────────────────
[HttpPost(nameof(SendTestAlarmReview))]
public async Task<ActionResult> SendTestAlarmReview()
{
await AlarmReviewService.SendTestBatchAsync();
return Ok(new { message = "Test review email sent to liu@inesco.energy. Check your inbox." });
}
[HttpPost(nameof(StartAlarmReviewCampaign))]
public ActionResult StartAlarmReviewCampaign()
{
AlarmReviewService.StartCampaign();
return Ok(new { message = "Alarm review campaign started." });
}
[HttpPost(nameof(StopAlarmReviewCampaign))]
public ActionResult StopAlarmReviewCampaign()
{
AlarmReviewService.StopCampaign();
return Ok(new { message = "Campaign paused — progress preserved. Use ResumeAlarmReviewCampaign to restart timers." });
}
[HttpPost(nameof(ResumeAlarmReviewCampaign))]
public ActionResult ResumeAlarmReviewCampaign()
{
AlarmReviewService.ResumeCampaign();
return Ok(new { message = "Campaign resumed — timers restarted from existing progress." });
}
[HttpPost(nameof(ResetAlarmReviewCampaign))]
public ActionResult ResetAlarmReviewCampaign()
{
AlarmReviewService.ResetCampaign();
return Ok(new { message = "Campaign fully reset — all progress deleted. Use StartAlarmReviewCampaign to begin again." });
}
[HttpGet(nameof(CorrectAlarm))]
public ActionResult CorrectAlarm(int batch, string key)
{
var html = AlarmReviewService.GetCorrectionPage(batch, key);
return Content(html, "text/html");
}
[HttpPost(nameof(ApplyAlarmCorrection))]
public ActionResult ApplyAlarmCorrection([FromBody] AlarmCorrectionRequest req)
{
if (req == null) return BadRequest();
var correction = new DiagnosticResponse
{
Explanation = req.Explanation ?? "",
Causes = req.Causes ?? new List<string>(),
NextSteps = req.NextSteps ?? new List<string>(),
};
var ok = AlarmReviewService.ApplyCorrection(req.BatchNumber, req.AlarmKey ?? "", correction);
return ok ? Ok(new { message = "Korrektur gespeichert." }) : BadRequest("Batch or alarm not found.");
}
[HttpGet(nameof(ReviewAlarms))]
public ActionResult ReviewAlarms(int batch, string reviewer)
{
var html = AlarmReviewService.GetReviewPage(batch, reviewer);
if (html is null) return NotFound("Batch not found or reviewer not recognised.");
return Content(html, "text/html");
}
[HttpPost(nameof(SubmitAlarmReview))]
public async Task<ActionResult> SubmitAlarmReview(int batch, string? reviewer, [FromBody] List<ReviewFeedback>? feedbacks)
{
// Batch 0 = test mode — run dry-run synthesis and return preview HTML (nothing is saved)
if (batch == 0)
{
var previewHtml = await AlarmReviewService.PreviewSynthesisAsync(feedbacks);
return Ok(new { preview = previewHtml });
}
var ok = AlarmReviewService.SubmitFeedback(batch, reviewer, feedbacks);
return ok ? Ok(new { message = "Feedback saved. Thank you!" })
: BadRequest("Batch not found, reviewer not recognised, or already submitted.");
}
[HttpGet(nameof(GetAlarmReviewStatus))]
public ActionResult GetAlarmReviewStatus()
{
return Ok(AlarmReviewService.GetStatus());
}
[HttpGet(nameof(DownloadCheckedKnowledgeBase))]
public ActionResult DownloadCheckedKnowledgeBase()
{
var content = AlarmReviewService.GetCheckedFileContent();
if (content is null) return NotFound("AlarmKnowledgeBaseChecked.cs has not been generated yet.");
return File(System.Text.Encoding.UTF8.GetBytes(content),
"text/plain",
"AlarmKnowledgeBaseChecked.cs");
}
}

View File

@ -0,0 +1,28 @@
namespace InnovEnergy.App.Backend.DataTypes;
/// <summary>
/// Pre-computed behavioral facts derived from hourly data.
/// All heavy analysis is done in C# — the AI only gets these clean conclusions.
/// </summary>
public class BehavioralPattern
{
// Peak hours
public int PeakLoadHour { get; set; } // 0-23, hour of day with highest avg load
public int PeakSolarHour { get; set; } // 0-23, hour with highest avg PV output
public int PeakSolarEndHour { get; set; } // last hour of meaningful solar window
public int HighestGridImportHour { get; set; } // 0-23, hour with most avg grid import
// kWh figures
public double AvgPeakLoadKwh { get; set; } // avg load at peak hour (per day)
public double AvgPeakSolarKwh { get; set; } // avg PV at peak solar hour (per day)
public double AvoidableGridKwh { get; set; } // grid import during hours solar was active
public double AvgGridImportAtPeakHour { get; set; } // avg grid import at worst hour
// Weekday vs weekend
public double WeekdayAvgDailyLoad { get; set; } // avg kWh/day Mon-Fri
public double WeekendAvgDailyLoad { get; set; } // avg kWh/day Sat-Sun
// Battery
public int AvgBatteryDepletedHour { get; set; } // avg hour when SoC first drops below 20%
public bool BatteryDepletesOvernight { get; set; } // true if battery regularly hits low SoC at night
}

View File

@ -0,0 +1,19 @@
namespace InnovEnergy.App.Backend.DataTypes;
public class HourlyEnergyData
{
public DateTime DateTime { get; set; } // e.g. 2026-02-14 08:00:00
public int Hour { get; set; } // 0-23
public string DayOfWeek { get; set; } = ""; // "Monday" etc.
public bool IsWeekend { get; set; }
// Energy for this hour (kWh) — derived from diff of consecutive "Today" cumulative snapshots
public double PvKwh { get; set; }
public double LoadKwh { get; set; }
public double GridImportKwh { get; set; }
public double BatteryChargedKwh { get; set; }
public double BatteryDischargedKwh { get; set; }
// Instantaneous state at snapshot time
public double BattSoC { get; set; } // % (0-100)
}

View File

@ -9,6 +9,11 @@ public class WeeklyReportResponse
public WeeklySummary CurrentWeek { get; set; } = new();
public WeeklySummary? PreviousWeek { get; set; }
// Pre-computed savings — single source of truth for UI and AI
public double TotalEnergySaved { get; set; } // kWh = Consumption - GridImport
public double TotalSavingsCHF { get; set; } // CHF = TotalEnergySaved * 0.27
public double DaysEquivalent { get; set; } // TotalEnergySaved / avg daily consumption
// Key ratios (current week)
public double SelfSufficiencyPercent { get; set; }
public double SelfConsumptionPercent { get; set; }
@ -20,7 +25,8 @@ public class WeeklyReportResponse
public double ConsumptionChangePercent { get; set; }
public double GridImportChangePercent { get; set; }
public List<DailyEnergyData> DailyData { get; set; } = new();
public List<DailyEnergyData> DailyData { get; set; } = new();
public BehavioralPattern? Behavior { get; set; }
public string AiInsight { get; set; } = "";
}

View File

@ -27,6 +27,7 @@ public static class Program
Db.Init();
LoadEnvFile();
DiagnosticService.Initialize();
AlarmReviewService.StartDailyScheduler();
var builder = WebApplication.CreateBuilder(args);
RabbitMqManager.InitializeEnvironment();

View File

@ -0,0 +1,231 @@
{
"AbnormalGridVoltage": "Unnormale Netzspannung",
"AbnormalGridFrequency": "Unnormale Netzfrequenz",
"InvertedSequenceOfGridVoltage": "Falsche Phasenreihenfolge",
"GridVoltagePhaseLoss": "Phasenausfall im Netz",
"AbnormalGridCurrent": "Unnormaler Netzstrom",
"AbnormalOutputVoltage": "Ungewöhnliche Ausgangsspannung",
"AbnormalOutputFrequency": "Ungewöhnliche Ausgangsfrequenz",
"AbnormalNullLine": "Fehlerhafter Nullleiter",
"AbnormalOffGridOutputVoltage": "Ungewöhnliche Backup-Spannung",
"ExcessivelyHighAmbientTemperature": "Zu hohe Umgebungstemperatur",
"ExcessiveRadiatorTemperature": "Überhitzter Kühlkörper",
"PcbOvertemperature": "Überhitzte Leiterplatte",
"DcConverterOvertemperature": "Überhitzter DC-Wandler",
"InverterOvertemperatureAlarm": "Warnung: Überhitzung",
"InverterOvertemperature": "Wechselrichter überhitzt",
"DcConverterOvertemperatureAlarm": "Übertemperaturalarm DC-Wandler",
"InsulationFault": "Isolationsfehler",
"LeakageProtectionFault": "Leckschutzfehler",
"AbnormalLeakageSelfCheck": "Anomaler Leckstrom-Selbsttest",
"PoorGrounding": "Schlechte Erdung",
"FanFault": "Lüfterfehler",
"AuxiliaryPowerFault": "Hilfsstromversorgung Fehler",
"ModelCapacityFault": "Modellkapazitätsfehler",
"AbnormalLightningArrester": "Überspannungsschutz Fehler",
"IslandProtection": "Inselbetrieb Schutz",
"Battery1NotConnected": "Batterie 1 nicht verbunden",
"Battery1Overvoltage": "Batterie 1 Überspannung",
"Battery1Undervoltage": "Batterie 1 Unterspannung",
"Battery1DischargeEnd": "Batterie 1 Entladung beendet",
"Battery1Inverted": "Batterie 1 Polarität vertauscht",
"Battery1OverloadTimeout": "Batterie 1 Überlastung",
"Battery1SoftStartFailure": "Batterie 1 Startfehler",
"Battery1PowerTubeFault": "Batterie 1 Leistungsteil defekt",
"Battery1InsufficientPower": "Batterie 1 Leistung unzureichend",
"Battery1BackupProhibited": "Batterie 1 Backup gesperrt",
"Battery2NotConnected": "Batterie 2 nicht verbunden",
"Battery2Overvoltage": "Batterie 2 Überspannung",
"Battery2Undervoltage": "Batterie 2 Unterspannung",
"Battery2DischargeEnd": "Batterie 2 Entladung beendet",
"Battery2Inverted": "Batterie 2 falsch angeschlossen",
"Battery2OverloadTimeout": "Batterie 2 Überlastung",
"Battery2SoftStartFailure": "Batterie 2 Startfehler",
"Battery2PowerTubeFault": "Batterie 2 Leistungsteil defekt",
"Battery2InsufficientPower": "Batterie 2 Leistung unzureichend",
"Battery2BackupProhibited": "Batterie 2 Backup gesperrt",
"LithiumBattery1ChargeForbidden": "Lithium-Batterie 1 Ladeverbot",
"LithiumBattery1DischargeForbidden": "Lithium-Batterie 1 Entladeverbot",
"LithiumBattery2ChargeForbidden": "Lithium-Batterie 2 Ladeverbot",
"LithiumBattery2DischargeForbidden": "Lithium-Batterie 2 Entladeverbot",
"LithiumBattery1Full": "Lithium-Batterie 1 voll",
"LithiumBattery1DischargeEnd": "Lithium-Batterie 1 entladen",
"LithiumBattery2Full": "Lithium-Batterie 2 voll",
"LithiumBattery2DischargeEnd": "Lithium-Batterie 2 entladen",
"LeadBatteryTemperatureAbnormality": "Batterietemperatur abnormal",
"BatteryAccessMethodError": "Batteriezugriffsfehler",
"Pv1NotAccessed": "PV1 nicht erreichbar",
"Pv1Overvoltage": "PV1 Überspannung",
"AbnormalPv1CurrentSharing": "Ungleichmäßiger PV1-Strom",
"Pv1PowerTubeFault": "PV1 Leistungstubus defekt",
"Pv1SoftStartFailure": "PV1 Soft-Start fehlgeschlagen",
"Pv1OverloadTimeout": "PV1-Überlastung",
"Pv1InsufficientPower": "PV1-Schwacher Strom",
"Photovoltaic1Overcurrent": "PV1-Überstrom",
"Pv2NotAccessed": "PV2-Nicht erkannt",
"Pv2Overvoltage": "PV2-Überspannung",
"AbnormalPv2CurrentSharing": "Ungewöhnliche Stromverteilung PV2",
"Pv2PowerTubeFault": "PV2-Leistungsrohrfehler",
"Pv2SoftStartFailure": "PV2-Softstart fehlgeschlagen",
"Pv2OverloadTimeout": "PV2-Überlastung Timeout",
"Pv2InsufficientPower": "Unzureichende Leistung PV2",
"Pv3NotConnected": "PV3 nicht verbunden",
"Pv3Overvoltage": "PV3 Überspannung",
"Pv3AverageCurrentAnomaly": "PV3 Stromanomalie",
"Pv3PowerTubeFailure": "PV3 Leistungselektronik defekt",
"Pv3SoftStartFailure": "PV3 Startfehler",
"Pv3OverloadTimeout": "PV3-Überlastung",
"Pv3ReverseConnection": "PV3-Falschpolung",
"Pv4NotConnected": "PV4 Nicht Verbunden",
"Pv4Overvoltage": "PV4 Überspannung",
"Pv4AverageCurrentAnomaly": "PV4 Stromanomalie",
"Pv4PowerTubeFailure": "PV4-Leistungsrohr defekt",
"Pv4SoftStartFailure": "PV4-Softstart fehlgeschlagen",
"Pv4OverloadTimeout": "PV4-Überlastung",
"Pv4ReverseConnection": "PV4 falsch angeschlossen",
"InsufficientPhotovoltaicPower": "Zu wenig Solarstrom",
"DcBusOvervoltage": "DC-Bus Überspannung",
"DcBusUndervoltage": "DC-Bus Unterspannung",
"DcBusVoltageUnbalance": "DC-Bus Spannungsungleichgewicht",
"BusSlowOvervoltage": "Langsame DC-Bus Überspannung",
"HardwareBusOvervoltage": "Hardware DC-Bus Überspannung",
"BusSoftStartFailure": "Fehler beim sanften Start",
"InverterPowerTubeFault": "Wechselrichter-Leistungshalbleiter defekt",
"HardwareOvercurrent": "Hardware-Überstrom",
"DcConverterOvervoltage": "DC-Wandler Überspannung",
"DcConverterHardwareOvervoltage": "DC-Wandler Hardware-Überspannung",
"DcConverterOvercurrent": "DC-Wandler Überstrom",
"DcConverterHardwareOvercurrent": "DC-Wandler Hardware-Überstrom",
"DcConverterResonatorOvercurrent": "DC-Wandler Resonanz-Überstrom",
"SystemOutputOverload": "Systemausgang überlastet",
"InverterOverload": "Wechselrichter überlastet",
"InverterOverloadTimeout": "Wechselrichter-Überlastung",
"LoadPowerOverload": "Überlastung der Lastleistung",
"BalancedCircuitOverloadTimeout": "Phasenausgleich-Überlastung",
"InverterSoftStartFailure": "Wechselrichter-Softstart-Fehler",
"Dsp1ParameterSettingFault": "DSP-Parameter-Fehler",
"Dsp2ParameterSettingFault": "DSP2 Parameterfehler",
"DspVersionCompatibilityFault": "DSP-Versionen nicht kompatibel",
"CpldVersionCompatibilityFault": "CPLD-Version nicht kompatibel",
"CpldCommunicationFault": "CPLD-Kommunikationsfehler",
"DspCommunicationFault": "DSP-Kommunikationsfehler",
"OutputVoltageDcOverlimit": "DC-Spannung zu hoch",
"OutputCurrentDcOverlimit": "DC-Strom zu hoch",
"RelaySelfCheckFails": "Relais-Selbsttest fehlgeschlagen",
"InverterRelayOpen": "Wechselrichter-Relais offen",
"InverterRelayShortCircuit": "Wechselrichter-Relais Kurzschluss",
"OpenCircuitOfPowerGridRelay": "Netzrelais offen",
"ShortCircuitOfPowerGridRelay": "Netzrelais kurzgeschlossen",
"GeneratorRelayOpenCircuit": "Generatorrelais offen",
"GeneratorRelayShortCircuit": "Generatorrelais kurzgeschlossen",
"AbnormalInverter": "Wechselrichter abnormal",
"ParallelCommunicationAlarm": "Parallelkommunikationsalarm",
"ParallelModuleMissing": "Parallelmodul fehlt",
"DuplicateMachineNumbersForParallelModules": "Doppelte Gerätenummern",
"ParameterConflictInParallelModule": "Parameterkonflikt im Parallelmodul",
"SystemDerating": "Systemleistung reduziert",
"PvAccessMethodErrorAlarm": "PV-Zugriffsfehler",
"ReservedAlarms4": "Reservierter Alarm 4",
"ReservedAlarms5": "Reservierter Alarm 5",
"ReverseMeterConnection": "Zähler falsch angeschlossen",
"InverterSealPulse": "Wechselrichter-Leistungsbegrenzung",
"AbnormalDieselGeneratorVoltage": "Ungewöhnliche Dieselgenerator-Spannung",
"AbnormalDieselGeneratorFrequency": "Ungewöhnliche Dieselgenerator-Frequenz",
"DieselGeneratorVoltageReverseSequence": "Falsche Phasenfolge des Generators",
"DieselGeneratorVoltageOutOfPhase": "Generator nicht synchronisiert",
"GeneratorOverload": "Generator überlastet",
"StringFault": "PV-String-Fehler",
"PvStringPidQuickConnectAbnormal": "PV-String-Anschluss defekt",
"DcSpdFunctionAbnormal": "DC-Überspannungsschutz defekt",
"PvShortCircuited": "PV-String kurzgeschlossen",
"PvBoostDriverAbnormal": "PV-Boost-Treiber defekt",
"AcSpdFunctionAbnormal": "AC-Überspannungsschutz defekt",
"DcFuseBlown": "DC-Sicherung durchgebrannt",
"DcInputVoltageTooHigh": "DC-Eingangsspannung zu hoch",
"PvReversed": "PV-Polarität vertauscht",
"PidFunctionAbnormal": "PID-Schutzfunktion gestört",
"PvStringDisconnected": "PV-String getrennt",
"PvStringCurrentUnbalanced": "PV-String Strom unausgeglichen",
"NoUtilityGrid": "Kein Stromnetz",
"GridVoltageOutOfRange": "Netzspannung außerhalb des Bereichs",
"GridFrequencyOutOfRange": "Netzfrequenz außerhalb des Bereichs",
"Overload": "Überlastung",
"MeterDisconnected": "Stromzähler getrennt",
"MeterReverselyConnected": "Zähler falsch angeschlossen",
"LinePeVoltageAbnormal": "Abnormale PE-Spannung",
"PhaseSequenceError": "Phasenfolgefehler",
"FanFailure": "Lüfterausfall",
"MeterAbnormal": "Störungsanzeige Zähler",
"OptimizerCommunicationAbnormal": "Kommunikationsstörung Optimierer",
"OverTemperature": "Überhitzung",
"OverTemperatureAlarm": "Überhitzungswarnung",
"NtcTemperatureSensorBroken": "Temperatursensor defekt",
"SyncSignalAbnormal": "Synchronisationsfehler",
"GridStartupConditionsNotMet": "Netzstartbedingungen nicht erfüllt",
"BatteryCommunicationFailure": "Batteriekommunikation fehlgeschlagen",
"BatteryDisconnected": "Batterie getrennt",
"BatteryVoltageTooHigh": "Batteriespannung zu hoch",
"BatteryVoltageTooLow": "Batteriespannung zu niedrig",
"BatteryReverseConnected": "Batterie falsch angeschlossen",
"LeadAcidTempSensorDisconnected": "Temperatursensor nicht angeschlossen",
"BatteryTemperatureOutOfRange": "Batterietemperatur außerhalb des Bereichs",
"BmsFault": "BMS-Fehler",
"LithiumBatteryOverload": "Batterie-Überlastung",
"BmsCommunicationAbnormal": "BMS-Kommunikationsfehler",
"BatterySpdAbnormal": "Batterie-Überspannungsschutz",
"OutputDcComponentBiasAbnormal": "DC-Versatz im Ausgang",
"DcComponentOverHighOutputVoltage": "DC-Komponente zu hohe Ausgangsspannung",
"OffGridOutputVoltageTooLow": "Netzunabhängige Ausgangsspannung zu niedrig",
"OffGridOutputVoltageTooHigh": "Netzunabhängige Ausgangsspannung zu hoch",
"OffGridOutputOverCurrent": "Netzunabhängiger Ausgangsüberstrom",
"OffGridOutputOverload": "Netzunabhängiger Ausgang überlastet",
"BalancedCircuitAbnormal": "Phasenausgleich gestört",
"ExportLimitationFailSafe": "Exportbegrenzung Notaus",
"DcBiasAbnormal": "DC-Vorspannung abnormal",
"HighDcComponentOutputCurrent": "Hohe DC-Komponente im Ausgangsstrom",
"BusVoltageSamplingAbnormal": "Spannungsmessung defekt",
"RelayFault": "Relaisfehler",
"BusVoltageAbnormal": "Gleichspannung abnormal",
"InternalCommunicationFailure": "Interne Kommunikation ausgefallen",
"TemperatureSensorDisconnected": "Temperatursensor getrennt",
"IgbtDriveFault": "IGBT-Ansteuerungsfehler",
"EepromError": "EEPROM-Fehler",
"AuxiliaryPowerAbnormal": "Hilfsstromversorgung abnormal",
"DcAcOvercurrentProtection": "Überstromschutz aktiviert",
"CommunicationProtocolMismatch": "Kommunikationsprotokoll-Fehler",
"DspComFirmwareMismatch": "Firmware-Inkompatibilität DSP/COM",
"DspSoftwareHardwareMismatch": "DSP-Software-Hardware-Inkompatibilität",
"CpldAbnormal": "CPLD-Fehler",
"RedundancySamplingInconsistent": "Inkonsistente redundante Messungen",
"PwmPassThroughSignalFailure": "PWM-Signalweg ausgefallen",
"AfciSelfTestFailure": "AFCI-Selbsttest fehlgeschlagen",
"PvCurrentSamplingAbnormal": "PV-Strommessung abnormal",
"AcCurrentSamplingAbnormal": "AC-Strommessung abnormal",
"BusSoftbootFailure": "DC-Bus-Vorstart fehlgeschlagen",
"EpoFault": "EPO-Fehler (Notaus)",
"MonitoringChipBootVerificationFailed": "Überwachungs-Chip Startfehler",
"BmsCommunicationFailure": "BMS-Kommunikationsfehler",
"BmsChargeDischargeFailure": "BMS-Lade-/Entladefehler",
"BatteryVoltageLow": "Batteriespannung zu niedrig",
"BatteryVoltageHigh": "Batteriespannung zu hoch",
"BatteryTemperatureAbnormal": "Batterietemperatur ungewöhnlich",
"BatteryReversed": "Batterie verkehrt herum",
"BatteryOpenCircuit": "Batteriekreis offen",
"BatteryOverloadProtection": "Batterieüberlastungsschutz",
"Bus2VoltageAbnormal": "Bus2-Spannung ungewöhnlich",
"BatteryChargeOcp": "Batterieladung Überstrom",
"BatteryDischargeOcp": "Batterieentladung Überstrom",
"BatterySoftStartFailed": "Batterie-Softstart fehlgeschlagen",
"EpsOutputShortCircuited": "EPS-Ausgang kurzgeschlossen",
"OffGridBusVoltageLow": "Netzunabhängige Busspannung zu niedrig",
"OffGridTerminalVoltageAbnormal": "Abnormale Spannung am Netzausgang",
"SoftStartFailed": "Sanfter Start fehlgeschlagen",
"OffGridOutputVoltageAbnormal": "Abnormale Ausgangsspannung im Netzmodus",
"BalancedCircuitSelfTestFailed": "Ausgleichsschaltungstest fehlgeschlagen",
"HighDcComponentOutputVoltage": "Hohe Gleichspannungskomponente im Ausgang",
"OffGridParallelSignalAbnormal": "Parallelsignalstörung",
"AFCIFault": "Lichtbogenfehler",
"GFCIHigh": "Erhöhter Fehlerstrom",
"PVVoltageHigh": "PV-Spannung zu hoch",
"OffGridBusVoltageTooLow": "Off-Grid-Busspannung zu niedrig"
}

View File

@ -0,0 +1,231 @@
{
"alarm_AbnormalGridVoltage": "Tension réseau anormale",
"alarm_AbnormalGridFrequency": "Fréquence réseau anormale",
"alarm_InvertedSequenceOfGridVoltage": "Séquence de tension inversée",
"alarm_GridVoltagePhaseLoss": "Perte de phase réseau",
"alarm_AbnormalGridCurrent": "Courant réseau anormal",
"alarm_AbnormalOutputVoltage": "Tension de sortie anormale",
"alarm_AbnormalOutputFrequency": "Fréquence de sortie anormale",
"alarm_AbnormalNullLine": "Ligne neutre anormale",
"alarm_AbnormalOffGridOutputVoltage": "Tension de sortie hors réseau anormale",
"alarm_ExcessivelyHighAmbientTemperature": "Température ambiante trop élevée",
"alarm_ExcessiveRadiatorTemperature": "Température excessive du radiateur",
"alarm_PcbOvertemperature": "Température excessive PCB",
"alarm_DcConverterOvertemperature": "Température excessive convertisseur DC",
"alarm_InverterOvertemperatureAlarm": "Alarme température onduleur",
"alarm_InverterOvertemperature": "Température onduleur excessive",
"alarm_DcConverterOvertemperatureAlarm": "Alarme surchauffe convertisseur DC",
"alarm_InsulationFault": "Défaut d'isolation",
"alarm_LeakageProtectionFault": "Défaut protection fuite",
"alarm_AbnormalLeakageSelfCheck": "Auto-test fuite anormale",
"alarm_PoorGrounding": "Mise à la terre insuffisante",
"alarm_FanFault": "Défaut du ventilateur",
"alarm_AuxiliaryPowerFault": "Défaut d'alimentation auxiliaire",
"alarm_ModelCapacityFault": "Défaut de configuration",
"alarm_AbnormalLightningArrester": "Paratonnerre défectueux",
"alarm_IslandProtection": "Protection d'îlotage",
"alarm_Battery1NotConnected": "Batterie 1 non connectée",
"alarm_Battery1Overvoltage": "Tension batterie 1 trop élevée",
"alarm_Battery1Undervoltage": "Tension batterie 1 trop basse",
"alarm_Battery1DischargeEnd": "Fin de décharge batterie 1",
"alarm_Battery1Inverted": "Polarité batterie 1 inversée",
"alarm_Battery1OverloadTimeout": "Dépassement de charge Batterie 1",
"alarm_Battery1SoftStartFailure": "Échec démarrage Batterie 1",
"alarm_Battery1PowerTubeFault": "Défaut électronique Batterie 1",
"alarm_Battery1InsufficientPower": "Puissance insuffisante Batterie 1",
"alarm_Battery1BackupProhibited": "Sauvegarde interdite Batterie 1",
"alarm_Battery2NotConnected": "Batterie 2 non connectée",
"alarm_Battery2Overvoltage": "Tension batterie 2 élevée",
"alarm_Battery2Undervoltage": "Tension batterie 2 basse",
"alarm_Battery2DischargeEnd": "Fin décharge batterie 2",
"alarm_Battery2Inverted": "Polarité batterie 2 inversée",
"alarm_Battery2OverloadTimeout": "Dépassement de charge Batterie 2",
"alarm_Battery2SoftStartFailure": "Échec démarrage Batterie 2",
"alarm_Battery2PowerTubeFault": "Défaut électronique Batterie 2",
"alarm_Battery2InsufficientPower": "Puissance insuffisante Batterie 2",
"alarm_Battery2BackupProhibited": "Sauvegarde interdite Batterie 2",
"alarm_LithiumBattery1ChargeForbidden": "Charge batterie lithium 1 interdite",
"alarm_LithiumBattery1DischargeForbidden": "Décharge batterie lithium 1 interdite",
"alarm_LithiumBattery2ChargeForbidden": "Charge batterie lithium 2 interdite",
"alarm_LithiumBattery2DischargeForbidden": "Décharge batterie lithium 2 interdite",
"alarm_LithiumBattery1Full": "Batterie lithium 1 pleine",
"alarm_LithiumBattery1DischargeEnd": "Fin de décharge batterie lithium 1",
"alarm_LithiumBattery2Full": "Batterie lithium 2 pleine",
"alarm_LithiumBattery2DischargeEnd": "Fin de décharge batterie lithium 2",
"alarm_LeadBatteryTemperatureAbnormality": "Température anormale batterie plomb",
"alarm_BatteryAccessMethodError": "Erreur de méthode d'accès batterie",
"alarm_Pv1NotAccessed": "Chaîne PV1 non accessible",
"alarm_Pv1Overvoltage": "Survoltage PV1",
"alarm_AbnormalPv1CurrentSharing": "Partage de courant PV1 anormal",
"alarm_Pv1PowerTubeFault": "Défaut du tube de puissance PV1",
"alarm_Pv1SoftStartFailure": "Échec de démarrage doux PV1",
"alarm_Pv1OverloadTimeout": "Dépassement de charge PV1",
"alarm_Pv1InsufficientPower": "Puissance PV1 insuffisante",
"alarm_Photovoltaic1Overcurrent": "Surintensité PV1",
"alarm_Pv2NotAccessed": "Chaîne PV2 inaccessible",
"alarm_Pv2Overvoltage": "Survoltage PV2",
"alarm_AbnormalPv2CurrentSharing": "Partage de courant anormal PV2",
"alarm_Pv2PowerTubeFault": "Défaillance du tube de puissance PV2",
"alarm_Pv2SoftStartFailure": "Échec de démarrage progressif PV2",
"alarm_Pv2OverloadTimeout": "Dépassement de charge PV2",
"alarm_Pv2InsufficientPower": "Puissance insuffisante PV2",
"alarm_Pv3NotConnected": "PV3 non connecté",
"alarm_Pv3Overvoltage": "Survoltage PV3",
"alarm_Pv3AverageCurrentAnomaly": "Anomalie courant PV3",
"alarm_Pv3PowerTubeFailure": "Défaillance tube PV3",
"alarm_Pv3SoftStartFailure": "Échec démarrage PV3",
"alarm_Pv3OverloadTimeout": "Dépassement de charge PV3",
"alarm_Pv3ReverseConnection": "Connexion inversée PV3",
"alarm_Pv4NotConnected": "Chaîne PV4 non connectée",
"alarm_Pv4Overvoltage": "Survoltage PV4",
"alarm_Pv4AverageCurrentAnomaly": "Anomalie de courant PV4",
"alarm_Pv4PowerTubeFailure": "Défaillance du tube de puissance PV4",
"alarm_Pv4SoftStartFailure": "Échec du démarrage progressif PV4",
"alarm_Pv4OverloadTimeout": "Dépassement de charge PV4",
"alarm_Pv4ReverseConnection": "Connexion inversée PV4",
"alarm_InsufficientPhotovoltaicPower": "Puissance photovoltaïque insuffisante",
"alarm_DcBusOvervoltage": "Tension DC trop élevée",
"alarm_DcBusUndervoltage": "Tension DC trop basse",
"alarm_DcBusVoltageUnbalance": "Déséquilibre tension DC",
"alarm_BusSlowOvervoltage": "Tension DC lente excessive",
"alarm_HardwareBusOvervoltage": "Tension DC critique",
"alarm_BusSoftStartFailure": "Échec démarrage progressif",
"alarm_InverterPowerTubeFault": "Défaut tube de puissance",
"alarm_HardwareOvercurrent": "Surintensité matérielle",
"alarm_DcConverterOvervoltage": "Survoltage convertisseur DC",
"alarm_DcConverterHardwareOvervoltage": "Survoltage matériel convertisseur DC",
"alarm_DcConverterOvercurrent": "Surintensité convertisseur CC",
"alarm_DcConverterHardwareOvercurrent": "Surintensité matérielle convertisseur CC",
"alarm_DcConverterResonatorOvercurrent": "Surintensité résonateur convertisseur CC",
"alarm_SystemOutputOverload": "Surcharge de sortie système",
"alarm_InverterOverload": "Surcharge onduleur",
"alarm_InverterOverloadTimeout": "Dépassement de charge de l'onduleur",
"alarm_LoadPowerOverload": "Surcharge de puissance de charge",
"alarm_BalancedCircuitOverloadTimeout": "Dépassement de charge du circuit équilibré",
"alarm_InverterSoftStartFailure": "Échec de démarrage progressif de l'onduleur",
"alarm_Dsp1ParameterSettingFault": "Défaillance de paramétrage DSP 1",
"alarm_Dsp2ParameterSettingFault": "Paramètre DSP2 incorrect",
"alarm_DspVersionCompatibilityFault": "Incompatibilité version DSP",
"alarm_CpldVersionCompatibilityFault": "Incompatibilité version CPLD",
"alarm_CpldCommunicationFault": "Échec communication CPLD",
"alarm_DspCommunicationFault": "Échec communication DSP",
"alarm_OutputVoltageDcOverlimit": "Tension de sortie DC excessive",
"alarm_OutputCurrentDcOverlimit": "Courant de sortie DC excessif",
"alarm_RelaySelfCheckFails": "Auto-test relais échoué",
"alarm_InverterRelayOpen": "Relais de l'onduleur ouvert",
"alarm_InverterRelayShortCircuit": "Relais de l'onduleur en court-circuit",
"alarm_OpenCircuitOfPowerGridRelay": "Relais du réseau ouvert",
"alarm_ShortCircuitOfPowerGridRelay": "Court-circuit du relais réseau",
"alarm_GeneratorRelayOpenCircuit": "Relais du générateur ouvert",
"alarm_GeneratorRelayShortCircuit": "Court-circuit du relais générateur",
"alarm_AbnormalInverter": "Onduleur anormal",
"alarm_ParallelCommunicationAlarm": "Alarme de communication parallèle",
"alarm_ParallelModuleMissing": "Module parallèle manquant",
"alarm_DuplicateMachineNumbersForParallelModules": "Numéros de machine en double",
"alarm_ParameterConflictInParallelModule": "Conflit de paramètres parallèle",
"alarm_SystemDerating": "Réduction de puissance du système",
"alarm_PvAccessMethodErrorAlarm": "Erreur méthode d'accès PV",
"alarm_ReservedAlarms4": "Alarme réservée 4",
"alarm_ReservedAlarms5": "Alarme réservée 5",
"alarm_ReverseMeterConnection": "Connexion du compteur inversée",
"alarm_InverterSealPulse": "Impulsion de scellement de l'onduleur",
"alarm_AbnormalDieselGeneratorVoltage": "Tension anormale du générateur diesel",
"alarm_AbnormalDieselGeneratorFrequency": "Fréquence anormale du générateur diesel",
"alarm_DieselGeneratorVoltageReverseSequence": "Séquence de phase inversée du générateur",
"alarm_DieselGeneratorVoltageOutOfPhase": "Déphasage du générateur",
"alarm_GeneratorOverload": "Surcharge du générateur",
"alarm_StringFault": "Défaut de chaîne",
"alarm_PvStringPidQuickConnectAbnormal": "Connexion rapide anormale",
"alarm_DcSpdFunctionAbnormal": "Problème de protection DC",
"alarm_PvShortCircuited": "Court-circuit PV",
"alarm_PvBoostDriverAbnormal": "Problème de convertisseur",
"alarm_AcSpdFunctionAbnormal": "Problème de protection contre les surtensions AC",
"alarm_DcFuseBlown": "Fusible DC grillé",
"alarm_DcInputVoltageTooHigh": "Tension DC d'entrée trop élevée",
"alarm_PvReversed": "Polarité PV inversée",
"alarm_PidFunctionAbnormal": "Problème de fonction PID",
"alarm_PvStringDisconnected": "Chaîne PV déconnectée",
"alarm_PvStringCurrentUnbalanced": "Déséquilibre de courant PV",
"alarm_NoUtilityGrid": "Réseau électrique absent",
"alarm_GridVoltageOutOfRange": "Tension réseau hors plage",
"alarm_GridFrequencyOutOfRange": "Fréquence réseau hors plage",
"alarm_Overload": "Surcharge",
"alarm_MeterDisconnected": "Compteur déconnecté",
"alarm_MeterReverselyConnected": "Compteur inversé",
"alarm_LinePeVoltageAbnormal": "Tension anormale",
"alarm_PhaseSequenceError": "Séquence de phase erronée",
"alarm_FanFailure": "Défaillance du ventilateur",
"alarm_MeterAbnormal": "Compteur anormal",
"alarm_OptimizerCommunicationAbnormal": "Communication optimiseur anormale",
"alarm_OverTemperature": "Température excessive",
"alarm_OverTemperatureAlarm": "Alarme température élevée",
"alarm_NtcTemperatureSensorBroken": "Capteur de température défectueux",
"alarm_SyncSignalAbnormal": "Signal de synchronisation anormal",
"alarm_GridStartupConditionsNotMet": "Conditions de démarrage réseau non remplies",
"alarm_BatteryCommunicationFailure": "Échec de communication batterie",
"alarm_BatteryDisconnected": "Batterie déconnectée",
"alarm_BatteryVoltageTooHigh": "Tension batterie trop élevée",
"alarm_BatteryVoltageTooLow": "Tension batterie trop basse",
"alarm_BatteryReverseConnected": "Batterie branchée à l'envers",
"alarm_LeadAcidTempSensorDisconnected": "Capteur température batterie plomb désactivé",
"alarm_BatteryTemperatureOutOfRange": "Température batterie hors plage",
"alarm_BmsFault": "Défaillance BMS",
"alarm_LithiumBatteryOverload": "Surcharge batterie lithium",
"alarm_BmsCommunicationAbnormal": "Communication BMS anormale",
"alarm_BatterySpdAbnormal": "Défaillance SPD batterie",
"alarm_OutputDcComponentBiasAbnormal": "Biais DC de sortie anormal",
"alarm_DcComponentOverHighOutputVoltage": "Tension de sortie trop élevée",
"alarm_OffGridOutputVoltageTooLow": "Tension de sortie hors réseau trop basse",
"alarm_OffGridOutputVoltageTooHigh": "Tension de sortie hors réseau trop élevée",
"alarm_OffGridOutputOverCurrent": "Courant de sortie hors réseau trop élevé",
"alarm_OffGridOutputOverload": "Surcharge sortie hors réseau",
"alarm_BalancedCircuitAbnormal": "Circuit équilibré anormal",
"alarm_ExportLimitationFailSafe": "Sécurité limite d'exportation",
"alarm_DcBiasAbnormal": "Biais DC anormal",
"alarm_HighDcComponentOutputCurrent": "Composante DC élevée courant de sortie",
"alarm_BusVoltageSamplingAbnormal": "Tension d'alimentation anormale",
"alarm_RelayFault": "Défaillance du relais",
"alarm_BusVoltageAbnormal": "Tension d'alimentation anormale",
"alarm_InternalCommunicationFailure": "Échec de communication interne",
"alarm_TemperatureSensorDisconnected": "Capteur de température déconnecté",
"alarm_IgbtDriveFault": "Défaillance de l'IGBT",
"alarm_EepromError": "Erreur EEPROM",
"alarm_AuxiliaryPowerAbnormal": "Alimentation auxiliaire anormale",
"alarm_DcAcOvercurrentProtection": "Protection contre les surintensités",
"alarm_CommunicationProtocolMismatch": "Incompatibilité de protocole",
"alarm_DspComFirmwareMismatch": "Incompatibilité firmware DSP/COM",
"alarm_DspSoftwareHardwareMismatch": "Incompatibilité logiciel DSP/matériel",
"alarm_CpldAbnormal": "CPLD anormal",
"alarm_RedundancySamplingInconsistent": "Échantillonnage redondant incohérent",
"alarm_PwmPassThroughSignalFailure": "Échec signal PWM",
"alarm_AfciSelfTestFailure": "Échec auto-test AFCI",
"alarm_PvCurrentSamplingAbnormal": "Mesure PV anormale",
"alarm_AcCurrentSamplingAbnormal": "Mesure AC anormale",
"alarm_BusSoftbootFailure": "Échec démarrage DC",
"alarm_EpoFault": "Défaillance EPO",
"alarm_MonitoringChipBootVerificationFailed": "Échec vérification démarrage",
"alarm_BmsCommunicationFailure": "Échec communication BMS",
"alarm_BmsChargeDischargeFailure": "Échec charge/décharge BMS",
"alarm_BatteryVoltageLow": "Tension batterie faible",
"alarm_BatteryVoltageHigh": "Tension batterie élevée",
"alarm_BatteryTemperatureAbnormal": "Température anormale de la batterie",
"alarm_BatteryReversed": "Batterie inversée",
"alarm_BatteryOpenCircuit": "Circuit batterie ouvert",
"alarm_BatteryOverloadProtection": "Protection contre la surcharge",
"alarm_Bus2VoltageAbnormal": "Tension anormale Bus2",
"alarm_BatteryChargeOcp": "Surintensité charge batterie",
"alarm_BatteryDischargeOcp": "Surintensité décharge batterie",
"alarm_BatterySoftStartFailed": "Démarrage en douceur échoué",
"alarm_EpsOutputShortCircuited": "Circuit de secours en court-circuit",
"alarm_OffGridBusVoltageLow": "Tension bus hors réseau basse",
"alarm_OffGridTerminalVoltageAbnormal": "Tension anormale terminal hors réseau",
"alarm_SoftStartFailed": "Démarrage progressif échoué",
"alarm_OffGridOutputVoltageAbnormal": "Tension de sortie hors réseau anormale",
"alarm_BalancedCircuitSelfTestFailed": "Autotest circuit équilibré échoué",
"alarm_HighDcComponentOutputVoltage": "Tension de sortie à composante CC élevée",
"alarm_OffGridParallelSignalAbnormal": "Signal parallèle hors réseau anormal",
"alarm_AFCIFault": "Défaillance AFCI",
"alarm_GFCIHigh": "Courant de défaut élevé",
"alarm_PVVoltageHigh": "Tension PV élevée",
"alarm_OffGridBusVoltageTooLow": "Tension du bus hors réseau trop faible"
}

View File

@ -0,0 +1,231 @@
{
"alarm_AbnormalGridVoltage": "Tensione di rete anomala",
"alarm_AbnormalGridFrequency": "Frequenza di rete anomala",
"alarm_InvertedSequenceOfGridVoltage": "Sequenza di fase invertita",
"alarm_GridVoltagePhaseLoss": "Mancanza di fase rete",
"alarm_AbnormalGridCurrent": "Corrente di rete anomala",
"alarm_AbnormalOutputVoltage": "Tensione di uscita anomala",
"alarm_AbnormalOutputFrequency": "Frequenza di uscita anomala",
"alarm_AbnormalNullLine": "Linea neutra anomala",
"alarm_AbnormalOffGridOutputVoltage": "Tensione di uscita in standby anomala",
"alarm_ExcessivelyHighAmbientTemperature": "Temperatura ambientale troppo alta",
"alarm_ExcessiveRadiatorTemperature": "Temperatura radiatore troppo alta",
"alarm_PcbOvertemperature": "Scheda elettronica troppo calda",
"alarm_DcConverterOvertemperature": "Sovratemperatura convertitore DC",
"alarm_InverterOvertemperatureAlarm": "Allarme surriscaldamento inverter",
"alarm_InverterOvertemperature": "Surriscaldamento inverter",
"alarm_DcConverterOvertemperatureAlarm": "Allarme sovratemperatura convertitore DC",
"alarm_InsulationFault": "Guasto isolamento",
"alarm_LeakageProtectionFault": "Guasto protezione dispersione",
"alarm_AbnormalLeakageSelfCheck": "Autocontrollo perdite anomalo",
"alarm_PoorGrounding": "Messa a terra insufficiente",
"alarm_FanFault": "Guasto ventilatore",
"alarm_AuxiliaryPowerFault": "Guasto Alimentazione Ausiliaria",
"alarm_ModelCapacityFault": "Guasto Configurazione Modello",
"alarm_AbnormalLightningArrester": "Parasurtense Anomalo",
"alarm_IslandProtection": "Protezione Isola",
"alarm_Battery1NotConnected": "Batteria 1 Non Connessa",
"alarm_Battery1Overvoltage": "Batteria 1 Sovratensione",
"alarm_Battery1Undervoltage": "Batteria 1 sottotensione",
"alarm_Battery1DischargeEnd": "Fine scarica batteria 1",
"alarm_Battery1Inverted": "Batteria 1 invertita",
"alarm_Battery1OverloadTimeout": "Tempo di sovraccarico batteria 1",
"alarm_Battery1SoftStartFailure": "Avvio morbido batteria 1 fallito",
"alarm_Battery1PowerTubeFault": "Guasto modulo di potenza batteria 1",
"alarm_Battery1InsufficientPower": "Batteria 1 Potenza Insufficiente",
"alarm_Battery1BackupProhibited": "Backup Batteria 1 Bloccato",
"alarm_Battery2NotConnected": "Batteria 2 Non Connessa",
"alarm_Battery2Overvoltage": "Sovratensione Batteria 2",
"alarm_Battery2Undervoltage": "Sottotensione Batteria 2",
"alarm_Battery2DischargeEnd": "Fine Scarica Batteria 2",
"alarm_Battery2Inverted": "Polarità batteria 2 invertita",
"alarm_Battery2OverloadTimeout": "Sovraccarico batteria 2",
"alarm_Battery2SoftStartFailure": "Avvio batteria 2 fallito",
"alarm_Battery2PowerTubeFault": "Guasto modulo potenza batteria 2",
"alarm_Battery2InsufficientPower": "Potenza insufficiente batteria 2",
"alarm_Battery2BackupProhibited": "Backup vietato batteria 2",
"alarm_LithiumBattery1ChargeForbidden": "Carica Batteria Litio 1 Bloccata",
"alarm_LithiumBattery1DischargeForbidden": "Scarica Batteria Litio 1 Bloccata",
"alarm_LithiumBattery2ChargeForbidden": "Carica Batteria Litio 2 Bloccata",
"alarm_LithiumBattery2DischargeForbidden": "Scarica Batteria Litio 2 Bloccata",
"alarm_LithiumBattery1Full": "Batteria Litio 1 Piena",
"alarm_LithiumBattery1DischargeEnd": "Fine Scarica Batteria Litio 1",
"alarm_LithiumBattery2Full": "Batteria Litio 2 Piena",
"alarm_LithiumBattery2DischargeEnd": "Fine Scarica Batteria 2",
"alarm_LeadBatteryTemperatureAbnormality": "Temperatura Batteria Anomala",
"alarm_BatteryAccessMethodError": "Errore Metodo Accesso Batteria",
"alarm_Pv1NotAccessed": "PV1 Non Rilevato",
"alarm_Pv1Overvoltage": "Sovratensione PV1",
"alarm_AbnormalPv1CurrentSharing": "Corrente PV1 anomala",
"alarm_Pv1PowerTubeFault": "Guasto tubo di potenza PV1",
"alarm_Pv1SoftStartFailure": "Avvio morbido PV1 fallito",
"alarm_Pv1OverloadTimeout": "Sovraccarico PV1",
"alarm_Pv1InsufficientPower": "Bassa potenza PV1",
"alarm_Photovoltaic1Overcurrent": "Sovracorrente PV1",
"alarm_Pv2NotAccessed": "PV2 non accessibile",
"alarm_Pv2Overvoltage": "Sovratensione PV2",
"alarm_AbnormalPv2CurrentSharing": "Condivisione corrente PV2 anomala",
"alarm_Pv2PowerTubeFault": "Guasto Tubo di Potenza PV2",
"alarm_Pv2SoftStartFailure": "Avvio Morbido PV2 Fallito",
"alarm_Pv2OverloadTimeout": "Sovraccarico PV2 Scaduto",
"alarm_Pv2InsufficientPower": "Potenza PV2 insufficiente",
"alarm_Pv3NotConnected": "PV3 non connesso",
"alarm_Pv3Overvoltage": "Sovratensione PV3",
"alarm_Pv3AverageCurrentAnomaly": "Anomalia Corrente Media PV3",
"alarm_Pv3PowerTubeFailure": "Guasto Tubo di Potenza PV3",
"alarm_Pv3SoftStartFailure": "Guasto Avvio Morbido PV3",
"alarm_Pv3OverloadTimeout": "Sovraccarico Pv3",
"alarm_Pv3ReverseConnection": "Collegamento Inverso Pv3",
"alarm_Pv4NotConnected": "Pv4 Non Collegato",
"alarm_Pv4Overvoltage": "Sovratensione PV4",
"alarm_Pv4AverageCurrentAnomaly": "Anomalia Corrente Media PV4",
"alarm_Pv4PowerTubeFailure": "Guasto Modulo di Potenza PV4",
"alarm_Pv4SoftStartFailure": "Avvio morbido fallito PV4",
"alarm_Pv4OverloadTimeout": "Sovraccarico prolungato PV4",
"alarm_Pv4ReverseConnection": "Connessione invertita PV4",
"alarm_InsufficientPhotovoltaicPower": "Potenza fotovoltaica insufficiente",
"alarm_DcBusOvervoltage": "Sovratensione bus DC",
"alarm_DcBusUndervoltage": "Sottotensione bus DC",
"alarm_DcBusVoltageUnbalance": "Squilibrio tensione DC",
"alarm_BusSlowOvervoltage": "Sovratensione lenta del bus",
"alarm_HardwareBusOvervoltage": "Sovratensione hardware del bus",
"alarm_BusSoftStartFailure": "Avvio morbido fallito",
"alarm_InverterPowerTubeFault": "Guasto modulo inverter",
"alarm_HardwareOvercurrent": "Sovracorrente hardware",
"alarm_DcConverterOvervoltage": "Sovratensione convertitore DC",
"alarm_DcConverterHardwareOvervoltage": "Protezione sovratensione hardware",
"alarm_DcConverterOvercurrent": "Sovracorrente convertitore DC",
"alarm_DcConverterHardwareOvercurrent": "Sovracorrente hardware convertitore DC",
"alarm_DcConverterResonatorOvercurrent": "Sovracorrente risonatore convertitore DC",
"alarm_SystemOutputOverload": "Sovraccarico uscita sistema",
"alarm_InverterOverload": "Sovraccarico Inverter",
"alarm_InverterOverloadTimeout": "Sovraccarico Inverter Prolungato",
"alarm_LoadPowerOverload": "Carico Elettrico Eccessivo",
"alarm_BalancedCircuitOverloadTimeout": "Sovraccarico circuito bilanciato",
"alarm_InverterSoftStartFailure": "Avvio inverter fallito",
"alarm_Dsp1ParameterSettingFault": "Parametri DSP1 errati",
"alarm_Dsp2ParameterSettingFault": "Errore configurazione parametri DSP 2",
"alarm_DspVersionCompatibilityFault": "Errore compatibilità versione DSP",
"alarm_CpldVersionCompatibilityFault": "Errore compatibilità versione CPLD",
"alarm_CpldCommunicationFault": "Guasto comunicazione CPLD",
"alarm_DspCommunicationFault": "Guasto comunicazione DSP",
"alarm_OutputVoltageDcOverlimit": "Tensione DC in uscita eccessiva",
"alarm_OutputCurrentDcOverlimit": "Corrente DC in uscita superata",
"alarm_RelaySelfCheckFails": "Autotest relè fallito",
"alarm_InverterRelayOpen": "Relè inverter aperto",
"alarm_InverterRelayShortCircuit": "Cortocircuito del relè dell'inverter",
"alarm_OpenCircuitOfPowerGridRelay": "Relè di rete aperto",
"alarm_ShortCircuitOfPowerGridRelay": "Cortocircuito del relè di rete",
"alarm_GeneratorRelayOpenCircuit": "Relè generatore aperto",
"alarm_GeneratorRelayShortCircuit": "Relè generatore corto circuito",
"alarm_AbnormalInverter": "Inverter anomalo",
"alarm_ParallelCommunicationAlarm": "Allarme Comunicazione Parallela",
"alarm_ParallelModuleMissing": "Modulo Parallelo Mancante",
"alarm_DuplicateMachineNumbersForParallelModules": "Numeri Duplicati Moduli Paralleli",
"alarm_ParameterConflictInParallelModule": "Conflitto parametri modulo parallelo",
"alarm_SystemDerating": "Riduzione prestazioni sistema",
"alarm_PvAccessMethodErrorAlarm": "Errore metodo accesso PV",
"alarm_ReservedAlarms4": "Allarme Riservato 4",
"alarm_ReservedAlarms5": "Allarme Riservato 5",
"alarm_ReverseMeterConnection": "Contatore Inverso",
"alarm_InverterSealPulse": "Impulso Sigillo Inverter",
"alarm_AbnormalDieselGeneratorVoltage": "Tensione Generatore Diesel Anomala",
"alarm_AbnormalDieselGeneratorFrequency": "Frequenza Generatore Diesel Anomala",
"alarm_DieselGeneratorVoltageReverseSequence": "Sequenza di fase invertita",
"alarm_DieselGeneratorVoltageOutOfPhase": "Fase del generatore errata",
"alarm_GeneratorOverload": "Sovraccarico del generatore",
"alarm_StringFault": "Guasto alla stringa",
"alarm_PvStringPidQuickConnectAbnormal": "Connessione rapida anomala",
"alarm_DcSpdFunctionAbnormal": "Protezione sovratensione DC anomala",
"alarm_PvShortCircuited": "Cortocircuito PV",
"alarm_PvBoostDriverAbnormal": "Anomalia driver di boost PV",
"alarm_AcSpdFunctionAbnormal": "Anomalia protezione da sovratensioni AC",
"alarm_DcFuseBlown": "Fusibile DC saltato",
"alarm_DcInputVoltageTooHigh": "Tensione DC troppo alta",
"alarm_PvReversed": "Polarità PV invertita",
"alarm_PidFunctionAbnormal": "Funzione PID Anomala",
"alarm_PvStringDisconnected": "Stringa PV Disconnessa",
"alarm_PvStringCurrentUnbalanced": "Corrente Stringa PV Squilibrata",
"alarm_NoUtilityGrid": "Nessuna rete elettrica",
"alarm_GridVoltageOutOfRange": "Tensione di rete fuori limite",
"alarm_GridFrequencyOutOfRange": "Frequenza di rete fuori limite",
"alarm_Overload": "Sovraccarico",
"alarm_MeterDisconnected": "Contatore scollegato",
"alarm_MeterReverselyConnected": "Contatore collegato inversamente",
"alarm_LinePeVoltageAbnormal": "Tensione PE anomala",
"alarm_PhaseSequenceError": "Errore sequenza fase",
"alarm_FanFailure": "Guasto ventola",
"alarm_MeterAbnormal": "Contatore Anomalo",
"alarm_OptimizerCommunicationAbnormal": "Comunicazione Ottimizzatore Anomala",
"alarm_OverTemperature": "Temperatura Eccessiva",
"alarm_OverTemperatureAlarm": "Allarme Temperatura Elevata",
"alarm_NtcTemperatureSensorBroken": "Sensore Temperatura NTC Guasto",
"alarm_SyncSignalAbnormal": "Segnale di Sincronizzazione Anomalo",
"alarm_GridStartupConditionsNotMet": "Condizioni di avvio rete non soddisfatte",
"alarm_BatteryCommunicationFailure": "Comunicazione batteria fallita",
"alarm_BatteryDisconnected": "Batteria scollegata",
"alarm_BatteryVoltageTooHigh": "Tensione batteria troppo alta",
"alarm_BatteryVoltageTooLow": "Tensione batteria troppo bassa",
"alarm_BatteryReverseConnected": "Batteria collegata al contrario",
"alarm_LeadAcidTempSensorDisconnected": "Sensore temperatura disconnesso",
"alarm_BatteryTemperatureOutOfRange": "Temperatura batteria anomala",
"alarm_BmsFault": "Guasto BMS",
"alarm_LithiumBatteryOverload": "Sovraccarico batteria litio",
"alarm_BmsCommunicationAbnormal": "Comunicazione BMS anomala",
"alarm_BatterySpdAbnormal": "SPD batteria anomalo",
"alarm_OutputDcComponentBiasAbnormal": "Bias DC anomalo in uscita",
"alarm_DcComponentOverHighOutputVoltage": "Tensione di uscita troppo alta",
"alarm_OffGridOutputVoltageTooLow": "Tensione di uscita troppo bassa",
"alarm_OffGridOutputVoltageTooHigh": "Tensione in uscita troppo alta",
"alarm_OffGridOutputOverCurrent": "Corrente in uscita eccessiva",
"alarm_OffGridOutputOverload": "Sovraccarico uscita off-grid",
"alarm_BalancedCircuitAbnormal": "Circuiti squilibrati anomali",
"alarm_ExportLimitationFailSafe": "Limite esportazione sicurezza",
"alarm_DcBiasAbnormal": "Bias DC anomalo",
"alarm_HighDcComponentOutputCurrent": "Corrente di uscita DC elevata",
"alarm_BusVoltageSamplingAbnormal": "Campionamento tensione bus anomalo",
"alarm_RelayFault": "Guasto Relè",
"alarm_BusVoltageAbnormal": "Tensione Bus Anomala",
"alarm_InternalCommunicationFailure": "Comunicazione Interna Interrotta",
"alarm_TemperatureSensorDisconnected": "Sensore temperatura scollegato",
"alarm_IgbtDriveFault": "Guasto al driver IGBT",
"alarm_EepromError": "Errore EEPROM",
"alarm_AuxiliaryPowerAbnormal": "Alimentazione ausiliaria anomala",
"alarm_DcAcOvercurrentProtection": "Protezione sovracorrente DC/AC",
"alarm_CommunicationProtocolMismatch": "Incompatibilità protocollo comunicazione",
"alarm_DspComFirmwareMismatch": "Incompatibilità firmware DSP/COM",
"alarm_DspSoftwareHardwareMismatch": "Incompatibilità software/hardware DSP",
"alarm_CpldAbnormal": "Anomalia CPLD",
"alarm_RedundancySamplingInconsistent": "Campioni ridondanti incoerenti",
"alarm_PwmPassThroughSignalFailure": "Segnale PWM guasto",
"alarm_AfciSelfTestFailure": "Autotest AFCI fallito",
"alarm_PvCurrentSamplingAbnormal": "Corrente PV Anomala",
"alarm_AcCurrentSamplingAbnormal": "Corrente AC Anomala",
"alarm_BusSoftbootFailure": "Avvio Bus DC Fallito",
"alarm_EpoFault": "Guasto EPO",
"alarm_MonitoringChipBootVerificationFailed": "Verifica avvio chip monitoraggio fallita",
"alarm_BmsCommunicationFailure": "Comunicazione BMS fallita",
"alarm_BmsChargeDischargeFailure": "Guasto Carica/Scarica BMS",
"alarm_BatteryVoltageLow": "Tensione Batteria Bassa",
"alarm_BatteryVoltageHigh": "Tensione Batteria Alta",
"alarm_BatteryTemperatureAbnormal": "Temperatura batteria anomala",
"alarm_BatteryReversed": "Batteria invertita",
"alarm_BatteryOpenCircuit": "Circuiti aperti batteria",
"alarm_BatteryOverloadProtection": "Protezione sovraccarico batteria",
"alarm_Bus2VoltageAbnormal": "Tensione bus2 anomala",
"alarm_BatteryChargeOcp": "Protezione sovraccarico carica",
"alarm_BatteryDischargeOcp": "Protezione sovraccarico scarica",
"alarm_BatterySoftStartFailed": "Avvio batteria fallito",
"alarm_EpsOutputShortCircuited": "Cortocircuito uscita EPS",
"alarm_OffGridBusVoltageLow": "Tensione Bus Fuori Rete Bassa",
"alarm_OffGridTerminalVoltageAbnormal": "Tensione Terminale Fuori Rete Anomala",
"alarm_SoftStartFailed": "Avvio Morbido Fallito",
"alarm_OffGridOutputVoltageAbnormal": "Tensione uscita off-grid anomala",
"alarm_BalancedCircuitSelfTestFailed": "Autotest circuito bilanciato fallito",
"alarm_HighDcComponentOutputVoltage": "Tensione uscita con componente DC elevato",
"alarm_OffGridParallelSignalAbnormal": "Segnale parallelo anomalo",
"alarm_AFCIFault": "Guasto AFCI",
"alarm_GFCIHigh": "Corrente di guasto elevata",
"alarm_PVVoltageHigh": "Tensione PV Elevata",
"alarm_OffGridBusVoltageTooLow": "Tensione bus off-grid troppo bassa"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@ public static class AlarmKnowledgeBase
if (GrowattAlarms.TryGetValue(normalized, out var growattDiag))
return growattDiag;
// Try case-insensitive match for Sinexcel (alarm names may vary in casing)
// Try case-insensitive match for both Sinexcel and Growatt
var lowerDesc = normalized.ToLowerInvariant();
foreach (var kvp in SinexcelAlarms)
{
@ -34,6 +34,12 @@ public static class AlarmKnowledgeBase
return kvp.Value;
}
foreach (var kvp in GrowattAlarms)
{
if (kvp.Key.ToLowerInvariant() == lowerDesc)
return kvp.Value;
}
return null;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,101 @@
using InnovEnergy.App.Backend.DataTypes;
namespace InnovEnergy.App.Backend.Services;
public static class BehaviorAnalyzer
{
private const double SolarActiveThresholdKwh = 0.1; // min PV kWh in an hour to count as "solar active"
private const double LowSoCThreshold = 20.0; // % below which battery is considered depleted
/// <summary>
/// Derives behavioral facts from hourly data for the current week only.
/// All computation is pure C# — no AI involved.
/// </summary>
public static BehavioralPattern Analyze(List<HourlyEnergyData> hourlyData)
{
if (hourlyData.Count == 0)
return new BehavioralPattern();
// ── Per-hour averages across the week ──────────────────────────────
// Group by hour-of-day (0-23), average each metric across all days
var byHour = Enumerable.Range(0, 24).Select(h =>
{
var rows = hourlyData.Where(r => r.Hour == h).ToList();
if (rows.Count == 0)
return (Hour: h, AvgPv: 0.0, AvgLoad: 0.0, AvgGridImport: 0.0);
return (
Hour: h,
AvgPv: rows.Average(r => r.PvKwh),
AvgLoad: rows.Average(r => r.LoadKwh),
AvgGridImport: rows.Average(r => r.GridImportKwh)
);
}).ToList();
// ── Peak load hour ─────────────────────────────────────────────────
var peakLoadEntry = byHour.OrderByDescending(h => h.AvgLoad).First();
// ── Peak solar hour and end of solar window ────────────────────────
var peakSolarEntry = byHour.OrderByDescending(h => h.AvgPv).First();
// Solar window: last hour in the day where avg PV > threshold
var solarActiveHours = byHour.Where(h => h.AvgPv >= SolarActiveThresholdKwh).ToList();
var peakSolarEndHour = solarActiveHours.Count > 0
? solarActiveHours.Max(h => h.Hour)
: peakSolarEntry.Hour;
// ── Highest grid-import hour ────────────────────────────────────────
var worstGridEntry = byHour.OrderByDescending(h => h.AvgGridImport).First();
// ── Avoidable grid imports: grid drawn during hours when solar was active ──
// For each actual hourly record: if solar > threshold AND grid import > 0 → avoidable
var avoidableGridKwh = Math.Round(
hourlyData
.Where(r => r.PvKwh >= SolarActiveThresholdKwh && r.GridImportKwh > 0)
.Sum(r => r.GridImportKwh),
1);
// ── Weekday vs weekend average daily load ──────────────────────────
var weekdayDays = hourlyData
.Where(r => !r.IsWeekend)
.GroupBy(r => r.DateTime.Date)
.Select(g => g.Sum(r => r.LoadKwh))
.ToList();
var weekendDays = hourlyData
.Where(r => r.IsWeekend)
.GroupBy(r => r.DateTime.Date)
.Select(g => g.Sum(r => r.LoadKwh))
.ToList();
var weekdayAvg = weekdayDays.Count > 0 ? Math.Round(weekdayDays.Average(), 1) : 0;
var weekendAvg = weekendDays.Count > 0 ? Math.Round(weekendDays.Average(), 1) : 0;
// ── Battery depletion hour ─────────────────────────────────────────
// For each day, find the first evening hour (after 18:00) where SoC < threshold
// Average that hour across days where it occurs
var depletionHours = hourlyData
.Where(r => r.Hour >= 18 && r.BattSoC > 0 && r.BattSoC < LowSoCThreshold)
.GroupBy(r => r.DateTime.Date)
.Select(g => g.OrderBy(r => r.Hour).First().Hour)
.ToList();
var avgDepletedHour = depletionHours.Count > 0 ? (int)Math.Round(depletionHours.Average()) : -1;
var batteryDepletsNight = depletionHours.Count >= 3; // happens on 3+ nights = consistent pattern
return new BehavioralPattern
{
PeakLoadHour = peakLoadEntry.Hour,
AvgPeakLoadKwh = Math.Round(peakLoadEntry.AvgLoad, 2),
PeakSolarHour = peakSolarEntry.Hour,
PeakSolarEndHour = peakSolarEndHour,
AvgPeakSolarKwh = Math.Round(peakSolarEntry.AvgPv, 2),
HighestGridImportHour = worstGridEntry.Hour,
AvgGridImportAtPeakHour = Math.Round(worstGridEntry.AvgGridImport, 2),
AvoidableGridKwh = avoidableGridKwh,
WeekdayAvgDailyLoad = weekdayAvg,
WeekendAvgDailyLoad = weekendAvg,
AvgBatteryDepletedHour = avgDepletedHour,
BatteryDepletesOvernight = batteryDepletsNight,
};
}
}

View File

@ -3,6 +3,7 @@ using Flurl.Http;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
namespace InnovEnergy.App.Backend.Services;
@ -18,6 +19,9 @@ public static class DiagnosticService
/// <summary>In-memory cache: errorDescription → parsed response.</summary>
private static readonly ConcurrentDictionary<string, DiagnosticResponse> Cache = new();
/// <summary>Pre-generated translations keyed by language code → alarm key → response.</summary>
private static readonly Dictionary<string, IReadOnlyDictionary<string, DiagnosticResponse>> Translations = new();
// ── initialisation ──────────────────────────────────────────────
public static void Initialize()
@ -25,12 +29,34 @@ public static class DiagnosticService
var apiKey = Environment.GetEnvironmentVariable("MISTRAL_API_KEY");
if (string.IsNullOrWhiteSpace(apiKey))
{
Console.Error.WriteLine("[DiagnosticService] MISTRAL_API_KEY not set AI diagnostics disabled.");
return;
else
_apiKey = apiKey;
// Load pre-generated translation files (en, de, fr, it) if available
// en.json is generated by generate_alarm_translations.py after the review campaign
var resourcesDir = Path.Combine(AppContext.BaseDirectory, "Resources");
foreach (var lang in new[] { "en", "de", "fr", "it" })
{
var file = Path.Combine(resourcesDir, $"AlarmTranslations.{lang}.json");
if (!File.Exists(file)) continue;
try
{
var json = File.ReadAllText(file);
var raw = JsonConvert.DeserializeObject<Dictionary<string, DiagnosticResponse>>(json);
if (raw is not null)
{
Translations[lang] = raw;
Console.WriteLine($"[DiagnosticService] Loaded {raw.Count} {lang} translations.");
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"[DiagnosticService] Failed to load AlarmTranslations.{lang}.json: {ex.Message}");
}
}
_apiKey = apiKey;
Console.WriteLine("[DiagnosticService] initialised.");
}
@ -46,6 +72,10 @@ public static class DiagnosticService
_ => "English"
};
/// <summary>Converts "AbnormalGridVoltage" → "Abnormal Grid Voltage".</summary>
private static string SplitCamelCase(string name) =>
Regex.Replace(name, @"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])", " ").Trim();
/// <summary>
/// Returns a diagnosis for <paramref name="errorDescription"/> in the given language.
/// For English: checks the static AlarmKnowledgeBase first, then in-memory cache, then Mistral AI.
@ -56,21 +86,38 @@ public static class DiagnosticService
{
var cacheKey = $"{errorDescription}|{language}";
// 1. For English only: check the static knowledge base first (no API call needed)
// 1. For English: check the static knowledge base first (no API call needed)
if (language == "en")
{
var knownDiagnosis = AlarmKnowledgeBase.TryGetDiagnosis(errorDescription);
if (knownDiagnosis is not null)
{
Console.WriteLine($"[DiagnosticService] Found diagnosis in knowledge base for: {errorDescription}");
return knownDiagnosis;
Console.WriteLine($"[DiagnosticService] Knowledge base hit (en): {errorDescription}");
// Return a new instance with Name set — avoids mutating the shared static dictionary
return new DiagnosticResponse
{
Name = SplitCamelCase(errorDescription),
Explanation = knownDiagnosis.Explanation,
Causes = knownDiagnosis.Causes,
NextSteps = knownDiagnosis.NextSteps,
};
}
}
// 2. If AI is not enabled, we can't proceed further
// 2. For non-English: check pre-generated translation files (no API call needed)
if (language != "en" && Translations.TryGetValue(language, out var langDict))
{
if (langDict.TryGetValue(errorDescription, out var translatedDiagnosis))
{
Console.WriteLine($"[DiagnosticService] Pre-generated translation hit ({language}): {errorDescription}");
return translatedDiagnosis;
}
}
// 3. If AI is not enabled, we can't proceed further
if (!IsEnabled) return null;
// 3. Check in-memory cache for previously fetched AI diagnoses
// 4. Check in-memory cache for previously fetched AI diagnoses
if (Cache.TryGetValue(cacheKey, out var cached))
return cached;
@ -104,22 +151,52 @@ public static class DiagnosticService
// ── test helper (no DB dependency) ─────────────────────────────
/// <summary>
/// Returns a diagnosis from the static knowledge base (English) or pre-generated
/// translations (other languages). Returns null if not found in either.
/// </summary>
public static DiagnosticResponse? TryGetTranslation(string errorDescription, string language)
{
// Check JSON translations first (en.json exists after review campaign)
if (Translations.TryGetValue(language, out var langDict) &&
langDict.TryGetValue(errorDescription, out var translated))
return translated;
// Fallback: English from compiled AlarmKnowledgeBase.cs (until en.json is deployed)
if (language == "en")
{
var kb = AlarmKnowledgeBase.TryGetDiagnosis(errorDescription);
if (kb is null) return null;
return new DiagnosticResponse
{
Name = SplitCamelCase(errorDescription),
Explanation = kb.Explanation,
Causes = kb.Causes,
NextSteps = kb.NextSteps,
};
}
return null;
}
/// <summary>
/// Calls Mistral directly with a generic prompt. For testing only - no DB lookup.
/// </summary>
public static async Task<DiagnosticResponse?> TestCallMistralAsync(string errorDescription)
public static async Task<DiagnosticResponse?> TestCallMistralAsync(string errorDescription, string language = "en")
{
if (!IsEnabled) return null;
var cacheKey = $"{errorDescription}|{language}";
// Check cache first
if (Cache.TryGetValue(errorDescription, out var cached))
if (Cache.TryGetValue(cacheKey, out var cached))
return cached;
var prompt = BuildPrompt(errorDescription, "SodioHome", new List<string>(), "en");
var prompt = BuildPrompt(errorDescription, "SodioHome", new List<string>(), language);
var response = await CallMistralAsync(prompt);
if (response is not null)
Cache.TryAdd(errorDescription, response);
Cache.TryAdd(cacheKey, response);
return response;
}
@ -132,18 +209,19 @@ public static class DiagnosticService
? string.Join(", ", recentErrors)
: "none";
return $@"You are a technician for Innovenergy {productName} battery energy storage systems.
These are lithium-ion BESS units with a BMS, PV inverter, and grid inverter.
return $@"You are a technician for {productName} battery energy storage systems.
These are sodium-ion BESS units with a BMS, PV inverter, and grid inverter.
Error: {errorDescription}
Other recent errors: {recentList}
Explain for a non-technical homeowner. Keep it very short and simple:
- name: 2-5 word display title for this alarm
- explanation: 1 short sentence, no jargon
- causes: 2-3 bullet points, plain language
- nextSteps: 2-3 simple action items a homeowner can understand
IMPORTANT: Write all text values in {LanguageName(language)}. Reply with ONLY valid JSON, no markdown:
{{""explanation"":""1 short sentence"",""causes"":[""...""],""nextSteps"":[""...""]}}
{{""name"":""short title"",""explanation"":""1 short sentence"",""causes"":[""...""],""nextSteps"":[""...""]}}
";
}
@ -212,6 +290,7 @@ IMPORTANT: Write all text values in {LanguageName(language)}. Reply with ONLY va
public class DiagnosticResponse
{
public string Name { get; set; } = "";
public string Explanation { get; set; } = "";
public IReadOnlyList<string> Causes { get; set; } = Array.Empty<string>();
public IReadOnlyList<string> NextSteps { get; set; } = Array.Empty<string>();

View File

@ -6,13 +6,14 @@ namespace InnovEnergy.App.Backend.Services;
public static class ExcelDataParser
{
// Column headers from the ESS Link Cloud Excel export
private const string ColDateTime = "Data time";
private const string ColPvToday = "PV Generated Energy Today";
private const string ColLoadToday = "Load Consumption Today";
private const string ColGridImportToday = "Purchased Energy Today";
private const string ColGridExportToday = "Feed in energy Today";
private const string ColBattChargedToday = "Daily Battery Charged";
private const string ColDateTime = "Data time";
private const string ColPvToday = "PV Generated Energy Today";
private const string ColLoadToday = "Load Consumption Today";
private const string ColGridImportToday = "Purchased Energy Today";
private const string ColGridExportToday = "Feed in energy Today";
private const string ColBattChargedToday = "Daily Battery Charged";
private const string ColBattDischargedToday = "Battery Discharged Today";
private const string ColBattSoC = "Battery 1 SoC"; // instantaneous %
/// <summary>
/// Parses an ESS Link Cloud Excel export file and returns one DailyEnergyData per day.
@ -79,6 +80,103 @@ public static class ExcelDataParser
return dailyLastRows.Values.ToList();
}
/// <summary>
/// Parses hourly energy snapshots from the xlsx.
/// For each hour of each day, finds the row nearest HH:00:00 and records the
/// cumulative "Today" values at that moment. The caller (BehaviorAnalyzer) then
/// diffs consecutive snapshots to get per-hour energy.
/// </summary>
public static List<HourlyEnergyData> ParseHourly(string filePath)
{
if (!File.Exists(filePath))
throw new FileNotFoundException($"Excel file not found: {filePath}");
using var workbook = new XLWorkbook(filePath);
var worksheet = workbook.Worksheet(1);
var lastRow = worksheet.LastRowUsed()?.RowNumber() ?? 0;
if (lastRow < 2)
throw new InvalidOperationException("Excel file has no data rows.");
// Build column map
var headerRow = worksheet.Row(1);
var colMap = new Dictionary<string, int>();
for (var col = 1; col <= worksheet.LastColumnUsed()?.ColumnNumber(); col++)
{
var header = headerRow.Cell(col).GetString().Trim();
if (!string.IsNullOrEmpty(header))
colMap[header] = col;
}
// SoC column is optional — not all exports include it
var hasSoC = colMap.ContainsKey(ColBattSoC);
// Read all rows into memory as (DateTime, cumulative values) pairs
var rawRows = new List<(DateTime Dt, double Pv, double Load, double GridIn, double BattChg, double BattDis, double SoC)>();
for (var row = 2; row <= lastRow; row++)
{
var dtStr = worksheet.Row(row).Cell(colMap[ColDateTime]).GetString().Trim();
if (string.IsNullOrEmpty(dtStr)) continue;
if (!DateTime.TryParse(dtStr, out var dt)) continue;
rawRows.Add((
dt,
GetDouble(worksheet, row, colMap[ColPvToday]),
GetDouble(worksheet, row, colMap[ColLoadToday]),
GetDouble(worksheet, row, colMap[ColGridImportToday]),
GetDouble(worksheet, row, colMap[ColBattChargedToday]),
GetDouble(worksheet, row, colMap[ColBattDischargedToday]),
hasSoC ? GetDouble(worksheet, row, colMap[ColBattSoC]) : 0
));
}
if (rawRows.Count == 0) return new List<HourlyEnergyData>();
// For each calendar hour that exists in the data, find the nearest row to HH:00:00
// Group rows by (date, hour) and pick the one closest to the round hour
var byHour = rawRows
.GroupBy(r => new DateTime(r.Dt.Year, r.Dt.Month, r.Dt.Day, r.Dt.Hour, 0, 0))
.OrderBy(g => g.Key)
.Select(g =>
{
var roundHour = g.Key;
var nearest = g.OrderBy(r => Math.Abs((r.Dt - roundHour).TotalSeconds)).First();
return (RoundHour: roundHour, Row: nearest);
})
.ToList();
// Diff consecutive snapshots within the same day to get per-hour energy
var result = new List<HourlyEnergyData>();
for (var i = 1; i < byHour.Count; i++)
{
var prev = byHour[i - 1];
var curr = byHour[i];
// Only diff within the same day — don't carry over across midnight
if (curr.RoundHour.Date != prev.RoundHour.Date) continue;
// Cumulative "Today" values reset at midnight, so diff is always >= 0 within a day
result.Add(new HourlyEnergyData
{
DateTime = curr.RoundHour,
Hour = curr.RoundHour.Hour,
DayOfWeek = curr.RoundHour.DayOfWeek.ToString(),
IsWeekend = curr.RoundHour.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday,
PvKwh = Math.Max(0, Math.Round(curr.Row.Pv - prev.Row.Pv, 3)),
LoadKwh = Math.Max(0, Math.Round(curr.Row.Load - prev.Row.Load, 3)),
GridImportKwh = Math.Max(0, Math.Round(curr.Row.GridIn - prev.Row.GridIn, 3)),
BatteryChargedKwh = Math.Max(0, Math.Round(curr.Row.BattChg - prev.Row.BattChg, 3)),
BatteryDischargedKwh = Math.Max(0, Math.Round(curr.Row.BattDis - prev.Row.BattDis, 3)),
BattSoC = curr.Row.SoC,
});
}
Console.WriteLine($"[ExcelDataParser] Parsed {result.Count} hourly records from {filePath}");
return result;
}
private static double GetDouble(IXLWorksheet ws, int row, int col)
{
var cell = ws.Row(row).Cell(col);

View File

@ -99,21 +99,21 @@ public static class ReportEmailService
Consumption: "Verbrauch",
GridImport: "Netzbezug",
GridExport: "Netzeinspeisung",
BatteryInOut: "Batterie Ein/Aus",
SolarEnergyUsed: "Genutzte Solarenergie",
StayedAtHome: "direkt genutzt",
BatteryInOut: "Batterie Laden / Entladen",
SolarEnergyUsed: "Energie gespart",
StayedAtHome: "Solar + Batterie, nicht vom Netz",
EstMoneySaved: "Geschätzte Ersparnis",
AtRate: "bei 0.27 CHF/kWh",
SolarCoverage: "Solare Deckung",
FromSolar: "durch Solar",
AtRate: "bei 0.39 CHF/kWh",
SolarCoverage: "Eigenversorgung",
FromSolar: "aus Solar + Batterie",
BatteryEff: "Batterie-Eff.",
OutVsIn: "Aus vs. Ein",
OutVsIn: "Entladung vs. Ladung",
Day: "Tag",
Load: "Last",
GridIn: "Netz Ein",
GridOut: "Netz Aus",
BattInOut: "Batt. Ein/Aus",
Footer: "Erstellt von <strong style=\"color:#666\">Inesco Energy Monitor Platform</strong> · Powered by Mistral AI"
BattInOut: "Batt. Laden/Entl.",
Footer: "Erstellt von <strong style=\"color:#666\">inesco Energy Monitor</strong>"
),
"fr" => new EmailStrings(
Title: "Rapport de performance hebdomadaire",
@ -129,21 +129,21 @@ public static class ReportEmailService
Consumption: "Consommation",
GridImport: "Import réseau",
GridExport: "Export réseau",
BatteryInOut: "Batterie Entrée/Sortie",
SolarEnergyUsed: "Énergie solaire utilisée",
StayedAtHome: "autoconsommée",
BatteryInOut: "Batterie Charge / Décharge",
SolarEnergyUsed: "Énergie économisée",
StayedAtHome: "solaire + batterie, non achetée au réseau",
EstMoneySaved: "Économies estimées",
AtRate: "à 0.27 CHF/kWh",
SolarCoverage: "Couverture solaire",
FromSolar: "depuis le solaire",
AtRate: "à 0.39 CHF/kWh",
SolarCoverage: "Autosuffisance",
FromSolar: "du solaire + batterie",
BatteryEff: "Eff. batterie",
OutVsIn: "sortie vs entrée",
OutVsIn: "décharge vs charge",
Day: "Jour",
Load: "Charge",
GridIn: "Réseau Ent.",
GridOut: "Réseau Sor.",
BattInOut: "Batt. Ent./Sor.",
Footer: "Généré par <strong style=\"color:#666\">Inesco Energy Monitor Platform</strong> · Propulsé par Mistral AI"
BattInOut: "Batt. Ch./Déch.",
Footer: "Généré par <strong style=\"color:#666\">inesco Energy Monitor</strong>"
),
"it" => new EmailStrings(
Title: "Rapporto settimanale delle prestazioni",
@ -159,21 +159,21 @@ public static class ReportEmailService
Consumption: "Consumo",
GridImport: "Import dalla rete",
GridExport: "Export nella rete",
BatteryInOut: "Batteria Ent./Usc.",
SolarEnergyUsed: "Energia solare utilizzata",
StayedAtHome: "rimasta in casa",
BatteryInOut: "Batteria Carica / Scarica",
SolarEnergyUsed: "Energia risparmiata",
StayedAtHome: "solare + batteria, non acquistata dalla rete",
EstMoneySaved: "Risparmio stimato",
AtRate: "a 0.27 CHF/kWh",
SolarCoverage: "Copertura solare",
FromSolar: "dal solare",
AtRate: "a 0.39 CHF/kWh",
SolarCoverage: "Autosufficienza",
FromSolar: "da solare + batteria",
BatteryEff: "Eff. batteria",
OutVsIn: "uscita vs entrata",
OutVsIn: "scarica vs carica",
Day: "Giorno",
Load: "Carico",
GridIn: "Rete Ent.",
GridOut: "Rete Usc.",
BattInOut: "Batt. Ent./Usc.",
Footer: "Generato da <strong style=\"color:#666\">Inesco Energy Monitor Platform</strong> · Powered by Mistral AI"
BattInOut: "Batt. Car./Sc.",
Footer: "Generato da <strong style=\"color:#666\">inesco Energy Monitor</strong>"
),
_ => new EmailStrings(
Title: "Weekly Performance Report",
@ -189,21 +189,21 @@ public static class ReportEmailService
Consumption: "Consumption",
GridImport: "Grid Import",
GridExport: "Grid Export",
BatteryInOut: "Battery In/Out",
SolarEnergyUsed: "Solar Energy Used",
StayedAtHome: "stayed at home",
BatteryInOut: "Battery Charge / Discharge",
SolarEnergyUsed: "Energy Saved",
StayedAtHome: "solar + battery, not bought from grid",
EstMoneySaved: "Est. Money Saved",
AtRate: "at 0.27 CHF/kWh",
SolarCoverage: "Solar Coverage",
FromSolar: "from solar",
AtRate: "at 0.39 CHF/kWh",
SolarCoverage: "Self-Sufficiency",
FromSolar: "from solar + battery",
BatteryEff: "Battery Eff.",
OutVsIn: "out vs in",
OutVsIn: "discharge vs charge",
Day: "Day",
Load: "Load",
GridIn: "Grid In",
GridOut: "Grid Out",
BattInOut: "Batt In/Out",
Footer: "Generated by <strong style=\"color:#666\">Inesco Energy Monitor Platform</strong> · Powered by Mistral AI"
BattInOut: "Batt. Ch./Dis.",
Footer: "Generated by <strong style=\"color:#666\">inesco Energy Monitor</strong>"
)
};
@ -230,21 +230,44 @@ public static class ReportEmailService
"</ul>"
: $"<p style=\"margin:0;line-height:1.6\">{FormatInsightLine(r.AiInsight)}</p>";
// Daily rows
// 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");
var dayName = DateTime.Parse(d.Date).ToString("ddd dd.MM");
var isCurrentWeek = string.Compare(d.Date, r.PeriodStart, StringComparison.Ordinal) >= 0;
var bgColor = isCurrentWeek ? "#ffffff" : "#f9f9f9";
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=""background:{bgColor}"">
<td style=""padding:6px 10px;border-bottom:1px solid #eee"">{dayName} {d.Date}</td>
<td style=""padding:6px 10px;border-bottom:1px solid #eee;text-align:right"">{d.PvProduction:F1}</td>
<td style=""padding:6px 10px;border-bottom:1px solid #eee;text-align:right"">{d.LoadConsumption:F1}</td>
<td style=""padding:6px 10px;border-bottom:1px solid #eee;text-align:right"">{d.GridImport:F1}</td>
<td style=""padding:6px 10px;border-bottom:1px solid #eee;text-align:right"">{d.GridExport:F1}</td>
<td style=""padding:6px 10px;border-bottom:1px solid #eee;text-align:right"">{d.BatteryCharged:F1}/{d.BatteryDischarged:F1}</td>
<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>";
}
@ -342,8 +365,8 @@ public static class ReportEmailService
<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.CurrentWeek.TotalPvProduction - r.CurrentWeek.TotalGridExport:F1} kWh", s.StayedAtHome, "#27ae60")}
{SavingsBox(s.EstMoneySaved, $"~{(r.CurrentWeek.TotalPvProduction - r.CurrentWeek.TotalGridExport) * 0.27:F1} CHF", s.AtRate, "#2980b9")}
{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>
@ -351,18 +374,18 @@ public static class ReportEmailService
</td>
</tr>
<!-- Daily Breakdown -->
<!-- Daily Breakdown (bar chart) -->
<tr>
<td style=""padding:0 30px 24px"">
<div style=""font-size:16px;font-weight:bold;margin-bottom:12px;color:#2c3e50"">{s.DailyBreakdown}</div>
<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"">
<th style=""padding:6px 10px;text-align:left"">{s.Day}</th>
<th style=""padding:6px 10px;text-align:right"">PV</th>
<th style=""padding:6px 10px;text-align:right"">{s.Load}</th>
<th style=""padding:6px 10px;text-align:right"">{s.GridIn}</th>
<th style=""padding:6px 10px;text-align:right"">{s.GridOut}</th>
<th style=""padding:6px 10px;text-align:right"">{s.BattInOut}</th>
<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 &nbsp;&nbsp;" : "")}
<span style=""display:inline-block;width:10px;height:10px;background:#3498db;border-radius:2px;margin-right:4px""></span>{s.Load} &nbsp;&nbsp;
{(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>
@ -407,10 +430,10 @@ public static class ReportEmailService
{
result = line;
}
// Bold numbers followed by units
// Bold all numbers: time ranges (14:0018:00), times (09:00), decimals, integers
result = System.Text.RegularExpressions.Regex.Replace(
result,
@"(\d+[\d,.]*\s*(?:kWh|CHF|%|days?))",
@"(\d{1,2}:\d{2}(?:[\-]\d{1,2}:\d{2})?|\d+[.,]\d+|\d+)",
"<strong>$1</strong>");
return result;
}

View File

@ -1,4 +1,3 @@
using System.Collections.Concurrent;
using Flurl.Http;
using InnovEnergy.App.Backend.DataTypes;
using Newtonsoft.Json;
@ -9,19 +8,15 @@ public static class WeeklyReportService
{
private static readonly string TmpReportDir = Environment.CurrentDirectory + "/tmp_report/";
private static readonly ConcurrentDictionary<string, string> InsightCache = new();
// Bump this version when the AI prompt changes to automatically invalidate old cache files
private const string CacheVersion = "v2";
/// <summary>
/// Generates a full weekly report for the given installation.
/// Caches the full report as JSON next to the xlsx. Cache is invalidated when xlsx is updated or CacheVersion changes.
/// Cache is invalidated automatically when the xlsx file is newer than the cache.
/// To force regeneration (e.g. after a prompt change), simply delete the cache files.
/// </summary>
public static async Task<WeeklyReportResponse> GenerateReportAsync(long installationId, string installationName, string language = "en")
{
var xlsxPath = TmpReportDir + installationId + ".xlsx";
var cachePath = TmpReportDir + $"{installationId}_{language}_{CacheVersion}.cache.json";
var cachePath = TmpReportDir + $"{installationId}_{language}.cache.json";
// Use cached report if xlsx hasn't changed since cache was written
if (File.Exists(cachePath) && File.Exists(xlsxPath))
@ -47,8 +42,10 @@ public static class WeeklyReportService
}
}
var allDays = ExcelDataParser.Parse(xlsxPath);
var report = await GenerateReportFromDataAsync(allDays, installationName, language);
// Parse both daily summaries and hourly intervals from the same xlsx
var allDays = ExcelDataParser.Parse(xlsxPath);
var allHourly = ExcelDataParser.ParseHourly(xlsxPath);
var report = await GenerateReportFromDataAsync(allDays, allHourly, installationName, language);
// Write cache
try
@ -64,15 +61,18 @@ public static class WeeklyReportService
}
/// <summary>
/// Core report generation from daily data. Data-source agnostic.
/// Core report generation. Accepts both daily summaries and hourly intervals.
/// </summary>
public static async Task<WeeklyReportResponse> GenerateReportFromDataAsync(
List<DailyEnergyData> allDays, string installationName, string language = "en")
List<DailyEnergyData> allDays,
List<HourlyEnergyData> allHourly,
string installationName,
string language = "en")
{
// Sort by date
allDays = allDays.OrderBy(d => d.Date).ToList();
// Split into previous week and current week
// Split into previous week and current week (daily)
List<DailyEnergyData> previousWeekDays;
List<DailyEnergyData> currentWeekDays;
@ -87,11 +87,15 @@ public static class WeeklyReportService
currentWeekDays = allDays;
}
// Restrict hourly data to current week only for behavioral analysis
var currentWeekStart = DateTime.Parse(currentWeekDays.First().Date);
var currentHourlyData = allHourly.Where(h => h.DateTime.Date >= currentWeekStart.Date).ToList();
var currentSummary = Summarize(currentWeekDays);
var previousSummary = previousWeekDays.Count > 0 ? Summarize(previousWeekDays) : null;
// Calculate key ratios for current week
var selfSufficiency = currentSummary.TotalConsumption > 0
// Key ratios for current week
var selfSufficiency = currentSummary.TotalConsumption > 0
? Math.Round((currentSummary.TotalConsumption - currentSummary.TotalGridImport) / currentSummary.TotalConsumption * 100, 1)
: 0;
@ -112,26 +116,42 @@ public static class WeeklyReportService
var consumptionChange = PercentChange(previousSummary?.TotalConsumption, currentSummary.TotalConsumption);
var gridImportChange = PercentChange(previousSummary?.TotalGridImport, currentSummary.TotalGridImport);
// AI insight
var aiInsight = await GetAiInsightAsync(currentWeekDays, currentSummary, previousSummary,
selfSufficiency, gridDependency, batteryEfficiency, installationName, language);
// Behavioral pattern from hourly data (pure C# — no AI)
var behavior = BehaviorAnalyzer.Analyze(currentHourlyData);
// Pre-computed savings — single source of truth for UI and AI
const double ElectricityPriceCHF = 0.39;
var totalEnergySaved = Math.Round(currentSummary.TotalConsumption - currentSummary.TotalGridImport, 1);
var totalSavingsCHF = Math.Round(totalEnergySaved * ElectricityPriceCHF, 0);
var avgDailyConsumption = currentWeekDays.Count > 0 ? currentSummary.TotalConsumption / currentWeekDays.Count : 0;
var daysEquivalent = avgDailyConsumption > 0 ? Math.Round(totalEnergySaved / avgDailyConsumption, 1) : 0;
// AI insight combining daily facts + behavioral pattern
var aiInsight = await GetAiInsightAsync(
currentWeekDays, currentSummary, previousSummary,
selfSufficiency, totalEnergySaved, totalSavingsCHF,
behavior, installationName, language);
return new WeeklyReportResponse
{
InstallationName = installationName,
PeriodStart = currentWeekDays.First().Date,
PeriodEnd = currentWeekDays.Last().Date,
CurrentWeek = currentSummary,
PreviousWeek = previousSummary,
SelfSufficiencyPercent = selfSufficiency,
SelfConsumptionPercent = selfConsumption,
InstallationName = installationName,
PeriodStart = currentWeekDays.First().Date,
PeriodEnd = currentWeekDays.Last().Date,
CurrentWeek = currentSummary,
PreviousWeek = previousSummary,
TotalEnergySaved = totalEnergySaved,
TotalSavingsCHF = totalSavingsCHF,
DaysEquivalent = daysEquivalent,
SelfSufficiencyPercent = selfSufficiency,
SelfConsumptionPercent = selfConsumption,
BatteryEfficiencyPercent = batteryEfficiency,
GridDependencyPercent = gridDependency,
GridDependencyPercent = gridDependency,
PvChangePercent = pvChange,
ConsumptionChangePercent = consumptionChange,
GridImportChangePercent = gridImportChange,
DailyData = allDays,
AiInsight = aiInsight,
DailyData = allDays,
Behavior = behavior,
AiInsight = aiInsight,
};
}
@ -163,13 +183,17 @@ public static class WeeklyReportService
_ => "English"
};
private static string FormatHour(int hour) => $"{hour:D2}:00";
private static string FormatHourSlot(int hour) => $"{hour:D2}:00{hour + 1:D2}:00";
private static async Task<string> GetAiInsightAsync(
List<DailyEnergyData> currentWeek,
WeeklySummary current,
WeeklySummary? previous,
double selfSufficiency,
double gridDependency,
double batteryEfficiency,
double totalEnergySaved,
double totalSavingsCHF,
BehavioralPattern behavior,
string installationName,
string language = "en")
{
@ -180,53 +204,114 @@ public static class WeeklyReportService
return "AI insight unavailable (API key not configured).";
}
// Cache key: installation + period + language
var cacheKey = $"{installationName}_{currentWeek.Last().Date}_{language}";
if (InsightCache.TryGetValue(cacheKey, out var cached))
return cached;
const double ElectricityPriceCHF = 0.39;
// Build compact prompt
var dayLines = string.Join("\n", currentWeek.Select(d =>
{
var dayName = DateTime.Parse(d.Date).ToString("ddd");
return $"{dayName} {d.Date}: PV={d.PvProduction:F1} Load={d.LoadConsumption:F1} GridIn={d.GridImport:F1} GridOut={d.GridExport:F1} BattIn={d.BatteryCharged:F1} BattOut={d.BatteryDischarged:F1}";
}));
// Detect which components are present
var hasPv = currentWeek.Sum(d => d.PvProduction) > 0.5;
var hasBattery = currentWeek.Sum(d => d.BatteryCharged) > 0.5
|| currentWeek.Sum(d => d.BatteryDischarged) > 0.5;
var hasGrid = currentWeek.Sum(d => d.GridImport) > 0.5;
var comparison = previous != null
? $"vs Last week: PV {current.TotalPvProduction} vs {previous.TotalPvProduction}, Grid Import {current.TotalGridImport} vs {previous.TotalGridImport}, Consumption {current.TotalConsumption} vs {previous.TotalConsumption}"
: "No previous week data available.";
var bestDay = currentWeek.OrderByDescending(d => d.PvProduction).First();
var worstDay = currentWeek.OrderBy(d => d.PvProduction).First();
var bestDayName = DateTime.Parse(bestDay.Date).ToString("dddd");
var worstDayName = DateTime.Parse(worstDay.Date).ToString("dddd");
var solarSavings = Math.Round(current.TotalPvProduction - current.TotalGridExport, 1);
var topBattDay = currentWeek.OrderByDescending(d => d.BatteryCharged).First();
var topBattDayName = DateTime.Parse(topBattDay.Date).ToString("dddd");
var prompt = $@"You are an energy advisor for a SodistoreHome installation: ""{installationName}"".
// Behavioral facts as compact lines
var peakSolarWindow = FormatHour(behavior.PeakSolarHour) + "" + FormatHour(behavior.PeakSolarEndHour);
var avoidableSavingsCHF = Math.Round(behavior.AvoidableGridKwh * ElectricityPriceCHF, 0);
Write exactly 4 bullet points (each on its own line starting with ""- ""). No bold markers, no asterisks, no markdown plain text only.
var battDepleteLine = hasBattery
? (behavior.AvgBatteryDepletedHour >= 0
? $"Battery typically depletes below 20% during {FormatHourSlot(behavior.AvgBatteryDepletedHour)}."
: "Battery stayed above 20% SoC every night this week.")
: "";
var weekdayWeekendLine = behavior.WeekendAvgDailyLoad > 0
? $"Weekday avg load: {behavior.WeekdayAvgDailyLoad} kWh/day. Weekend avg: {behavior.WeekendAvgDailyLoad} kWh/day."
: $"Weekday avg load: {behavior.WeekdayAvgDailyLoad} kWh/day.";
// Build conditional fact lines
var pvDailyFact = hasPv
? $"- PV: total {current.TotalPvProduction:F1} kWh this week. Best day: {bestDayName} ({bestDay.PvProduction:F1} kWh), worst: {worstDayName} ({worstDay.PvProduction:F1} kWh). Solar covered {selfSufficiency}% of consumption."
: "";
var battDailyFact = hasBattery
? $"- Battery: {current.TotalBatteryCharged:F1} kWh charged, {current.TotalBatteryDischarged:F1} kWh discharged. Most active day: {topBattDayName} ({topBattDay.BatteryCharged:F1} kWh charged)."
: "";
var gridDailyFact = hasGrid
? $"- Grid import: {current.TotalGridImport:F1} kWh total this week."
: "";
var pvBehaviorLines = hasPv ? $@"
- Solar active window: {peakSolarWindow}; peak hour: {FormatHourSlot(behavior.PeakSolarHour)}, avg {behavior.AvgPeakSolarKwh} kWh during that hour
- Grid imported while solar was active: {behavior.AvoidableGridKwh} kWh = {avoidableSavingsCHF} CHF that could have been avoided" : "";
var gridBehaviorLine = hasGrid
? $"- Highest grid-import hour: {FormatHourSlot(behavior.HighestGridImportHour)}, avg {behavior.AvgGridImportAtPeakHour} kWh during that hour"
: "";
var battBehaviorLine = !string.IsNullOrEmpty(battDepleteLine) ? $"- {battDepleteLine}" : "";
// Build conditional instructions
var instruction1 = $"1. Energy savings: Write 12 sentences. Say that this week, thanks to sodistore home, the customer avoided buying {totalEnergySaved} kWh from the grid, saving {totalSavingsCHF} CHF (at {ElectricityPriceCHF} CHF/kWh). Use these exact numbers — do not recalculate or change them.";
var instruction2 = hasPv
? $"2. Solar performance: Comment on the best and worst solar day this week and the likely weather reason."
: hasGrid
? $"2. Grid usage: Comment on the {current.TotalGridImport:F1} kWh drawn from the grid this week and what time of day drives it most ({FormatHourSlot(behavior.HighestGridImportHour)})."
: "2. Consumption pattern: Comment on the weekday vs weekend load pattern.";
var instruction3 = hasBattery
? $"3. Battery performance: Use the daily facts. Keep it simple for a homeowner."
: "3. Consumption pattern: Comment on the peak load time and weekday vs weekend usage.";
var instruction4 = hasPv
? $"4. Smart action for next week: Write exactly 2 sentences. Sentence 1: point out the timing mismatch using exact numbers — peak household load is during {FormatHourSlot(behavior.PeakLoadHour)} ({behavior.AvgPeakLoadKwh} kWh) but solar peaks during {FormatHourSlot(behavior.PeakSolarHour)} ({behavior.AvgPeakSolarKwh} kWh), with solar active from {peakSolarWindow}. Sentence 2: suggest shifting energy-intensive appliances (such as washing machine, dishwasher, heat pump, or EV charger if applicable) to run during the solar window {peakSolarWindow} — do not assume which specific device the customer has."
: hasGrid
? $"4. Smart action for next week: Write exactly 2 sentences. Sentence 1: state that the peak grid-import hour is {FormatHourSlot(behavior.HighestGridImportHour)} ({behavior.AvgGridImportAtPeakHour} kWh avg). Sentence 2: suggest one action to reduce grid use during that hour — shifting energy-intensive appliances (washing machine, dishwasher, heat pump, EV charger) away from that time."
: "4. Smart action for next week: Give one practical tip to reduce energy consumption based on the peak load time and weekday/weekend pattern.";
var prompt = $@"You are an energy advisor for a sodistore home installation: ""{installationName}"".
Write 4 bullet points (each on its own line starting with ""- ""). No bold markers, no asterisks, no markdown plain text only.
IMPORTANT FORMAT RULE: Each bullet MUST start with a short title followed by a colon, then the description. Example: ""- Title label: Description text here."" Translate the title label into {LanguageName(language)} but always keep the ""Title: description"" structure.
1. Solar savings: this week the system saved {solarSavings} kWh from the grid. Explain what this means in simple terms (e.g. equivalent to X days of average household use, or roughly X CHF saved at ~0.27 CHF/kWh).
2. Best vs worst solar day: name the best and worst days with their PV kWh values. Mention likely weather reason.
3. Battery performance: was the battery well-utilized this week? Mention charge/discharge totals and any standout days.
4. Tip of the week: one specific, practical recommendation based on THIS week's patterns to save more energy or money.
CRITICAL: All numbers below are pre-calculated. Use these values as-is do not recalculate, round differently, or change any number.
Rules: Use actual day names and numbers. Keep each bullet to 1-2 sentences. Write for a homeowner, not an engineer. Do NOT use asterisks or any formatting marks.
IMPORTANT: Write your entire response in {LanguageName(language)}.
SYSTEM COMPONENTS: PV={hasPv}, Battery={hasBattery}, Grid={hasGrid}
Daily data (kWh):
{dayLines}
DAILY FACTS:
- Total consumption: {current.TotalConsumption:F1} kWh this week. Self-sufficiency: {selfSufficiency}%.
{pvDailyFact}
{battDailyFact}
{gridDailyFact}
Totals: PV={current.TotalPvProduction:F1} Load={current.TotalConsumption:F1} GridIn={current.TotalGridImport:F1} GridOut={current.TotalGridExport:F1} BattIn={current.TotalBatteryCharged:F1} BattOut={current.TotalBatteryDischarged:F1}
Solar used at home={solarSavings} kWh ({selfSufficiency}% of consumption covered by solar)
Battery-eff={batteryEfficiency}%
{comparison}";
BEHAVIORAL PATTERN (from hourly data this week):
- Peak household load: {FormatHourSlot(behavior.PeakLoadHour)}, avg {behavior.AvgPeakLoadKwh} kWh during that hour
- {weekdayWeekendLine}{pvBehaviorLines}
{gridBehaviorLine}
{battBehaviorLine}
INSTRUCTIONS:
{instruction1}
{instruction2}
{instruction3}
{instruction4}
Rules: Write for a homeowner, not an engineer. Do NOT use asterisks or any formatting marks. Only describe components that exist (PV={hasPv}, Battery={hasBattery}, Grid={hasGrid}). Do NOT add any closing remark, summary sentence, or motivational phrase after the 4 bullet points (e.g. no 'Das ist ein guter Fortschritt', 'Keep it up', 'Good progress', etc.). Write exactly 4 bullet points nothing before, nothing after.
IMPORTANT: Write your entire response in {LanguageName(language)}.";
try
{
var requestBody = new
{
model = "mistral-small-latest",
messages = new[] { new { role = "user", content = prompt } },
max_tokens = 350,
model = "mistral-small-latest",
messages = new[] { new { role = "user", content = prompt } },
max_tokens = 400,
temperature = 0.3
};
@ -241,7 +326,6 @@ Battery-eff={batteryEfficiency}%
if (!string.IsNullOrWhiteSpace(content))
{
var insight = content.Trim();
InsightCache.TryAdd(cacheKey, insight);
Console.WriteLine($"[WeeklyReportService] AI insight generated ({insight.Length} chars).");
return insight;
}

View File

@ -0,0 +1,320 @@
#!/usr/bin/env python3
"""
generate_alarm_translations.py
Post-campaign script: reads AlarmTranslationsChecked.de.json (the reviewed and
AI-synthesized German content), translates into English, French, and Italian,
and writes:
Resources/AlarmTranslations.de.json replace with reviewed German
Resources/AlarmTranslations.en.json back-translated from German
Resources/AlarmTranslations.fr.json translated from German
Resources/AlarmTranslations.it.json translated from German
Services/AlarmKnowledgeBase.cs updated English source (keeps same structure)
Run this AFTER the review campaign is complete:
export MISTRAL_API_KEY=your_key_here
cd csharp/App/Backend
python3 generate_alarm_translations.py
"""
import json
import os
import re
import sys
import time
import shutil
from typing import Optional
import requests
# ── Config ─────────────────────────────────────────────────────────────────
CHECKED_FILE = "Resources/AlarmTranslationsChecked.de.json"
KNOWLEDGE_BASE = "Services/AlarmKnowledgeBase.cs"
RESOURCES_DIR = "Resources"
MISTRAL_URL = "https://api.mistral.ai/v1/chat/completions"
MISTRAL_MODEL = "mistral-large-latest"
BATCH_SIZE = 5 # alarms per API call
RETRY_DELAY = 5 # seconds between retries on rate-limit
MAX_RETRIES = 3
REQUEST_TIMEOUT = (10, 90)
TARGET_LANGUAGES = {
"en": "English",
"fr": "French",
"it": "Italian",
}
# ── Mistral API ─────────────────────────────────────────────────────────────
def call_mistral(api_key: str, prompt: str) -> Optional[str]:
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
body = {
"model": MISTRAL_MODEL,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": 1800,
"temperature": 0.1,
}
for attempt in range(1, MAX_RETRIES + 1):
try:
resp = requests.post(MISTRAL_URL, headers=headers, json=body, timeout=REQUEST_TIMEOUT)
if resp.status_code == 429:
print(f" Rate limited, waiting {RETRY_DELAY}s (attempt {attempt}/{MAX_RETRIES})...")
time.sleep(RETRY_DELAY * attempt)
continue
resp.raise_for_status()
content = resp.json()["choices"][0]["message"]["content"].strip()
if content.startswith("```"):
first_newline = content.index("\n")
content = content[first_newline + 1:]
if content.endswith("```"):
content = content[:-3].strip()
return content
except requests.RequestException as e:
print(f" HTTP error: {e} (attempt {attempt}/{MAX_RETRIES})")
time.sleep(RETRY_DELAY)
return None
def translate_batch(api_key: str, batch: dict, target_language: str) -> Optional[dict]:
"""
Translates a batch of German alarm entries into the target language.
Input: { "AlarmKey": { "Explanation": "...", "Causes": [...], "NextSteps": [...] } }
Output: same structure in target language.
"""
prompt = f"""You are translating battery energy storage system alarm descriptions from German into {target_language}.
The source content has been reviewed by field engineers and is accurate.
Translate faithfully keep the same number of bullet points, same meaning, plain language for homeowners.
Input JSON (German):
{json.dumps(batch, ensure_ascii=False, indent=2)}
Return ONLY a valid JSON object with the same alarm keys. Each value must have exactly:
{{
"Explanation": "translated explanation (1 sentence)",
"Causes": ["translated cause 1", ...],
"NextSteps": ["translated step 1", ...]
}}
Reply with ONLY the JSON object, no markdown, no extra text."""
raw = call_mistral(api_key, prompt)
if raw is None:
return None
try:
return json.loads(raw)
except json.JSONDecodeError as e:
print(f" JSON parse error: {e}")
print(f" Raw (first 300 chars): {raw[:300]}")
return None
# ── AlarmKnowledgeBase.cs generation ────────────────────────────────────────
def parse_kb_key_sections(filepath: str) -> dict:
"""
Reads AlarmKnowledgeBase.cs and returns {key: "Sinexcel"|"Growatt"}
preserving the original section order.
"""
with open(filepath, "r", encoding="utf-8") as f:
content = f.read()
sinexcel_match = re.search(r'SinexcelAlarms\s*=\s*new Dictionary.*?\{(.*?)^\s*\};', content, re.DOTALL | re.MULTILINE)
growatt_match = re.search(r'GrowattAlarms\s*=\s*new Dictionary.*?\{(.*?)^\s*\};', content, re.DOTALL | re.MULTILINE)
result = {}
if sinexcel_match:
for key in re.findall(r'\["(\w+)"\]\s*=\s*new\(\)', sinexcel_match.group(1)):
result[key] = "Sinexcel"
if growatt_match:
for key in re.findall(r'\["(\w+)"\]\s*=\s*new\(\)', growatt_match.group(1)):
result[key] = "Growatt"
return result
def cs_escape(s: str) -> str:
"""Escapes a string for use inside a C# double-quoted string literal."""
return s.replace("\\", "\\\\").replace('"', '\\"')
def write_knowledge_base_cs(filepath: str, en_translations: dict, key_sections: dict):
"""
Writes an updated AlarmKnowledgeBase.cs using the new English translations,
preserving the original Sinexcel/Growatt section structure.
"""
sinexcel_keys = [k for k, s in key_sections.items() if s == "Sinexcel"]
growatt_keys = [k for k, s in key_sections.items() if s == "Growatt"]
def entry_block(key: str) -> str:
entry = en_translations.get(key)
if not entry:
return f' // [{key}] — no translation available\n'
exp = cs_escape(entry.get("Explanation", ""))
causes = ",\n ".join(f'"{cs_escape(c)}"' for c in entry.get("Causes", []))
steps = ",\n ".join(f'"{cs_escape(s)}"' for s in entry.get("NextSteps", []))
return (
f' ["{key}"] = new()\n'
f' {{\n'
f' Explanation = "{exp}",\n'
f' Causes = new[] {{ {causes} }},\n'
f' NextSteps = new[] {{ {steps} }}\n'
f' }},\n'
)
lines = []
lines.append("namespace InnovEnergy.App.Backend.Services;\n")
lines.append("\n")
lines.append("/// <summary>\n")
lines.append("/// Static knowledge base for Sinexcel and Growatt alarms.\n")
lines.append("/// Provides pre-defined diagnostics without requiring Mistral API calls.\n")
lines.append("/// Updated by generate_alarm_translations.py after the review campaign.\n")
lines.append("/// </summary>\n")
lines.append("public static class AlarmKnowledgeBase\n")
lines.append("{\n")
lines.append(" public static DiagnosticResponse? TryGetDiagnosis(string alarmDescription)\n")
lines.append(" {\n")
lines.append(" if (string.IsNullOrWhiteSpace(alarmDescription)) return null;\n")
lines.append(" var normalized = alarmDescription.Trim();\n")
lines.append(" if (SinexcelAlarms.TryGetValue(normalized, out var s)) return s;\n")
lines.append(" if (GrowattAlarms.TryGetValue(normalized, out var g)) return g;\n")
lines.append(" var lower = normalized.ToLowerInvariant();\n")
lines.append(" foreach (var kvp in SinexcelAlarms) if (kvp.Key.ToLowerInvariant() == lower) return kvp.Value;\n")
lines.append(" foreach (var kvp in GrowattAlarms) if (kvp.Key.ToLowerInvariant() == lower) return kvp.Value;\n")
lines.append(" return null;\n")
lines.append(" }\n")
lines.append("\n")
lines.append(" // ── Sinexcel Alarms ──────────────────────────────────────────────────────\n")
lines.append("\n")
lines.append(" private static readonly IReadOnlyDictionary<string, DiagnosticResponse> SinexcelAlarms = new Dictionary<string, DiagnosticResponse>\n")
lines.append(" {\n")
for key in sinexcel_keys:
lines.append(entry_block(key))
lines.append(" };\n")
lines.append("\n")
lines.append(" // ── Growatt Alarms ───────────────────────────────────────────────────────\n")
lines.append("\n")
lines.append(" private static readonly IReadOnlyDictionary<string, DiagnosticResponse> GrowattAlarms = new Dictionary<string, DiagnosticResponse>\n")
lines.append(" {\n")
for key in growatt_keys:
lines.append(entry_block(key))
lines.append(" };\n")
lines.append("}\n")
with open(filepath, "w", encoding="utf-8") as f:
f.writelines(lines)
print(f" ✓ Wrote updated AlarmKnowledgeBase.cs ({len(sinexcel_keys)} Sinexcel + {len(growatt_keys)} Growatt keys)")
# ── Main ────────────────────────────────────────────────────────────────────
def load_env_file(env_path: str) -> dict:
env = {}
try:
with open(env_path) as f:
for line in f:
line = line.strip()
if line and not line.startswith("#") and "=" in line:
k, _, v = line.partition("=")
env[k.strip()] = v.strip()
except FileNotFoundError:
pass
return env
def main():
api_key = os.environ.get("MISTRAL_API_KEY", "").strip()
if not api_key:
script_dir = os.path.dirname(os.path.abspath(__file__))
api_key = load_env_file(os.path.join(script_dir, ".env")).get("MISTRAL_API_KEY", "").strip()
if not api_key:
print("ERROR: MISTRAL_API_KEY not found in environment or .env file.")
sys.exit(1)
print("MISTRAL_API_KEY loaded.")
# Load reviewed German source
if not os.path.exists(CHECKED_FILE):
print(f"ERROR: {CHECKED_FILE} not found. Run the review campaign first.")
sys.exit(1)
with open(CHECKED_FILE, "r", encoding="utf-8") as f:
german_source = json.load(f)
alarm_keys = list(german_source.keys())
print(f"Loaded {len(alarm_keys)} alarms from {CHECKED_FILE}.")
# Step 1: copy reviewed German as the new de.json
de_out = os.path.join(RESOURCES_DIR, "AlarmTranslations.de.json")
shutil.copy(CHECKED_FILE, de_out)
print(f"\n✓ Copied reviewed German → {de_out}")
# Step 2: translate to en, fr, it
all_translations = {} # lang_code → {key → entry}
for lang_code, lang_name in TARGET_LANGUAGES.items():
print(f"\n── Translating to {lang_name} ({lang_code}) ──")
translations = {}
failed_keys = []
batches = [
{k: german_source[k] for k in alarm_keys[i:i + BATCH_SIZE]}
for i in range(0, len(alarm_keys), BATCH_SIZE)
]
for batch_num, batch in enumerate(batches, 1):
keys_in_batch = list(batch.keys())
print(f" Batch {batch_num}/{len(batches)}: {', '.join(keys_in_batch)}")
result = translate_batch(api_key, batch, lang_name)
if result is None:
print(f" FAILED batch {batch_num} — marking keys as failed")
failed_keys.extend(keys_in_batch)
continue
for key in keys_in_batch:
if key in result:
entry = result[key]
translations[key] = {
"Explanation": entry.get("Explanation", ""),
"Causes": entry.get("Causes", []),
"NextSteps": entry.get("NextSteps", []),
}
else:
print(f" WARNING: key '{key}' missing from batch result")
failed_keys.append(key)
if batch_num < len(batches):
time.sleep(1)
all_translations[lang_code] = translations
out_file = os.path.join(RESOURCES_DIR, f"AlarmTranslations.{lang_code}.json")
with open(out_file, "w", encoding="utf-8") as f:
json.dump(translations, f, ensure_ascii=False, indent=2)
print(f" ✓ Wrote {len(translations)} entries → {out_file}")
if failed_keys:
print(f" ⚠ Failed keys ({len(failed_keys)}): {failed_keys}")
# Step 3: update AlarmKnowledgeBase.cs with the new English back-translation
print("\n── Updating AlarmKnowledgeBase.cs ──")
if "en" in all_translations and os.path.exists(KNOWLEDGE_BASE):
key_sections = parse_kb_key_sections(KNOWLEDGE_BASE)
write_knowledge_base_cs(KNOWLEDGE_BASE, all_translations["en"], key_sections)
else:
print(" Skipped — en.json not generated or AlarmKnowledgeBase.cs not found.")
print("\n✓ Done. Review the output files before deploying.")
print(" Next: cd csharp/App/Backend && dotnet build && ./deploy.sh")
if __name__ == "__main__":
main()

View File

@ -63,6 +63,14 @@ function Log(props: LogProps) {
const { removeToken } = tokencontext;
const intl = useIntl();
/** "AbnormalGridVoltage" → "Abnormal Grid Voltage" */
const splitCamelCase = (s: string) =>
s.replace(/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/g, ' ').trim();
/** Returns a translated alarm display name, falling back to camelCase split. */
const alarmDisplayName = (description: string) =>
intl.formatMessage({ id: `alarm_${description}`, defaultMessage: splitCamelCase(description) });
const [diagnoses, setDiagnoses] = useState<{ description: string; lastSeen: string; response: DiagnosticResponse }[]>([]);
const [diagnosisLoading, setDiagnosisLoading] = useState(false);
const [expandedDiagnoses, setExpandedDiagnoses] = useState<Set<number>>(new Set());
@ -100,15 +108,8 @@ function Log(props: LogProps) {
});
}, [updateCount]);
// fetch AI diagnosis for the latest 3 unique errors/warnings
// only when installation status is red (2) or orange (1)
// fetch AI diagnosis for all unique errors/warnings from the last 24 hours
useEffect(() => {
// skip diagnosis if status is not alarm (2) or warning (1)
if (props.status !== 1 && props.status !== 2) {
setDiagnoses([]);
return;
}
// filter to last 24 hours only
const now = new Date();
const cutoff = new Date(now.getTime() - 24 * 60 * 60 * 1000);
@ -122,13 +123,13 @@ function Log(props: LogProps) {
})
.sort((a, b) => (b.date + ' ' + b.time).localeCompare(a.date + ' ' + a.time));
// deduplicate — keep the most-recent occurrence of each unique description
const seen = new Set<string>();
const targets: ErrorMessage[] = [];
for (const item of all) {
if (!seen.has(item.description)) {
seen.add(item.description);
targets.push(item);
if (targets.length >= 3) break;
}
}
@ -146,7 +147,7 @@ function Log(props: LogProps) {
Promise.all(
targets.map(target =>
axiosConfig
.get(`/DiagnoseError?installationId=${props.id}&errorDescription=${encodeURIComponent(target.description)}`)
.get(`/DiagnoseError?installationId=${props.id}&errorDescription=${encodeURIComponent(target.description)}&language=${intl.locale}`)
.then((res: AxiosResponse<DiagnosticResponse>) => {
if (res.status === 204 || !res.data || !res.data.explanation) return null;
return { description: target.description, lastSeen: target.date + ' ' + target.time, response: res.data };
@ -158,7 +159,7 @@ function Log(props: LogProps) {
}).finally(() => {
setDiagnosisLoading(false);
});
}, [errors, warnings, props.status]);
}, [errors, warnings]);
const handleErrorButtonPressed = () => {
setErrorButtonPressed(!errorButtonPressed);
@ -233,7 +234,7 @@ function Log(props: LogProps) {
setDemoLoading(true);
setDemoResult(null);
axiosConfigWithoutToken
.get(`/TestDiagnoseError?errorDescription=${encodeURIComponent(alarm)}`)
.get(`/TestDiagnoseError?errorDescription=${encodeURIComponent(alarm)}&language=${intl.locale}`)
.then((res: AxiosResponse<TestDiagnoseResult>) => {
setDemoResult(res.data);
})
@ -253,296 +254,6 @@ function Log(props: LogProps) {
return <Chip label="Not available" size="small" color="default" />;
};
const warningDescriptionMap: { [key: string]: string } = {
// BMS warnings
"TaM1": "TaM1: BMS temperature high",
"TbM1": "TbM1: Battery temperature high",
"VBm1": "VBm1: Bus voltage low",
"VBM1": "VBM1: Bus voltage high",
"IDM1": "IDM1: Discharge current high",
"vsm1": "vsm1: String voltage low",
"vsM1": "vsM1: String voltage high",
"iCM1": "iCM1: Charge current high",
"iDM1": "iDM1: Discharge current high",
"MID1": "MID1: String voltages unbalanced",
"BLPW": "BLPW: Not enough charging power on bus",
"CCBF": "CCBF: Internal charger hardware failure",
"Ah_W": "Ah_W: String SOC low",
"MPMM": "MPMM: Midpoint wiring problem",
"TCdi": "TCdi: Temperature difference between strings high",
"LMPW": "LMPW: String voltages unbalance warning",
"TOCW": "TOCW: Top of Charge requested",
// Sinexcel warnings (WARNING/INFO severity)
"Inverted sequenceof grid voltage": "Grid phase sequence reversed",
"Excessivelyhigh ambient temperature": "Ambient temperature too high",
"Excessive radiator temperature": "Radiator/heatsink temperature high",
"Island protection": "Island protection active (auto-recovers)",
"Battery 1over voltage": "Battery 1 voltage too high",
"Battery 1under voltage": "Battery 1 voltage too low",
"Battery 1discharge end": "Battery 1 discharge complete (auto-recovers)",
"Battery 1inverted": "Battery 1 polarity reversed!",
"Battery 2over voltage": "Battery 2 voltage too high",
"Battery 2under voltage": "Battery 2 voltage too low",
"Battery 2discharge end": "Battery 2 discharge complete (auto-recovers)",
"Battery 2inverted": "Battery 2 polarity reversed!",
"PV 1notaccessed": "PV string 1 not accessible",
"PV 1over voltage": "PV string 1 voltage too high",
"PV 2notaccessed": "PV string 2 not accessible",
"PV 2over voltage": "PV string 2 voltage too high",
"DC busover voltage": "DC bus voltage too high",
"DC busunder voltage": "DC bus voltage too low",
"Inverter soft start failure": "Inverter soft-start failed",
"Battery 1soft start failure": "Battery 1 soft-start failed",
"Battery 2soft start failure": "Battery 2 soft-start failed",
"Output voltageDC overlimit": "DC component in output voltage high",
"Output currentDC overlimit": "DC component in output current high",
"Poorgrounding": "Poor ground connection detected",
"PV 1soft startfailure": "PV 1 soft-start failed",
"PV 2soft startfailure": "PV 2 soft-start failed",
"PCBover temperature": "PCB temperature too high",
"DC converter over temperature": "DC converter temperature high",
"Busslow over voltage": "Slow bus over-voltage",
"DC converter over voltage": "DC converter voltage high",
"DC converter over current": "DC converter current high",
"DC converter resonator over current": "DC converter resonator overcurrent",
"PV 1insufficient power": "PV 1 power insufficient (auto-recovers)",
"PV 2insufficient power": "PV 2 power insufficient (auto-recovers)",
"Battery 1insufficient power": "Battery 1 power insufficient (auto-recovers)",
"Battery 2insufficiency power": "Battery 2 power insufficient",
"Lithium battery 1 chargeforbidden": "Lithium battery 1 charging forbidden",
"Lithium battery 1 dischargeforbidden": "Lithium battery 1 discharging forbidden",
"Lithium battery 2 chargeforbidden": "Lithium battery 2 charging forbidden",
"Lithium battery 2 dischargeforbidden": "Lithium battery 2 discharging forbidden",
"Lithium battery 1full": "Lithium battery 1 fully charged",
"Lithium battery 1 dischargeend": "Lithium battery 1 discharge end",
"Lithium battery 2full": "Lithium battery 2 fully charged",
"Lithium battery 2 dischargeend": "Lithium battery 2 discharge end",
"Inverter over temperaturealarm": "Inverter over-temperature alarm",
"Inverter over temperature": "Inverter temperature high",
"DC converter over temperaturealarm": "DC converter over-temperature alarm",
"Systemderating": "System power derating active",
"PVaccessmethod erroralarm": "PV access method error",
"Parallelmodule missing": "Parallel module missing",
"Duplicatemachine numbersforparallel modules": "Duplicate parallel module IDs",
"Para meterconflictin parallelmodule": "Parameter conflict in parallel modules",
"Reservedalarms 4": "Reserved alarm 4",
"InverterSealPulse": "Inverter seal pulse active",
"PV 3over voltage": "PV 3 voltage too high",
"PV 3average current anomaly": "PV 3 current anomaly",
"PV 4over voltage": "PV 4 voltage too high",
"PV 4average current anomaly": "PV 4 current anomaly",
"PV 3soft startfailure": "PV 3 soft-start failed",
"PV 4soft startfailure": "PV 4 soft-start failed",
"Batteryaccessmethod error": "Battery access method error",
"Reservedalarms 5": "Reserved alarm 5",
"Battery 1backup prohibited": "Battery 1 backup prohibited",
"Battery 2backup prohibited": "Battery 2 backup prohibited",
"Bus soft startfailure": "Bus soft-start failed",
"Insufficient photovoltaic power": "Insufficient PV power",
"Photovoltaic 1 over current": "PV 1 overcurrent",
"Photovoltaic 2 over current": "PV 2 overcurrent",
"Photovoltaic 3 over current": "PV 3 overcurrent",
"Photovoltaic 4 over current": "PV 4 overcurrent",
"Battery 1over current": "Battery 1 overcurrent",
"Battery 2over current": "Battery 2 overcurrent",
"Battery 1charging sealingwave": "Battery 1 charge limiting",
"Battery 2charging sealingwave": "Battery 2 charge limiting",
// Growatt warnings
"Warning 200": "String fault",
"Warning 201": "PV string/PID terminals abnormal",
"Warning 203": "PV1 or PV2 short circuited",
"Warning 208": "DC fuse blown",
"Warning 209": "DC input voltage too high",
"Warning 219": "PID function abnormal",
"Warning 220": "PV string disconnected",
"Warning 221": "PV string current unbalanced",
"Warning 300": "No grid connection / grid power failure",
"Warning 301": "Grid voltage out of range",
"Warning 302": "Grid frequency out of range",
"Warning 303": "System overload",
"Warning 308": "Meter disconnected",
"Warning 309": "Meter L/N reversed",
"Warning 310": "N-PE voltage abnormal",
"Warning 311": "Phase sequence error (auto-adjusts)",
"Warning 400": "Fan failure",
"Warning 401": "Meter abnormal",
"Warning 402": "Optimizer communication abnormal",
"Warning 407": "Over-temperature",
"Warning 408": "NTC temperature sensor broken",
"Warning 411": "Sync signal abnormal",
"Warning 412": "Grid connection requirements not met",
"Warning 500": "Inverter-battery communication failed",
"Warning 501": "Battery disconnected",
"Warning 502": "Battery voltage too high",
"Warning 503": "Battery voltage too low",
"Warning 504": "Battery terminals reversed",
"Warning 505": "Lead-acid battery temp sensor disconnected",
"Warning 506": "Battery temperature out of range",
"Warning 507": "BMS fault: charging/discharging failed",
"Warning 508": "Lithium battery overload protection",
"Warning 509": "BMS communication abnormal",
"Warning 510": "BAT SPD function abnormal",
"Warning 600": "Output DC component bias abnormal",
"Warning 601": "High DC in output voltage",
"Warning 602": "Off-grid output voltage too low",
"Warning 603": "Off-grid output voltage too high",
"Warning 604": "Off-grid output overcurrent",
"Warning 605": "Off-grid bus voltage too low",
"Warning 606": "Off-grid output overload",
"Warning 609": "Balanced circuit abnormal"
};
const errorDescriptionMap: { [key: string]: string } = {
// BMS errors
"Tam": "Tam: Recoverable, BMS temperature too low",
"TaM2": "TaM2: Recoverable, BMS temperature too high",
"Tbm": "Tbm: Recoverable, Battery temperature too low",
"TbM2": "TbM2: Recoverable, Battery temperature too high",
"VBm2": "VBm2: Recoverable, Recoverable: Bus voltage too low",
"VBM2": "VBM2: Recoverable,Recoverable: Bus voltage too high",
"IDM2": "IDM2: Recoverable, Discharge current too high",
"ISOB": "ISOB: Unrecoverable, Electrical insulation failure",
"MSWE": "MSWE: Unrecoverable, Main switch failure",
"FUSE": "FUSE: Unrecoverable, Main fuse blown",
"HTRE": "HTRE: Recoverable, Battery failed to warm up",
"TCPE": "TCPE: Unrecoverable, Temperature sensor failure",
"STRE": "STRE: Recoverable, Voltage measurement circuit fails",
"CME": "CME: Recoverable, Current sensor failure",
"HWFL": "HWFL: Recoverable, BMS hardware failure",
"HWEM": "HWEM: Recoverable, Hardware protection tripped",
"ThM": "ThM: Recoverable, Heatsink temperature too high",
"vsm2": "vsm2: Unrecoverable, Low string voltage failure",
"vsM2": "vsM2: Recoverable, String voltage too high",
"iCM2": "iCM2: Unrecoverable, Charge current too high",
"iDM2": "iDM2: Recoverable, Discharge current too high",
"MID2": "MID2: Recoverable, String voltage unbalance too high",
"HTFS": "HTFS: Recoverable, Unrecoverable: Heater Fuse Blown",
"DATA": "DATA: Recoverable, Unrecoverable: Parameters out of range",
"LMPA": "LMPA: Unrecoverable, String voltages unbalance alarm",
"HEBT": "HEBT: Recoverable, oss of heartbeat",
// Sinexcel errors (ERROR severity - require manual intervention)
"Abnormal grid voltage": "Grid voltage abnormal",
"Abnormal grid frequency": "Grid frequency abnormal",
"Grid voltage phase loss": "Grid phase loss detected",
"Abnormal output voltage": "Output voltage abnormal",
"Abnormal output frequency": "Output frequency abnormal",
"Abnormalnullline": "Null/neutral line abnormal",
"Insulation fault": "Insulation fault detected",
"Leakage protection fault": "Leakage/ground fault protection tripped",
"Auxiliary power fault": "Auxiliary power supply fault",
"Fan fault": "Cooling fan fault",
"Model capacity fault": "Model/capacity configuration fault",
"Abnormal lightning arrester": "Surge protection device abnormal",
"Battery 1not connected": "Battery 1 not connected",
"Battery 2not connected": "Battery 2 not connected",
"AbnormalPV 1current sharing": "PV 1 current sharing abnormal",
"AbnormalPV 2current sharing": "PV 2 current sharing abnormal",
"DC bus voltage unbalance": "DC bus voltage unbalance",
"System output overload": "System output overloaded",
"Inverter overload": "Inverter overloaded",
"Inverter overload timeout": "Inverter overload timeout",
"Battery 1overload timeout": "Battery 1 overload timeout",
"Battery 2overload timeout": "Battery 2 overload timeout",
"DSP 1para meter setting fault": "DSP 1 parameter setting fault",
"DSP 2para meter setting fault": "DSP 2 parameter setting fault",
"DSPversion compatibility fault": "DSP version compatibility fault",
"CPLDversion compatibility fault": "CPLD version compatibility fault",
"CPLD communication fault": "CPLD communication fault",
"DSP communication fault": "DSP communication fault",
"Relayself-checkfails": "Relay self-check failed",
"Abnormal inverter": "Abnormal inverter condition",
"Balancedcircuit overload timeout": "Balance circuit overload timeout",
"PV 1overload timeout": "PV 1 overload timeout",
"PV 2overload timeout": "PV 2 overload timeout",
"Abnormaloff-grid output voltage": "Off-grid output voltage abnormal",
"Parallel communicationalarm": "Parallel communication alarm",
"Inverter relayopen": "Inverter relay open",
"PV 3not connected": "PV 3 not connected",
"PV 4not connected": "PV 4 not connected",
"PV 3overload timeout": "PV 3 overload timeout",
"PV 4overload timeout": "PV 4 overload timeout",
"Abnormal diesel generator voltage": "Diesel generator voltage abnormal",
"Abnormal diesel generator frequency": "Diesel generator frequency abnormal",
"Diesel generator voltageoutof phase": "Diesel generator out of phase",
"Lead battery temperature abnormality": "Lead battery temperature abnormal",
"Abnormal grid current": "Grid current abnormal",
"Generator overload": "Generator overloaded",
"Opencircuitof power grid relay": "Grid relay open circuit",
"Shortcircuitof power grid relay": "Grid relay short circuit",
"generator Relayopencircuit": "Generator relay open circuit",
"generator Relayshortcircuit": "Generator relay short circuit",
"Load power overload": "Load power overload",
"Abnormal leakage self-check": "Leakage self-check abnormal",
// Sinexcel PROTECTION errors (require service - do not restart)
"PV 1power tube fault": "PV 1 power tube fault - Contact Service",
"PV 2power tube fault": "PV 2 power tube fault - Contact Service",
"Battery 1power tube fault": "Battery 1 power tube fault - Contact Service",
"Battery 2power tube fault": "Battery 2 power tube fault - Contact Service",
"Inverter power tube fault": "Inverter power tube fault - Contact Service",
"Hardware bus over voltage": "Hardware bus overvoltage - Contact Service",
"Hardware over current": "Hardware overcurrent - Contact Service",
"DC converter hardware over voltage": "DC converter hardware overvoltage - Contact Service",
"DC converter hardware over current": "DC converter hardware overcurrent - Contact Service",
"Inverter relayshort circuit": "Inverter relay short circuit - Contact Service",
"Reverse meter connection": "Meter connected in reverse - Contact Service",
"PV 3power tube failure": "PV 3 power tube failure - Contact Service",
"PV 4power tube Failure": "PV 4 power tube failure - Contact Service",
"PV 3reverse connection": "PV 3 reverse connection - Contact Service",
"PV 4reverse connection": "PV 4 reverse connection - Contact Service",
"Diesel generator voltage reverse sequence": "Generator phase reversed - Contact Service",
// Growatt errors (PROTECTION severity)
"Error 309": "Grid ROCOF abnormal",
"Error 311": "Export limitation fail-safe",
"Error 400": "DCI bias abnormal",
"Error 402": "High DC in output current",
"Error 404": "Bus voltage sampling abnormal",
"Error 405": "Relay fault",
"Error 408": "Over-temperature protection",
"Error 409": "Bus voltage abnormal",
"Error 411": "Internal communication failure",
"Error 412": "Temperature sensor disconnected",
"Error 413": "IGBT drive fault",
"Error 414": "EEPROM error",
"Error 415": "Auxiliary power supply abnormal",
"Error 416": "DC/AC overcurrent protection",
"Error 417": "Communication protocol mismatch",
"Error 418": "DSP/COM firmware mismatch",
"Error 419": "DSP software/hardware mismatch",
"Error 421": "CPLD abnormal",
"Error 422": "Redundancy sampling inconsistent",
"Error 423": "PWM pass-through signal failure",
"Error 425": "AFCI self-test failure",
"Error 426": "PV current sampling abnormal",
"Error 427": "AC current sampling abnormal",
"Error 429": "BUS soft-boot failure",
"Error 430": "EPO fault",
"Error 431": "Monitoring chip BOOT verification failed",
"Error 500": "BMS-inverter communication failed",
"Error 501": "BMS: battery charge/discharge failed",
"Error 503": "Battery voltage exceeds threshold",
"Error 504": "Battery temperature out of range",
"Error 506": "Battery open-circuited",
"Error 507": "Battery overload protection",
"Error 508": "BUS2 voltage abnormal",
"Error 509": "BAT charge overcurrent protection",
"Error 510": "BAT discharge overcurrent protection",
"Error 511": "BAT soft start failed",
"Error 601": "Off-grid bus voltage low",
"Error 602": "Abnormal voltage at off-grid terminal",
"Error 603": "Off-grid soft start failed",
"Error 604": "Off-grid output voltage abnormal",
"Error 605": "Balanced circuit self-test failed",
"Error 606": "High DC in output voltage",
"Error 608": "Off-grid parallel signal abnormal",
"AFCI Fault": "Arc fault detected - Check PV connections",
"GFCI High": "High leakage current detected",
"PV Voltage High": "DC input voltage exceeds limit"
};
return (
<Container maxWidth="xl">
@ -613,7 +324,7 @@ function Log(props: LogProps) {
<Box sx={{ mt: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: '8px', mb: 1 }}>
<Typography variant="caption" color="text.secondary">
<strong>{demoResult.alarm}</strong>
<strong>{splitCamelCase(demoResult.alarm)}</strong>
</Typography>
{sourceChip(demoResult.source)}
</Box>
@ -667,7 +378,7 @@ function Log(props: LogProps) {
<Box sx={{ padding: '16px' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: '8px', mb: 1 }}>
<Typography variant="subtitle2" fontWeight="bold" color="primary">
{diag.description}
{diag.response.name || alarmDisplayName(diag.description)}
</Typography>
<Typography variant="caption" color="text.secondary">
<FormattedMessage id="lastSeen" defaultMessage="Last seen" />: {diag.lastSeen}
@ -724,7 +435,7 @@ function Log(props: LogProps) {
<Button
variant="contained"
onClick={handleErrorButtonPressed}
sx={{ marginTop: '20px', backgroundColor: errorButtonPressed ? '#808080' : 'default'}}
sx={{ marginTop: '20px', backgroundColor: errorButtonPressed ? '#808080' : 'default', textTransform: 'none' }}
>
<FormattedMessage id="Show Errors" defaultMessage="Show Errors" />
</Button>
@ -914,7 +625,7 @@ function Log(props: LogProps) {
gutterBottom
noWrap
>
{errorDescriptionMap[error.description] || error.description}
{alarmDisplayName(error.description)}
</Typography>
</div>
<div
@ -1035,7 +746,7 @@ function Log(props: LogProps) {
<Button
variant="contained"
onClick={handleWarningButtonPressed}
sx={{ marginTop: '20px', backgroundColor: warningButtonPressed ? '#808080' : 'default'}}
sx={{ marginTop: '20px', backgroundColor: warningButtonPressed ? '#808080' : 'default', textTransform: 'none' }}
>
<FormattedMessage
@ -1229,7 +940,7 @@ function Log(props: LogProps) {
gutterBottom
noWrap
>
{warningDescriptionMap[warning.description] || warning.description}
{alarmDisplayName(warning.description)}
</Typography>
</div>
<div

View File

@ -43,6 +43,9 @@ interface WeeklyReportResponse {
periodEnd: string;
currentWeek: WeeklySummary;
previousWeek: WeeklySummary | null;
totalEnergySaved: number;
totalSavingsCHF: number;
daysEquivalent: number;
selfSufficiencyPercent: number;
selfConsumptionPercent: number;
batteryEfficiencyPercent: number;
@ -54,25 +57,25 @@ interface WeeklyReportResponse {
aiInsight: string;
}
// Matches: time ranges (14:0018:00), times (09:00), decimals (126.4 / 1,3), integers (34)
// Any number in any language gets bolded — no unit matching needed
const BOLD_PATTERN = /(\d{1,2}:\d{2}(?:[\-]\d{1,2}:\d{2})?|\d+[.,]\d+|\d+)/g;
const isBold = (s: string) => /\d/.test(s);
// Renders a bullet line: bolds the "Title" part before the first colon, and numbers with units
function FormattedBullet({ text }: { text: string }) {
const colonIdx = text.indexOf(':');
if (colonIdx > 0) {
const title = text.slice(0, colonIdx);
const rest = text.slice(colonIdx + 1); // e.g. " This week, your system saved 120.9 kWh..."
// Bold numbers+units in the rest
const restParts = rest.split(/(\d+[\d,.]*\s*(?:kWh|CHF|%|days?))/g).map((p, i) =>
/\d+[\d,.]*\s*(?:kWh|CHF|%|days?)/.test(p)
? <strong key={i}>{p}</strong>
: <span key={i}>{p}</span>
const restParts = rest.split(BOLD_PATTERN).map((p, i) =>
isBold(p) ? <strong key={i}>{p}</strong> : <span key={i}>{p}</span>
);
return <><strong>{title}</strong>:{restParts}</>;
}
// No colon — just bold numbers
const parts = text.split(/(\d+[\d,.]*\s*(?:kWh|CHF|%|days?))/g).map((p, i) =>
/\d+[\d,.]*\s*(?:kWh|CHF|%|days?)/.test(p)
? <strong key={i}>{p}</strong>
: <span key={i}>{p}</span>
// No colon — just bold figures
const parts = text.split(BOLD_PATTERN).map((p, i) =>
isBold(p) ? <strong key={i}>{p}</strong> : <span key={i}>{p}</span>
);
return <>{parts}</>;
}
@ -159,6 +162,10 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
const cur = report.currentWeek;
const prev = report.previousWeek;
// Backend: currentWeek = last 7 days, previousWeek = everything before
const currentWeekDayCount = Math.min(7, report.dailyData.length);
const previousWeekDayCount = Math.max(1, report.dailyData.length - currentWeekDayCount);
const formatChange = (pct: number) =>
pct === 0 ? '—' : pct > 0 ? `+${pct.toFixed(1)}%` : `${pct.toFixed(1)}%`;
@ -173,9 +180,9 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
.map((line) => line.replace(/^[\d]+[.)]\s*/, '').replace(/^[-*]\s*/, '').trim())
.filter((line) => line.length > 0);
// Savings-focused KPI values
const solarSavingsKwh = Math.round((cur.totalPvProduction - cur.totalGridExport) * 10) / 10;
const estimatedSavingsCHF = Math.round(solarSavingsKwh * 0.27 * 10) / 10;
// Read pre-computed values from backend — no arithmetic in the frontend
const totalEnergySavedKwh = report.totalEnergySaved;
const totalSavingsCHF = report.totalSavingsCHF;
// Find max value for daily bar chart scaling
const maxDailyValue = Math.max(
@ -270,15 +277,16 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
<Grid item xs={6} sm={3}>
<SavingsCard
label={intl.formatMessage({ id: 'solarEnergyUsed' })}
value={`${solarSavingsKwh} kWh`}
value={`${totalEnergySavedKwh} kWh`}
subtitle={intl.formatMessage({ id: 'solarStayedHome' })}
color="#27ae60"
hint={report.daysEquivalent > 0 ? `${report.daysEquivalent} ${intl.formatMessage({ id: 'daysOfYourUsage' })}` : undefined}
/>
</Grid>
<Grid item xs={6} sm={3}>
<SavingsCard
label={intl.formatMessage({ id: 'estMoneySaved' })}
value={`~${estimatedSavingsCHF} CHF`}
value={`~${totalSavingsCHF} CHF`}
subtitle={intl.formatMessage({ id: 'atCHFRate' })}
color="#2980b9"
/>
@ -329,6 +337,18 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
{prev && <td style={{ textAlign: 'right', color: '#888' }}>{prev.totalConsumption.toFixed(1)} kWh</td>}
{prev && <td style={{ textAlign: 'right', color: changeColor(report.consumptionChangePercent, true) }}>{formatChange(report.consumptionChangePercent)}</td>}
</tr>
<tr style={{ background: '#fafafa' }}>
<td style={{ color: '#888', paddingLeft: '20px', fontSize: '13px' }}>
<FormattedMessage id="avgDailyConsumption" defaultMessage="Avg Daily Consumption" />
</td>
<td style={{ textAlign: 'right', color: '#888', fontSize: '13px' }}>
{(cur.totalConsumption / currentWeekDayCount).toFixed(1)} kWh
</td>
{prev && <td style={{ textAlign: 'right', color: '#bbb', fontSize: '13px' }}>
{(prev.totalConsumption / previousWeekDayCount).toFixed(1)} kWh
</td>}
{prev && <td />}
</tr>
<tr>
<td><FormattedMessage id="gridImport" defaultMessage="Grid Import" /></td>
<td style={{ textAlign: 'right', fontWeight: 'bold' }}>{cur.totalGridImport.toFixed(1)} kWh</td>
@ -423,7 +443,7 @@ function WeeklyReport({ installationId }: WeeklyReportProps) {
);
}
function SavingsCard({ label, value, subtitle, color }: { label: string; value: string; subtitle: string; color: string }) {
function SavingsCard({ label, value, subtitle, color, hint }: { label: string; value: string; subtitle: string; color: string; hint?: string }) {
return (
<Box
sx={{
@ -442,6 +462,11 @@ function SavingsCard({ label, value, subtitle, color }: { label: string; value:
<Typography variant="caption" sx={{ color: '#888' }}>
{subtitle}
</Typography>
{hint && (
<Typography variant="caption" sx={{ display: 'block', color, fontWeight: 'bold', mt: 0.5 }}>
{hint}
</Typography>
)}
</Box>
);
}

View File

@ -33,6 +33,7 @@ export interface Action {
}
export interface DiagnosticResponse {
name: string;
explanation: string;
causes: string[];
nextSteps: string[];

View File

@ -98,14 +98,16 @@
"reportTitle": "Wöchentlicher Leistungsbericht",
"weeklyInsights": "Wöchentliche Einblicke",
"weeklySavings": "Ihre Einsparungen diese Woche",
"solarEnergyUsed": "Genutzte Solarenergie",
"solarStayedHome": "Ihrer Solarenergie blieb zu Hause",
"solarEnergyUsed": "Energie gespart",
"solarStayedHome": "Solar + Batterie, nicht vom Netz",
"daysOfYourUsage": "Tage Ihres Verbrauchs",
"estMoneySaved": "Geschätzte Ersparnisse",
"atCHFRate": "bei 0,27 CHF/kWh Ø",
"solarCoverage": "Solarabdeckung",
"fromSolarSub": "des Verbrauchs aus Solar",
"atCHFRate": "bei 0,39 CHF/kWh Ø",
"solarCoverage": "Eigenversorgung",
"fromSolarSub": "aus Solar + Batterie",
"avgDailyConsumption": "Ø Tagesverbrauch",
"batteryEfficiency": "Batterieeffizienz",
"batteryEffSub": "Energie aus vs. Energie ein",
"batteryEffSub": "Entladung vs. Ladung",
"weeklySummary": "Wöchentliche Zusammenfassung",
"metric": "Kennzahl",
"thisWeek": "Diese Woche",
@ -114,7 +116,7 @@
"consumption": "Verbrauch",
"gridImport": "Netzbezug",
"gridExport": "Netzeinspeisung",
"batteryInOut": "Batterie Ein / Aus",
"batteryInOut": "Batterie Laden / Entladen",
"dailyBreakdown": "Tägliche Aufschlüsselung",
"prevWeek": "(Vorwoche)",
"sendReport": "Bericht senden",
@ -142,5 +144,239 @@
"demo_custom_group": "Benutzerdefiniert (kann Mistral KI verwenden)",
"demo_custom_option": "Benutzerdefinierten Alarm eingeben…",
"demo_custom_placeholder": "z.B. UnknownBatteryFault",
"demo_diagnose_button": "Diagnostizieren"
"demo_diagnose_button": "Diagnostizieren",
"alarm_AbnormalGridVoltage": "Unnormale Netzspannung",
"alarm_AbnormalGridFrequency": "Unnormale Netzfrequenz",
"alarm_InvertedSequenceOfGridVoltage": "Falsche Phasenreihenfolge",
"alarm_GridVoltagePhaseLoss": "Phasenausfall im Netz",
"alarm_AbnormalGridCurrent": "Unnormaler Netzstrom",
"alarm_AbnormalOutputVoltage": "Ungewöhnliche Ausgangsspannung",
"alarm_AbnormalOutputFrequency": "Ungewöhnliche Ausgangsfrequenz",
"alarm_AbnormalNullLine": "Fehlerhafter Nullleiter",
"alarm_AbnormalOffGridOutputVoltage": "Ungewöhnliche Backup-Spannung",
"alarm_ExcessivelyHighAmbientTemperature": "Zu hohe Umgebungstemperatur",
"alarm_ExcessiveRadiatorTemperature": "Überhitzter Kühlkörper",
"alarm_PcbOvertemperature": "Überhitzte Leiterplatte",
"alarm_DcConverterOvertemperature": "Überhitzter DC-Wandler",
"alarm_InverterOvertemperatureAlarm": "Warnung: Überhitzung",
"alarm_InverterOvertemperature": "Wechselrichter überhitzt",
"alarm_DcConverterOvertemperatureAlarm": "Übertemperaturalarm DC-Wandler",
"alarm_InsulationFault": "Isolationsfehler",
"alarm_LeakageProtectionFault": "Leckschutzfehler",
"alarm_AbnormalLeakageSelfCheck": "Anomaler Leckstrom-Selbsttest",
"alarm_PoorGrounding": "Schlechte Erdung",
"alarm_FanFault": "Lüfterfehler",
"alarm_AuxiliaryPowerFault": "Hilfsstromversorgung Fehler",
"alarm_ModelCapacityFault": "Modellkapazitätsfehler",
"alarm_AbnormalLightningArrester": "Überspannungsschutz Fehler",
"alarm_IslandProtection": "Inselbetrieb Schutz",
"alarm_Battery1NotConnected": "Batterie 1 nicht verbunden",
"alarm_Battery1Overvoltage": "Batterie 1 Überspannung",
"alarm_Battery1Undervoltage": "Batterie 1 Unterspannung",
"alarm_Battery1DischargeEnd": "Batterie 1 Entladung beendet",
"alarm_Battery1Inverted": "Batterie 1 Polarität vertauscht",
"alarm_Battery1OverloadTimeout": "Batterie 1 Überlastung",
"alarm_Battery1SoftStartFailure": "Batterie 1 Startfehler",
"alarm_Battery1PowerTubeFault": "Batterie 1 Leistungsteil defekt",
"alarm_Battery1InsufficientPower": "Batterie 1 Leistung unzureichend",
"alarm_Battery1BackupProhibited": "Batterie 1 Backup gesperrt",
"alarm_Battery2NotConnected": "Batterie 2 nicht verbunden",
"alarm_Battery2Overvoltage": "Batterie 2 Überspannung",
"alarm_Battery2Undervoltage": "Batterie 2 Unterspannung",
"alarm_Battery2DischargeEnd": "Batterie 2 Entladung beendet",
"alarm_Battery2Inverted": "Batterie 2 falsch angeschlossen",
"alarm_Battery2OverloadTimeout": "Batterie 2 Überlastung",
"alarm_Battery2SoftStartFailure": "Batterie 2 Startfehler",
"alarm_Battery2PowerTubeFault": "Batterie 2 Leistungsteil defekt",
"alarm_Battery2InsufficientPower": "Batterie 2 Leistung unzureichend",
"alarm_Battery2BackupProhibited": "Batterie 2 Backup gesperrt",
"alarm_LithiumBattery1ChargeForbidden": "Lithium-Batterie 1 Ladeverbot",
"alarm_LithiumBattery1DischargeForbidden": "Lithium-Batterie 1 Entladeverbot",
"alarm_LithiumBattery2ChargeForbidden": "Lithium-Batterie 2 Ladeverbot",
"alarm_LithiumBattery2DischargeForbidden": "Lithium-Batterie 2 Entladeverbot",
"alarm_LithiumBattery1Full": "Lithium-Batterie 1 voll",
"alarm_LithiumBattery1DischargeEnd": "Lithium-Batterie 1 entladen",
"alarm_LithiumBattery2Full": "Lithium-Batterie 2 voll",
"alarm_LithiumBattery2DischargeEnd": "Lithium-Batterie 2 entladen",
"alarm_LeadBatteryTemperatureAbnormality": "Batterietemperatur abnormal",
"alarm_BatteryAccessMethodError": "Batteriezugriffsfehler",
"alarm_Pv1NotAccessed": "PV1 nicht erreichbar",
"alarm_Pv1Overvoltage": "PV1 Überspannung",
"alarm_AbnormalPv1CurrentSharing": "Ungleichmäßiger PV1-Strom",
"alarm_Pv1PowerTubeFault": "PV1 Leistungstubus defekt",
"alarm_Pv1SoftStartFailure": "PV1 Soft-Start fehlgeschlagen",
"alarm_Pv1OverloadTimeout": "PV1-Überlastung",
"alarm_Pv1InsufficientPower": "PV1-Schwacher Strom",
"alarm_Photovoltaic1Overcurrent": "PV1-Überstrom",
"alarm_Pv2NotAccessed": "PV2-Nicht erkannt",
"alarm_Pv2Overvoltage": "PV2-Überspannung",
"alarm_AbnormalPv2CurrentSharing": "Ungewöhnliche Stromverteilung PV2",
"alarm_Pv2PowerTubeFault": "PV2-Leistungsrohrfehler",
"alarm_Pv2SoftStartFailure": "PV2-Softstart fehlgeschlagen",
"alarm_Pv2OverloadTimeout": "PV2-Überlastung Timeout",
"alarm_Pv2InsufficientPower": "Unzureichende Leistung PV2",
"alarm_Pv3NotConnected": "PV3 nicht verbunden",
"alarm_Pv3Overvoltage": "PV3 Überspannung",
"alarm_Pv3AverageCurrentAnomaly": "PV3 Stromanomalie",
"alarm_Pv3PowerTubeFailure": "PV3 Leistungselektronik defekt",
"alarm_Pv3SoftStartFailure": "PV3 Startfehler",
"alarm_Pv3OverloadTimeout": "PV3-Überlastung",
"alarm_Pv3ReverseConnection": "PV3-Falschpolung",
"alarm_Pv4NotConnected": "PV4 Nicht Verbunden",
"alarm_Pv4Overvoltage": "PV4 Überspannung",
"alarm_Pv4AverageCurrentAnomaly": "PV4 Stromanomalie",
"alarm_Pv4PowerTubeFailure": "PV4-Leistungsrohr defekt",
"alarm_Pv4SoftStartFailure": "PV4-Softstart fehlgeschlagen",
"alarm_Pv4OverloadTimeout": "PV4-Überlastung",
"alarm_Pv4ReverseConnection": "PV4 falsch angeschlossen",
"alarm_InsufficientPhotovoltaicPower": "Zu wenig Solarstrom",
"alarm_DcBusOvervoltage": "DC-Bus Überspannung",
"alarm_DcBusUndervoltage": "DC-Bus Unterspannung",
"alarm_DcBusVoltageUnbalance": "DC-Bus Spannungsungleichgewicht",
"alarm_BusSlowOvervoltage": "Langsame DC-Bus Überspannung",
"alarm_HardwareBusOvervoltage": "Hardware DC-Bus Überspannung",
"alarm_BusSoftStartFailure": "Fehler beim sanften Start",
"alarm_InverterPowerTubeFault": "Wechselrichter-Leistungshalbleiter defekt",
"alarm_HardwareOvercurrent": "Hardware-Überstrom",
"alarm_DcConverterOvervoltage": "DC-Wandler Überspannung",
"alarm_DcConverterHardwareOvervoltage": "DC-Wandler Hardware-Überspannung",
"alarm_DcConverterOvercurrent": "DC-Wandler Überstrom",
"alarm_DcConverterHardwareOvercurrent": "DC-Wandler Hardware-Überstrom",
"alarm_DcConverterResonatorOvercurrent": "DC-Wandler Resonanz-Überstrom",
"alarm_SystemOutputOverload": "Systemausgang überlastet",
"alarm_InverterOverload": "Wechselrichter überlastet",
"alarm_InverterOverloadTimeout": "Wechselrichter-Überlastung",
"alarm_LoadPowerOverload": "Überlastung der Lastleistung",
"alarm_BalancedCircuitOverloadTimeout": "Phasenausgleich-Überlastung",
"alarm_InverterSoftStartFailure": "Wechselrichter-Softstart-Fehler",
"alarm_Dsp1ParameterSettingFault": "DSP-Parameter-Fehler",
"alarm_Dsp2ParameterSettingFault": "DSP2 Parameterfehler",
"alarm_DspVersionCompatibilityFault": "DSP-Versionen nicht kompatibel",
"alarm_CpldVersionCompatibilityFault": "CPLD-Version nicht kompatibel",
"alarm_CpldCommunicationFault": "CPLD-Kommunikationsfehler",
"alarm_DspCommunicationFault": "DSP-Kommunikationsfehler",
"alarm_OutputVoltageDcOverlimit": "DC-Spannung zu hoch",
"alarm_OutputCurrentDcOverlimit": "DC-Strom zu hoch",
"alarm_RelaySelfCheckFails": "Relais-Selbsttest fehlgeschlagen",
"alarm_InverterRelayOpen": "Wechselrichter-Relais offen",
"alarm_InverterRelayShortCircuit": "Wechselrichter-Relais Kurzschluss",
"alarm_OpenCircuitOfPowerGridRelay": "Netzrelais offen",
"alarm_ShortCircuitOfPowerGridRelay": "Netzrelais kurzgeschlossen",
"alarm_GeneratorRelayOpenCircuit": "Generatorrelais offen",
"alarm_GeneratorRelayShortCircuit": "Generatorrelais kurzgeschlossen",
"alarm_AbnormalInverter": "Wechselrichter abnormal",
"alarm_ParallelCommunicationAlarm": "Parallelkommunikationsalarm",
"alarm_ParallelModuleMissing": "Parallelmodul fehlt",
"alarm_DuplicateMachineNumbersForParallelModules": "Doppelte Gerätenummern",
"alarm_ParameterConflictInParallelModule": "Parameterkonflikt im Parallelmodul",
"alarm_SystemDerating": "Systemleistung reduziert",
"alarm_PvAccessMethodErrorAlarm": "PV-Zugriffsfehler",
"alarm_ReservedAlarms4": "Reservierter Alarm 4",
"alarm_ReservedAlarms5": "Reservierter Alarm 5",
"alarm_ReverseMeterConnection": "Zähler falsch angeschlossen",
"alarm_InverterSealPulse": "Wechselrichter-Leistungsbegrenzung",
"alarm_AbnormalDieselGeneratorVoltage": "Ungewöhnliche Dieselgenerator-Spannung",
"alarm_AbnormalDieselGeneratorFrequency": "Ungewöhnliche Dieselgenerator-Frequenz",
"alarm_DieselGeneratorVoltageReverseSequence": "Falsche Phasenfolge des Generators",
"alarm_DieselGeneratorVoltageOutOfPhase": "Generator nicht synchronisiert",
"alarm_GeneratorOverload": "Generator überlastet",
"alarm_StringFault": "PV-String-Fehler",
"alarm_PvStringPidQuickConnectAbnormal": "PV-String-Anschluss defekt",
"alarm_DcSpdFunctionAbnormal": "DC-Überspannungsschutz defekt",
"alarm_PvShortCircuited": "PV-String kurzgeschlossen",
"alarm_PvBoostDriverAbnormal": "PV-Boost-Treiber defekt",
"alarm_AcSpdFunctionAbnormal": "AC-Überspannungsschutz defekt",
"alarm_DcFuseBlown": "DC-Sicherung durchgebrannt",
"alarm_DcInputVoltageTooHigh": "DC-Eingangsspannung zu hoch",
"alarm_PvReversed": "PV-Polarität vertauscht",
"alarm_PidFunctionAbnormal": "PID-Schutzfunktion gestört",
"alarm_PvStringDisconnected": "PV-String getrennt",
"alarm_PvStringCurrentUnbalanced": "PV-String Strom unausgeglichen",
"alarm_NoUtilityGrid": "Kein Stromnetz",
"alarm_GridVoltageOutOfRange": "Netzspannung außerhalb des Bereichs",
"alarm_GridFrequencyOutOfRange": "Netzfrequenz außerhalb des Bereichs",
"alarm_Overload": "Überlastung",
"alarm_MeterDisconnected": "Stromzähler getrennt",
"alarm_MeterReverselyConnected": "Zähler falsch angeschlossen",
"alarm_LinePeVoltageAbnormal": "Abnormale PE-Spannung",
"alarm_PhaseSequenceError": "Phasenfolgefehler",
"alarm_FanFailure": "Lüfterausfall",
"alarm_MeterAbnormal": "Störungsanzeige Zähler",
"alarm_OptimizerCommunicationAbnormal": "Kommunikationsstörung Optimierer",
"alarm_OverTemperature": "Überhitzung",
"alarm_OverTemperatureAlarm": "Überhitzungswarnung",
"alarm_NtcTemperatureSensorBroken": "Temperatursensor defekt",
"alarm_SyncSignalAbnormal": "Synchronisationsfehler",
"alarm_GridStartupConditionsNotMet": "Netzstartbedingungen nicht erfüllt",
"alarm_BatteryCommunicationFailure": "Batteriekommunikation fehlgeschlagen",
"alarm_BatteryDisconnected": "Batterie getrennt",
"alarm_BatteryVoltageTooHigh": "Batteriespannung zu hoch",
"alarm_BatteryVoltageTooLow": "Batteriespannung zu niedrig",
"alarm_BatteryReverseConnected": "Batterie falsch angeschlossen",
"alarm_LeadAcidTempSensorDisconnected": "Temperatursensor nicht angeschlossen",
"alarm_BatteryTemperatureOutOfRange": "Batterietemperatur außerhalb des Bereichs",
"alarm_BmsFault": "BMS-Fehler",
"alarm_LithiumBatteryOverload": "Batterie-Überlastung",
"alarm_BmsCommunicationAbnormal": "BMS-Kommunikationsfehler",
"alarm_BatterySpdAbnormal": "Batterie-Überspannungsschutz",
"alarm_OutputDcComponentBiasAbnormal": "DC-Versatz im Ausgang",
"alarm_DcComponentOverHighOutputVoltage": "DC-Komponente zu hohe Ausgangsspannung",
"alarm_OffGridOutputVoltageTooLow": "Netzunabhängige Ausgangsspannung zu niedrig",
"alarm_OffGridOutputVoltageTooHigh": "Netzunabhängige Ausgangsspannung zu hoch",
"alarm_OffGridOutputOverCurrent": "Netzunabhängiger Ausgangsüberstrom",
"alarm_OffGridOutputOverload": "Netzunabhängiger Ausgang überlastet",
"alarm_BalancedCircuitAbnormal": "Phasenausgleich gestört",
"alarm_ExportLimitationFailSafe": "Exportbegrenzung Notaus",
"alarm_DcBiasAbnormal": "DC-Vorspannung abnormal",
"alarm_HighDcComponentOutputCurrent": "Hohe DC-Komponente im Ausgangsstrom",
"alarm_BusVoltageSamplingAbnormal": "Spannungsmessung defekt",
"alarm_RelayFault": "Relaisfehler",
"alarm_BusVoltageAbnormal": "Gleichspannung abnormal",
"alarm_InternalCommunicationFailure": "Interne Kommunikation ausgefallen",
"alarm_TemperatureSensorDisconnected": "Temperatursensor getrennt",
"alarm_IgbtDriveFault": "IGBT-Ansteuerungsfehler",
"alarm_EepromError": "EEPROM-Fehler",
"alarm_AuxiliaryPowerAbnormal": "Hilfsstromversorgung abnormal",
"alarm_DcAcOvercurrentProtection": "Überstromschutz aktiviert",
"alarm_CommunicationProtocolMismatch": "Kommunikationsprotokoll-Fehler",
"alarm_DspComFirmwareMismatch": "Firmware-Inkompatibilität DSP/COM",
"alarm_DspSoftwareHardwareMismatch": "DSP-Software-Hardware-Inkompatibilität",
"alarm_CpldAbnormal": "CPLD-Fehler",
"alarm_RedundancySamplingInconsistent": "Inkonsistente redundante Messungen",
"alarm_PwmPassThroughSignalFailure": "PWM-Signalweg ausgefallen",
"alarm_AfciSelfTestFailure": "AFCI-Selbsttest fehlgeschlagen",
"alarm_PvCurrentSamplingAbnormal": "PV-Strommessung abnormal",
"alarm_AcCurrentSamplingAbnormal": "AC-Strommessung abnormal",
"alarm_BusSoftbootFailure": "DC-Bus-Vorstart fehlgeschlagen",
"alarm_EpoFault": "EPO-Fehler (Notaus)",
"alarm_MonitoringChipBootVerificationFailed": "Überwachungs-Chip Startfehler",
"alarm_BmsCommunicationFailure": "BMS-Kommunikationsfehler",
"alarm_BmsChargeDischargeFailure": "BMS-Lade-/Entladefehler",
"alarm_BatteryVoltageLow": "Batteriespannung zu niedrig",
"alarm_BatteryVoltageHigh": "Batteriespannung zu hoch",
"alarm_BatteryTemperatureAbnormal": "Batterietemperatur ungewöhnlich",
"alarm_BatteryReversed": "Batterie verkehrt herum",
"alarm_BatteryOpenCircuit": "Batteriekreis offen",
"alarm_BatteryOverloadProtection": "Batterieüberlastungsschutz",
"alarm_Bus2VoltageAbnormal": "Bus2-Spannung ungewöhnlich",
"alarm_BatteryChargeOcp": "Batterieladung Überstrom",
"alarm_BatteryDischargeOcp": "Batterieentladung Überstrom",
"alarm_BatterySoftStartFailed": "Batterie-Softstart fehlgeschlagen",
"alarm_EpsOutputShortCircuited": "EPS-Ausgang kurzgeschlossen",
"alarm_OffGridBusVoltageLow": "Netzunabhängige Busspannung zu niedrig",
"alarm_OffGridTerminalVoltageAbnormal": "Abnormale Spannung am Netzausgang",
"alarm_SoftStartFailed": "Sanfter Start fehlgeschlagen",
"alarm_OffGridOutputVoltageAbnormal": "Abnormale Ausgangsspannung im Netzmodus",
"alarm_BalancedCircuitSelfTestFailed": "Ausgleichsschaltungstest fehlgeschlagen",
"alarm_HighDcComponentOutputVoltage": "Hohe Gleichspannungskomponente im Ausgang",
"alarm_OffGridParallelSignalAbnormal": "Parallelsignalstörung",
"alarm_AFCIFault": "Lichtbogenfehler",
"alarm_GFCIHigh": "Erhöhter Fehlerstrom",
"alarm_PVVoltageHigh": "PV-Spannung zu hoch",
"alarm_OffGridBusVoltageTooLow": "Off-Grid-Busspannung zu niedrig",
"Information": "Informationen",
"allInstallations": "Alle Installationen",
"group": "Gruppe",
"groups": "Gruppen",
"requiredOrderNumber": "Pflichtbestellnummer"
}

View File

@ -80,14 +80,16 @@
"reportTitle": "Weekly Performance Report",
"weeklyInsights": "Weekly Insights",
"weeklySavings": "Your Savings This Week",
"solarEnergyUsed": "Solar Energy Used",
"solarStayedHome": "of your solar stayed at home",
"solarEnergyUsed": "Energy Saved",
"solarStayedHome": "solar + battery, not bought from grid",
"daysOfYourUsage": "days of your usage",
"estMoneySaved": "Est. Money Saved",
"atCHFRate": "at 0.27 CHF/kWh avg.",
"solarCoverage": "Solar Coverage",
"fromSolarSub": "of consumption from solar",
"atCHFRate": "at 0.39 CHF/kWh avg.",
"solarCoverage": "Self-Sufficiency",
"fromSolarSub": "from solar + battery",
"avgDailyConsumption": "Avg Daily Consumption",
"batteryEfficiency": "Battery Efficiency",
"batteryEffSub": "energy out vs energy in",
"batteryEffSub": "discharge vs charge",
"weeklySummary": "Weekly Summary",
"metric": "Metric",
"thisWeek": "This Week",
@ -96,7 +98,7 @@
"consumption": "Consumption",
"gridImport": "Grid Import",
"gridExport": "Grid Export",
"batteryInOut": "Battery In / Out",
"batteryInOut": "Battery Charge / Discharge",
"dailyBreakdown": "Daily Breakdown",
"prevWeek": "(prev week)",
"sendReport": "Send Report",

View File

@ -92,14 +92,16 @@
"reportTitle": "Rapport de performance hebdomadaire",
"weeklyInsights": "Aperçus hebdomadaires",
"weeklySavings": "Vos économies cette semaine",
"solarEnergyUsed": "Énergie solaire utilisée",
"solarStayedHome": "de votre solaire est resté à la maison",
"solarEnergyUsed": "Énergie économisée",
"solarStayedHome": "solaire + batterie, non achetée au réseau",
"daysOfYourUsage": "jours de votre consommation",
"estMoneySaved": "Économies estimées",
"atCHFRate": "à 0,27 CHF/kWh moy.",
"solarCoverage": "Couverture solaire",
"fromSolarSub": "de la consommation provenant du solaire",
"atCHFRate": "à 0,39 CHF/kWh moy.",
"solarCoverage": "Autosuffisance",
"fromSolarSub": "du solaire + batterie",
"avgDailyConsumption": "Conso. quotidienne moy.",
"batteryEfficiency": "Efficacité de la batterie",
"batteryEffSub": "énergie sortante vs énergie entrante",
"batteryEffSub": "décharge vs charge",
"weeklySummary": "Résumé hebdomadaire",
"metric": "Métrique",
"thisWeek": "Cette semaine",
@ -108,7 +110,7 @@
"consumption": "Consommation",
"gridImport": "Importation réseau",
"gridExport": "Exportation réseau",
"batteryInOut": "Batterie Entrée / Sortie",
"batteryInOut": "Batterie Charge / Décharge",
"dailyBreakdown": "Répartition quotidienne",
"prevWeek": "(semaine précédente)",
"sendReport": "Envoyer le rapport",
@ -136,5 +138,245 @@
"demo_custom_group": "Personnalisé (peut utiliser Mistral IA)",
"demo_custom_option": "Saisir une alarme personnalisée…",
"demo_custom_placeholder": "ex. UnknownBatteryFault",
"demo_diagnose_button": "Diagnostiquer"
"demo_diagnose_button": "Diagnostiquer",
"alarm_AbnormalGridVoltage": "Tension réseau anormale",
"alarm_AbnormalGridFrequency": "Fréquence réseau anormale",
"alarm_InvertedSequenceOfGridVoltage": "Séquence de tension inversée",
"alarm_GridVoltagePhaseLoss": "Perte de phase réseau",
"alarm_AbnormalGridCurrent": "Courant réseau anormal",
"alarm_AbnormalOutputVoltage": "Tension de sortie anormale",
"alarm_AbnormalOutputFrequency": "Fréquence de sortie anormale",
"alarm_AbnormalNullLine": "Ligne neutre anormale",
"alarm_AbnormalOffGridOutputVoltage": "Tension de sortie hors réseau anormale",
"alarm_ExcessivelyHighAmbientTemperature": "Température ambiante trop élevée",
"alarm_ExcessiveRadiatorTemperature": "Température excessive du radiateur",
"alarm_PcbOvertemperature": "Température excessive PCB",
"alarm_DcConverterOvertemperature": "Température excessive convertisseur DC",
"alarm_InverterOvertemperatureAlarm": "Alarme température onduleur",
"alarm_InverterOvertemperature": "Température onduleur excessive",
"alarm_DcConverterOvertemperatureAlarm": "Alarme surchauffe convertisseur DC",
"alarm_InsulationFault": "Défaut d'isolation",
"alarm_LeakageProtectionFault": "Défaut protection fuite",
"alarm_AbnormalLeakageSelfCheck": "Auto-test fuite anormale",
"alarm_PoorGrounding": "Mise à la terre insuffisante",
"alarm_FanFault": "Défaut du ventilateur",
"alarm_AuxiliaryPowerFault": "Défaut d'alimentation auxiliaire",
"alarm_ModelCapacityFault": "Défaut de configuration",
"alarm_AbnormalLightningArrester": "Paratonnerre défectueux",
"alarm_IslandProtection": "Protection d'îlotage",
"alarm_Battery1NotConnected": "Batterie 1 non connectée",
"alarm_Battery1Overvoltage": "Tension batterie 1 trop élevée",
"alarm_Battery1Undervoltage": "Tension batterie 1 trop basse",
"alarm_Battery1DischargeEnd": "Fin de décharge batterie 1",
"alarm_Battery1Inverted": "Polarité batterie 1 inversée",
"alarm_Battery1OverloadTimeout": "Dépassement de charge Batterie 1",
"alarm_Battery1SoftStartFailure": "Échec démarrage Batterie 1",
"alarm_Battery1PowerTubeFault": "Défaut électronique Batterie 1",
"alarm_Battery1InsufficientPower": "Puissance insuffisante Batterie 1",
"alarm_Battery1BackupProhibited": "Sauvegarde interdite Batterie 1",
"alarm_Battery2NotConnected": "Batterie 2 non connectée",
"alarm_Battery2Overvoltage": "Tension batterie 2 élevée",
"alarm_Battery2Undervoltage": "Tension batterie 2 basse",
"alarm_Battery2DischargeEnd": "Fin décharge batterie 2",
"alarm_Battery2Inverted": "Polarité batterie 2 inversée",
"alarm_Battery2OverloadTimeout": "Dépassement de charge Batterie 2",
"alarm_Battery2SoftStartFailure": "Échec démarrage Batterie 2",
"alarm_Battery2PowerTubeFault": "Défaut électronique Batterie 2",
"alarm_Battery2InsufficientPower": "Puissance insuffisante Batterie 2",
"alarm_Battery2BackupProhibited": "Sauvegarde interdite Batterie 2",
"alarm_LithiumBattery1ChargeForbidden": "Charge batterie lithium 1 interdite",
"alarm_LithiumBattery1DischargeForbidden": "Décharge batterie lithium 1 interdite",
"alarm_LithiumBattery2ChargeForbidden": "Charge batterie lithium 2 interdite",
"alarm_LithiumBattery2DischargeForbidden": "Décharge batterie lithium 2 interdite",
"alarm_LithiumBattery1Full": "Batterie lithium 1 pleine",
"alarm_LithiumBattery1DischargeEnd": "Fin de décharge batterie lithium 1",
"alarm_LithiumBattery2Full": "Batterie lithium 2 pleine",
"alarm_LithiumBattery2DischargeEnd": "Fin de décharge batterie lithium 2",
"alarm_LeadBatteryTemperatureAbnormality": "Température anormale batterie plomb",
"alarm_BatteryAccessMethodError": "Erreur de méthode d'accès batterie",
"alarm_Pv1NotAccessed": "Chaîne PV1 non accessible",
"alarm_Pv1Overvoltage": "Survoltage PV1",
"alarm_AbnormalPv1CurrentSharing": "Partage de courant PV1 anormal",
"alarm_Pv1PowerTubeFault": "Défaut du tube de puissance PV1",
"alarm_Pv1SoftStartFailure": "Échec de démarrage doux PV1",
"alarm_Pv1OverloadTimeout": "Dépassement de charge PV1",
"alarm_Pv1InsufficientPower": "Puissance PV1 insuffisante",
"alarm_Photovoltaic1Overcurrent": "Surintensité PV1",
"alarm_Pv2NotAccessed": "Chaîne PV2 inaccessible",
"alarm_Pv2Overvoltage": "Survoltage PV2",
"alarm_AbnormalPv2CurrentSharing": "Partage de courant anormal PV2",
"alarm_Pv2PowerTubeFault": "Défaillance du tube de puissance PV2",
"alarm_Pv2SoftStartFailure": "Échec de démarrage progressif PV2",
"alarm_Pv2OverloadTimeout": "Dépassement de charge PV2",
"alarm_Pv2InsufficientPower": "Puissance insuffisante PV2",
"alarm_Pv3NotConnected": "PV3 non connecté",
"alarm_Pv3Overvoltage": "Survoltage PV3",
"alarm_Pv3AverageCurrentAnomaly": "Anomalie courant PV3",
"alarm_Pv3PowerTubeFailure": "Défaillance tube PV3",
"alarm_Pv3SoftStartFailure": "Échec démarrage PV3",
"alarm_Pv3OverloadTimeout": "Dépassement de charge PV3",
"alarm_Pv3ReverseConnection": "Connexion inversée PV3",
"alarm_Pv4NotConnected": "Chaîne PV4 non connectée",
"alarm_Pv4Overvoltage": "Survoltage PV4",
"alarm_Pv4AverageCurrentAnomaly": "Anomalie de courant PV4",
"alarm_Pv4PowerTubeFailure": "Défaillance du tube de puissance PV4",
"alarm_Pv4SoftStartFailure": "Échec du démarrage progressif PV4",
"alarm_Pv4OverloadTimeout": "Dépassement de charge PV4",
"alarm_Pv4ReverseConnection": "Connexion inversée PV4",
"alarm_InsufficientPhotovoltaicPower": "Puissance photovoltaïque insuffisante",
"alarm_DcBusOvervoltage": "Tension DC trop élevée",
"alarm_DcBusUndervoltage": "Tension DC trop basse",
"alarm_DcBusVoltageUnbalance": "Déséquilibre tension DC",
"alarm_BusSlowOvervoltage": "Tension DC lente excessive",
"alarm_HardwareBusOvervoltage": "Tension DC critique",
"alarm_BusSoftStartFailure": "Échec démarrage progressif",
"alarm_InverterPowerTubeFault": "Défaut tube de puissance",
"alarm_HardwareOvercurrent": "Surintensité matérielle",
"alarm_DcConverterOvervoltage": "Survoltage convertisseur DC",
"alarm_DcConverterHardwareOvervoltage": "Survoltage matériel convertisseur DC",
"alarm_DcConverterOvercurrent": "Surintensité convertisseur CC",
"alarm_DcConverterHardwareOvercurrent": "Surintensité matérielle convertisseur CC",
"alarm_DcConverterResonatorOvercurrent": "Surintensité résonateur convertisseur CC",
"alarm_SystemOutputOverload": "Surcharge de sortie système",
"alarm_InverterOverload": "Surcharge onduleur",
"alarm_InverterOverloadTimeout": "Dépassement de charge de l'onduleur",
"alarm_LoadPowerOverload": "Surcharge de puissance de charge",
"alarm_BalancedCircuitOverloadTimeout": "Dépassement de charge du circuit équilibré",
"alarm_InverterSoftStartFailure": "Échec de démarrage progressif de l'onduleur",
"alarm_Dsp1ParameterSettingFault": "Défaillance de paramétrage DSP 1",
"alarm_Dsp2ParameterSettingFault": "Paramètre DSP2 incorrect",
"alarm_DspVersionCompatibilityFault": "Incompatibilité version DSP",
"alarm_CpldVersionCompatibilityFault": "Incompatibilité version CPLD",
"alarm_CpldCommunicationFault": "Échec communication CPLD",
"alarm_DspCommunicationFault": "Échec communication DSP",
"alarm_OutputVoltageDcOverlimit": "Tension de sortie DC excessive",
"alarm_OutputCurrentDcOverlimit": "Courant de sortie DC excessif",
"alarm_RelaySelfCheckFails": "Auto-test relais échoué",
"alarm_InverterRelayOpen": "Relais de l'onduleur ouvert",
"alarm_InverterRelayShortCircuit": "Relais de l'onduleur en court-circuit",
"alarm_OpenCircuitOfPowerGridRelay": "Relais du réseau ouvert",
"alarm_ShortCircuitOfPowerGridRelay": "Court-circuit du relais réseau",
"alarm_GeneratorRelayOpenCircuit": "Relais du générateur ouvert",
"alarm_GeneratorRelayShortCircuit": "Court-circuit du relais générateur",
"alarm_AbnormalInverter": "Onduleur anormal",
"alarm_ParallelCommunicationAlarm": "Alarme de communication parallèle",
"alarm_ParallelModuleMissing": "Module parallèle manquant",
"alarm_DuplicateMachineNumbersForParallelModules": "Numéros de machine en double",
"alarm_ParameterConflictInParallelModule": "Conflit de paramètres parallèle",
"alarm_SystemDerating": "Réduction de puissance du système",
"alarm_PvAccessMethodErrorAlarm": "Erreur méthode d'accès PV",
"alarm_ReservedAlarms4": "Alarme réservée 4",
"alarm_ReservedAlarms5": "Alarme réservée 5",
"alarm_ReverseMeterConnection": "Connexion du compteur inversée",
"alarm_InverterSealPulse": "Impulsion de scellement de l'onduleur",
"alarm_AbnormalDieselGeneratorVoltage": "Tension anormale du générateur diesel",
"alarm_AbnormalDieselGeneratorFrequency": "Fréquence anormale du générateur diesel",
"alarm_DieselGeneratorVoltageReverseSequence": "Séquence de phase inversée du générateur",
"alarm_DieselGeneratorVoltageOutOfPhase": "Déphasage du générateur",
"alarm_GeneratorOverload": "Surcharge du générateur",
"alarm_StringFault": "Défaut de chaîne",
"alarm_PvStringPidQuickConnectAbnormal": "Connexion rapide anormale",
"alarm_DcSpdFunctionAbnormal": "Problème de protection DC",
"alarm_PvShortCircuited": "Court-circuit PV",
"alarm_PvBoostDriverAbnormal": "Problème de convertisseur",
"alarm_AcSpdFunctionAbnormal": "Problème de protection contre les surtensions AC",
"alarm_DcFuseBlown": "Fusible DC grillé",
"alarm_DcInputVoltageTooHigh": "Tension DC d'entrée trop élevée",
"alarm_PvReversed": "Polarité PV inversée",
"alarm_PidFunctionAbnormal": "Problème de fonction PID",
"alarm_PvStringDisconnected": "Chaîne PV déconnectée",
"alarm_PvStringCurrentUnbalanced": "Déséquilibre de courant PV",
"alarm_NoUtilityGrid": "Réseau électrique absent",
"alarm_GridVoltageOutOfRange": "Tension réseau hors plage",
"alarm_GridFrequencyOutOfRange": "Fréquence réseau hors plage",
"alarm_Overload": "Surcharge",
"alarm_MeterDisconnected": "Compteur déconnecté",
"alarm_MeterReverselyConnected": "Compteur inversé",
"alarm_LinePeVoltageAbnormal": "Tension anormale",
"alarm_PhaseSequenceError": "Séquence de phase erronée",
"alarm_FanFailure": "Défaillance du ventilateur",
"alarm_MeterAbnormal": "Compteur anormal",
"alarm_OptimizerCommunicationAbnormal": "Communication optimiseur anormale",
"alarm_OverTemperature": "Température excessive",
"alarm_OverTemperatureAlarm": "Alarme température élevée",
"alarm_NtcTemperatureSensorBroken": "Capteur de température défectueux",
"alarm_SyncSignalAbnormal": "Signal de synchronisation anormal",
"alarm_GridStartupConditionsNotMet": "Conditions de démarrage réseau non remplies",
"alarm_BatteryCommunicationFailure": "Échec de communication batterie",
"alarm_BatteryDisconnected": "Batterie déconnectée",
"alarm_BatteryVoltageTooHigh": "Tension batterie trop élevée",
"alarm_BatteryVoltageTooLow": "Tension batterie trop basse",
"alarm_BatteryReverseConnected": "Batterie branchée à l'envers",
"alarm_LeadAcidTempSensorDisconnected": "Capteur température batterie plomb désactivé",
"alarm_BatteryTemperatureOutOfRange": "Température batterie hors plage",
"alarm_BmsFault": "Défaillance BMS",
"alarm_LithiumBatteryOverload": "Surcharge batterie lithium",
"alarm_BmsCommunicationAbnormal": "Communication BMS anormale",
"alarm_BatterySpdAbnormal": "Défaillance SPD batterie",
"alarm_OutputDcComponentBiasAbnormal": "Biais DC de sortie anormal",
"alarm_DcComponentOverHighOutputVoltage": "Tension de sortie trop élevée",
"alarm_OffGridOutputVoltageTooLow": "Tension de sortie hors réseau trop basse",
"alarm_OffGridOutputVoltageTooHigh": "Tension de sortie hors réseau trop élevée",
"alarm_OffGridOutputOverCurrent": "Courant de sortie hors réseau trop élevé",
"alarm_OffGridOutputOverload": "Surcharge sortie hors réseau",
"alarm_BalancedCircuitAbnormal": "Circuit équilibré anormal",
"alarm_ExportLimitationFailSafe": "Sécurité limite d'exportation",
"alarm_DcBiasAbnormal": "Biais DC anormal",
"alarm_HighDcComponentOutputCurrent": "Composante DC élevée courant de sortie",
"alarm_BusVoltageSamplingAbnormal": "Tension d'alimentation anormale",
"alarm_RelayFault": "Défaillance du relais",
"alarm_BusVoltageAbnormal": "Tension d'alimentation anormale",
"alarm_InternalCommunicationFailure": "Échec de communication interne",
"alarm_TemperatureSensorDisconnected": "Capteur de température déconnecté",
"alarm_IgbtDriveFault": "Défaillance de l'IGBT",
"alarm_EepromError": "Erreur EEPROM",
"alarm_AuxiliaryPowerAbnormal": "Alimentation auxiliaire anormale",
"alarm_DcAcOvercurrentProtection": "Protection contre les surintensités",
"alarm_CommunicationProtocolMismatch": "Incompatibilité de protocole",
"alarm_DspComFirmwareMismatch": "Incompatibilité firmware DSP/COM",
"alarm_DspSoftwareHardwareMismatch": "Incompatibilité logiciel DSP/matériel",
"alarm_CpldAbnormal": "CPLD anormal",
"alarm_RedundancySamplingInconsistent": "Échantillonnage redondant incohérent",
"alarm_PwmPassThroughSignalFailure": "Échec signal PWM",
"alarm_AfciSelfTestFailure": "Échec auto-test AFCI",
"alarm_PvCurrentSamplingAbnormal": "Mesure PV anormale",
"alarm_AcCurrentSamplingAbnormal": "Mesure AC anormale",
"alarm_BusSoftbootFailure": "Échec démarrage DC",
"alarm_EpoFault": "Défaillance EPO",
"alarm_MonitoringChipBootVerificationFailed": "Échec vérification démarrage",
"alarm_BmsCommunicationFailure": "Échec communication BMS",
"alarm_BmsChargeDischargeFailure": "Échec charge/décharge BMS",
"alarm_BatteryVoltageLow": "Tension batterie faible",
"alarm_BatteryVoltageHigh": "Tension batterie élevée",
"alarm_BatteryTemperatureAbnormal": "Température anormale de la batterie",
"alarm_BatteryReversed": "Batterie inversée",
"alarm_BatteryOpenCircuit": "Circuit batterie ouvert",
"alarm_BatteryOverloadProtection": "Protection contre la surcharge",
"alarm_Bus2VoltageAbnormal": "Tension anormale Bus2",
"alarm_BatteryChargeOcp": "Surintensité charge batterie",
"alarm_BatteryDischargeOcp": "Surintensité décharge batterie",
"alarm_BatterySoftStartFailed": "Démarrage en douceur échoué",
"alarm_EpsOutputShortCircuited": "Circuit de secours en court-circuit",
"alarm_OffGridBusVoltageLow": "Tension bus hors réseau basse",
"alarm_OffGridTerminalVoltageAbnormal": "Tension anormale terminal hors réseau",
"alarm_SoftStartFailed": "Démarrage progressif échoué",
"alarm_OffGridOutputVoltageAbnormal": "Tension de sortie hors réseau anormale",
"alarm_BalancedCircuitSelfTestFailed": "Autotest circuit équilibré échoué",
"alarm_HighDcComponentOutputVoltage": "Tension de sortie à composante CC élevée",
"alarm_OffGridParallelSignalAbnormal": "Signal parallèle hors réseau anormal",
"alarm_AFCIFault": "Défaillance AFCI",
"alarm_GFCIHigh": "Courant de défaut élevé",
"alarm_PVVoltageHigh": "Tension PV élevée",
"alarm_OffGridBusVoltageTooLow": "Tension du bus hors réseau trop faible",
"Information": "Informations",
"allInstallations": "Toutes les installations",
"group": "Groupe",
"groups": "Groupes",
"requiredOrderNumber": "Numéro de commande requis",
"addNewChild": "Ajouter un sous-élément",
"addNewDialogButton": "Ajouter un bouton de dialogue",
"groupTabs": "Groupes",
"groupTree": "Arborescence de groupes",
"installationTabs": "Installations",
"navigationTabs": "Navigation"
}

View File

@ -103,14 +103,16 @@
"reportTitle": "Rapporto settimanale sulle prestazioni",
"weeklyInsights": "Approfondimenti settimanali",
"weeklySavings": "I tuoi risparmi questa settimana",
"solarEnergyUsed": "Energia solare utilizzata",
"solarStayedHome": "della tua energia solare è rimasta a casa",
"solarEnergyUsed": "Energia risparmiata",
"solarStayedHome": "solare + batteria, non acquistata dalla rete",
"daysOfYourUsage": "giorni del tuo consumo",
"estMoneySaved": "Risparmio stimato",
"atCHFRate": "a 0,27 CHF/kWh media",
"solarCoverage": "Copertura solare",
"fromSolarSub": "del consumo da fonte solare",
"atCHFRate": "a 0,39 CHF/kWh media",
"solarCoverage": "Autosufficienza",
"fromSolarSub": "da solare + batteria",
"avgDailyConsumption": "Consumo medio giornaliero",
"batteryEfficiency": "Efficienza della batteria",
"batteryEffSub": "energia in uscita vs energia in entrata",
"batteryEffSub": "scarica vs carica",
"weeklySummary": "Riepilogo settimanale",
"metric": "Metrica",
"thisWeek": "Questa settimana",
@ -119,7 +121,7 @@
"consumption": "Consumo",
"gridImport": "Importazione rete",
"gridExport": "Esportazione rete",
"batteryInOut": "Batteria Entrata / Uscita",
"batteryInOut": "Batteria Carica / Scarica",
"dailyBreakdown": "Ripartizione giornaliera",
"prevWeek": "(settimana precedente)",
"sendReport": "Invia rapporto",
@ -147,5 +149,234 @@
"demo_custom_group": "Personalizzato (potrebbe usare Mistral IA)",
"demo_custom_option": "Inserisci allarme personalizzato…",
"demo_custom_placeholder": "es. UnknownBatteryFault",
"demo_diagnose_button": "Diagnostica"
"demo_diagnose_button": "Diagnostica",
"alarm_AbnormalGridVoltage": "Tensione di rete anomala",
"alarm_AbnormalGridFrequency": "Frequenza di rete anomala",
"alarm_InvertedSequenceOfGridVoltage": "Sequenza di fase invertita",
"alarm_GridVoltagePhaseLoss": "Fase di rete mancante",
"alarm_AbnormalGridCurrent": "Corrente di rete anomala",
"alarm_AbnormalOutputVoltage": "Tensione di uscita anomala",
"alarm_AbnormalOutputFrequency": "Frequenza di uscita anomala",
"alarm_AbnormalNullLine": "Linea neutra anomala",
"alarm_AbnormalOffGridOutputVoltage": "Tensione di uscita off-grid anomala",
"alarm_ExcessivelyHighAmbientTemperature": "Temperatura ambiente eccessivamente alta",
"alarm_ExcessiveRadiatorTemperature": "Temperatura radiatore eccessiva",
"alarm_PcbOvertemperature": "Temperatura PCB eccessiva",
"alarm_DcConverterOvertemperature": "Temperatura convertitore DC eccessiva",
"alarm_InverterOvertemperatureAlarm": "Allarme temperatura inverter elevata",
"alarm_InverterOvertemperature": "Temperatura inverter eccessiva",
"alarm_DcConverterOvertemperatureAlarm": "Allarme sovratemperatura convertitore DC",
"alarm_InsulationFault": "Guasto isolamento",
"alarm_LeakageProtectionFault": "Guasto protezione dispersione",
"alarm_AbnormalLeakageSelfCheck": "Autocontrollo dispersione anomalo",
"alarm_PoorGrounding": "Messa a terra insufficiente",
"alarm_FanFault": "Guasto Ventola",
"alarm_AuxiliaryPowerFault": "Guasto Alimentazione Ausiliaria",
"alarm_ModelCapacityFault": "Guasto Configurazione Modello",
"alarm_AbnormalLightningArrester": "Parasurtense Anomalo",
"alarm_IslandProtection": "Protezione Isola",
"alarm_Battery1NotConnected": "Batteria 1 non collegata",
"alarm_Battery1Overvoltage": "Batteria 1 sovratensione",
"alarm_Battery1Undervoltage": "Batteria 1 sottotensione",
"alarm_Battery1DischargeEnd": "Batteria 1 scarica",
"alarm_Battery1Inverted": "Batteria 1 polarità invertita",
"alarm_Battery1OverloadTimeout": "Timeout sovraccarico batteria 1",
"alarm_Battery1SoftStartFailure": "Avvio morbido fallito batteria 1",
"alarm_Battery1PowerTubeFault": "Guasto modulo potenza batteria 1",
"alarm_Battery1InsufficientPower": "Potenza insufficiente batteria 1",
"alarm_Battery1BackupProhibited": "Backup vietato batteria 1",
"alarm_Battery2NotConnected": "Batteria 2 non collegata",
"alarm_Battery2Overvoltage": "Sovratensione batteria 2",
"alarm_Battery2Undervoltage": "Sottotensione batteria 2",
"alarm_Battery2DischargeEnd": "Fine scarica batteria 2",
"alarm_Battery2Inverted": "Polarità invertita batteria 2",
"alarm_Battery2OverloadTimeout": "Timeout sovraccarico Batteria 2",
"alarm_Battery2SoftStartFailure": "Avvio morbido fallito Batteria 2",
"alarm_Battery2PowerTubeFault": "Guasto modulo potenza Batteria 2",
"alarm_Battery2InsufficientPower": "Potenza insufficiente Batteria 2",
"alarm_Battery2BackupProhibited": "Backup vietato Batteria 2",
"alarm_LithiumBattery1ChargeForbidden": "Carica Batteria Litio 1 Bloccata",
"alarm_LithiumBattery1DischargeForbidden": "Scarica Batteria Litio 1 Bloccata",
"alarm_LithiumBattery2ChargeForbidden": "Carica Batteria Litio 2 Bloccata",
"alarm_LithiumBattery2DischargeForbidden": "Scarica Batteria Litio 2 Bloccata",
"alarm_LithiumBattery1Full": "Batteria Litio 1 Piena",
"alarm_LithiumBattery1DischargeEnd": "Fine scarica batteria litio 1",
"alarm_LithiumBattery2Full": "Batteria litio 2 piena",
"alarm_LithiumBattery2DischargeEnd": "Fine scarica batteria litio 2",
"alarm_LeadBatteryTemperatureAbnormality": "Temperatura batteria piombo anomala",
"alarm_BatteryAccessMethodError": "Errore metodo accesso batteria",
"alarm_Pv1NotAccessed": "PV1 non accessibile",
"alarm_Pv1Overvoltage": "Sovratensione PV1",
"alarm_AbnormalPv1CurrentSharing": "Corrente PV1 anomala",
"alarm_Pv1PowerTubeFault": "Guasto tubo di potenza PV1",
"alarm_Pv1SoftStartFailure": "Avvio morbido PV1 fallito",
"alarm_Pv1OverloadTimeout": "Sovraccarico PV1",
"alarm_Pv1InsufficientPower": "Bassa potenza PV1",
"alarm_Photovoltaic1Overcurrent": "Sovracorrente PV1",
"alarm_Pv2NotAccessed": "PV2 non rilevato",
"alarm_Pv2Overvoltage": "Sovratensione PV2",
"alarm_AbnormalPv2CurrentSharing": "Condivisione Corrente PV2 Anomala",
"alarm_Pv2PowerTubeFault": "Guasto Tubo di Potenza PV2",
"alarm_Pv2SoftStartFailure": "Avvio Morbido PV2 Fallito",
"alarm_Pv2OverloadTimeout": "Sovraccarico PV2 Timeout",
"alarm_Pv2InsufficientPower": "Potenza Insufficiente PV2",
"alarm_Pv3NotConnected": "PV3 non connesso",
"alarm_Pv3Overvoltage": "Sovratensione PV3",
"alarm_Pv3AverageCurrentAnomaly": "Corrente PV3 anomala",
"alarm_Pv3PowerTubeFailure": "Guasto tubo di potenza PV3",
"alarm_Pv3SoftStartFailure": "Avvio morbido PV3 fallito",
"alarm_Pv3OverloadTimeout": "Sovraccarico PV3",
"alarm_Pv3ReverseConnection": "Connessione Inversa PV3",
"alarm_Pv4NotConnected": "PV4 Non Collegato",
"alarm_Pv4Overvoltage": "Sovratensione PV4",
"alarm_Pv4AverageCurrentAnomaly": "Anomalia Corrente PV4",
"alarm_Pv4PowerTubeFailure": "Guasto Tubo di Potenza PV4",
"alarm_Pv4SoftStartFailure": "Avvio Morbido PV4 Fallito",
"alarm_Pv4OverloadTimeout": "Sovraccarico PV4",
"alarm_Pv4ReverseConnection": "Connessione Inversa PV4",
"alarm_InsufficientPhotovoltaicPower": "Potenza Fotovoltaica Insufficiente",
"alarm_DcBusOvervoltage": "Sovratensione Bus DC",
"alarm_DcBusUndervoltage": "Sottotensione Bus DC",
"alarm_DcBusVoltageUnbalance": "Squilibrio Tensione Bus DC",
"alarm_BusSlowOvervoltage": "Sovratensione Lenta Bus",
"alarm_HardwareBusOvervoltage": "Sovratensione Critica Bus",
"alarm_BusSoftStartFailure": "Avvio morbido fallito",
"alarm_InverterPowerTubeFault": "Guasto al modulo di potenza",
"alarm_HardwareOvercurrent": "Sovracorrente hardware",
"alarm_DcConverterOvervoltage": "Sovratensione convertitore DC",
"alarm_DcConverterHardwareOvervoltage": "Sovratensione hardware convertitore",
"alarm_DcConverterOvercurrent": "Sovraccarico convertitore DC",
"alarm_DcConverterHardwareOvercurrent": "Sovraccarico hardware convertitore DC",
"alarm_DcConverterResonatorOvercurrent": "Sovraccarico risonatore convertitore DC",
"alarm_SystemOutputOverload": "Sovraccarico uscita sistema",
"alarm_InverterOverload": "Sovraccarico inverter",
"alarm_InverterOverloadTimeout": "Sovraccarico Inverter",
"alarm_LoadPowerOverload": "Sovraccarico Carico",
"alarm_BalancedCircuitOverloadTimeout": "Sovraccarico Circuito Bilanciato",
"alarm_InverterSoftStartFailure": "Avvio Inverter Fallito",
"alarm_Dsp1ParameterSettingFault": "Parametri DSP Errati",
"alarm_Dsp2ParameterSettingFault": "Errore configurazione parametri DSP 2",
"alarm_DspVersionCompatibilityFault": "Errore compatibilità versione DSP",
"alarm_CpldVersionCompatibilityFault": "Errore compatibilità versione CPLD",
"alarm_CpldCommunicationFault": "Errore comunicazione CPLD",
"alarm_DspCommunicationFault": "Errore comunicazione DSP",
"alarm_OutputVoltageDcOverlimit": "Tensione DC in uscita eccessiva",
"alarm_OutputCurrentDcOverlimit": "Corrente DC in uscita eccessiva",
"alarm_RelaySelfCheckFails": "Autotest relè fallito",
"alarm_InverterRelayOpen": "Relè inverter aperto",
"alarm_InverterRelayShortCircuit": "Relè inverter in cortocircuito",
"alarm_OpenCircuitOfPowerGridRelay": "Relè di rete aperto",
"alarm_ShortCircuitOfPowerGridRelay": "Relè di rete in cortocircuito",
"alarm_GeneratorRelayOpenCircuit": "Relè del generatore aperto",
"alarm_GeneratorRelayShortCircuit": "Relè del generatore in cortocircuito",
"alarm_AbnormalInverter": "Inverter anomalo",
"alarm_ParallelCommunicationAlarm": "Allarme Comunicazione Parallela",
"alarm_ParallelModuleMissing": "Modulo Parallelo Mancante",
"alarm_DuplicateMachineNumbersForParallelModules": "Numeri Duplicati Moduli Paralleli",
"alarm_ParameterConflictInParallelModule": "Conflitto Parametri Modulo Parallelo",
"alarm_SystemDerating": "Riduzione Prestazioni Sistema",
"alarm_PvAccessMethodErrorAlarm": "Errore Metodo Accesso PV",
"alarm_ReservedAlarms4": "Allarme Riservato 4",
"alarm_ReservedAlarms5": "Allarme Riservato 5",
"alarm_ReverseMeterConnection": "Contatore Inverso",
"alarm_InverterSealPulse": "Impulso Sigillo Inverter",
"alarm_AbnormalDieselGeneratorVoltage": "Tensione anomala del generatore",
"alarm_AbnormalDieselGeneratorFrequency": "Frequenza anomala del generatore",
"alarm_DieselGeneratorVoltageReverseSequence": "Sequenza di fase invertita",
"alarm_DieselGeneratorVoltageOutOfPhase": "Generatore fuori fase",
"alarm_GeneratorOverload": "Sovraccarico del generatore",
"alarm_StringFault": "Guasto Stringa",
"alarm_PvStringPidQuickConnectAbnormal": "Connessione Rapida Anomala",
"alarm_DcSpdFunctionAbnormal": "Protezione SPD Anomala",
"alarm_PvShortCircuited": "Cortocircuito PV",
"alarm_PvBoostDriverAbnormal": "Driver di Boost Anomalo",
"alarm_AcSpdFunctionAbnormal": "Funzione SPD AC anomala",
"alarm_DcFuseBlown": "Fusibile DC saltato",
"alarm_DcInputVoltageTooHigh": "Tensione DC in ingresso troppo alta",
"alarm_PvReversed": "Polarità PV invertita",
"alarm_PidFunctionAbnormal": "Funzione PID anomala",
"alarm_PvStringDisconnected": "Stringa PV disconnessa",
"alarm_PvStringCurrentUnbalanced": "Corrente stringa PV squilibrata",
"alarm_NoUtilityGrid": "Nessuna rete elettrica",
"alarm_GridVoltageOutOfRange": "Tensione rete fuori limite",
"alarm_GridFrequencyOutOfRange": "Frequenza rete fuori limite",
"alarm_Overload": "Sovraccarico",
"alarm_MeterDisconnected": "Contatore scollegato",
"alarm_MeterReverselyConnected": "Contatore collegato al contrario",
"alarm_LinePeVoltageAbnormal": "Tensione anomala PE",
"alarm_PhaseSequenceError": "Errore sequenza fase",
"alarm_FanFailure": "Guasto Ventilatore",
"alarm_MeterAbnormal": "Contatore Anomalo",
"alarm_OptimizerCommunicationAbnormal": "Comunicazione Ottimizzatore Anomala",
"alarm_OverTemperature": "Temperatura Eccessiva",
"alarm_OverTemperatureAlarm": "Allarme Temperatura Eccessiva",
"alarm_NtcTemperatureSensorBroken": "Sensore temperatura guasto",
"alarm_SyncSignalAbnormal": "Segnale di sincronizzazione anomalo",
"alarm_GridStartupConditionsNotMet": "Condizioni di avvio rete non soddisfatte",
"alarm_BatteryCommunicationFailure": "Comunicazione batteria fallita",
"alarm_BatteryDisconnected": "Batteria scollegata",
"alarm_BatteryVoltageTooHigh": "Tensione batteria troppo alta",
"alarm_BatteryVoltageTooLow": "Tensione batteria troppo bassa",
"alarm_BatteryReverseConnected": "Batteria collegata al contrario",
"alarm_LeadAcidTempSensorDisconnected": "Sensore temperatura piombo acido scollegato",
"alarm_BatteryTemperatureOutOfRange": "Temperatura batteria fuori range",
"alarm_BmsFault": "Guasto BMS",
"alarm_LithiumBatteryOverload": "Sovraccarico Batteria Litio",
"alarm_BmsCommunicationAbnormal": "Comunicazione BMS Anomala",
"alarm_BatterySpdAbnormal": "SPD Batteria Anomalo",
"alarm_OutputDcComponentBiasAbnormal": "Bias DC di Uscita Anomalo",
"alarm_DcComponentOverHighOutputVoltage": "Tensione di uscita DC troppo alta",
"alarm_OffGridOutputVoltageTooLow": "Tensione di uscita off-grid troppo bassa",
"alarm_OffGridOutputVoltageTooHigh": "Tensione di uscita off-grid troppo alta",
"alarm_OffGridOutputOverCurrent": "Corrente di uscita off-grid troppo alta",
"alarm_OffGridOutputOverload": "Sovraccarico Uscita Off-Grid",
"alarm_BalancedCircuitAbnormal": "Circuiti Squilibrati Anomali",
"alarm_ExportLimitationFailSafe": "Limite Esportazione Sicurezza",
"alarm_DcBiasAbnormal": "Bias DC Anomalo",
"alarm_HighDcComponentOutputCurrent": "Alta Componente DC Corrente",
"alarm_BusVoltageSamplingAbnormal": "Campionamento tensione anomalo",
"alarm_RelayFault": "Guasto al relè",
"alarm_BusVoltageAbnormal": "Tensione bus anomala",
"alarm_InternalCommunicationFailure": "Comunicazione interna guasta",
"alarm_TemperatureSensorDisconnected": "Sensore temperatura scollegato",
"alarm_IgbtDriveFault": "Guasto guida IGBT",
"alarm_EepromError": "Errore EEPROM",
"alarm_AuxiliaryPowerAbnormal": "Alimentazione ausiliaria anomala",
"alarm_DcAcOvercurrentProtection": "Protezione sovracorrente DC/AC",
"alarm_CommunicationProtocolMismatch": "Protocollo di comunicazione non corrispondente",
"alarm_DspComFirmwareMismatch": "Incompatibilità firmware DSP/COM",
"alarm_DspSoftwareHardwareMismatch": "Incompatibilità software/hardware DSP",
"alarm_CpldAbnormal": "Anomalia CPLD",
"alarm_RedundancySamplingInconsistent": "Campionamento ridondante inconsistente",
"alarm_PwmPassThroughSignalFailure": "Guasto segnale PWM",
"alarm_AfciSelfTestFailure": "Test AFCI fallito",
"alarm_PvCurrentSamplingAbnormal": "Corrente PV anomala",
"alarm_AcCurrentSamplingAbnormal": "Corrente AC anomala",
"alarm_BusSoftbootFailure": "Avvio bus fallito",
"alarm_EpoFault": "Guasto EPO",
"alarm_MonitoringChipBootVerificationFailed": "Verifica avvio chip monitoraggio fallita",
"alarm_BmsCommunicationFailure": "Comunicazione BMS fallita",
"alarm_BmsChargeDischargeFailure": "Carica/scarica BMS bloccata",
"alarm_BatteryVoltageLow": "Tensione batteria bassa",
"alarm_BatteryVoltageHigh": "Tensione batteria alta",
"alarm_BatteryTemperatureAbnormal": "Temperatura batteria anomala",
"alarm_BatteryReversed": "Batteria invertita",
"alarm_BatteryOpenCircuit": "Circolazione batteria aperta",
"alarm_BatteryOverloadProtection": "Protezione sovraccarico batteria",
"alarm_Bus2VoltageAbnormal": "Tensione Bus2 anomala",
"alarm_BatteryChargeOcp": "Sovraccarico carica batteria",
"alarm_BatteryDischargeOcp": "Sovraccarico scarica batteria",
"alarm_BatterySoftStartFailed": "Avvio batteria fallito",
"alarm_EpsOutputShortCircuited": "Uscita EPS in cortocircuito",
"alarm_OffGridBusVoltageLow": "Tensione bus off-grid bassa",
"alarm_OffGridTerminalVoltageAbnormal": "Tensione terminale anomala",
"alarm_SoftStartFailed": "Avvio morbido fallito",
"alarm_OffGridOutputVoltageAbnormal": "Tensione di uscita anomala",
"alarm_BalancedCircuitSelfTestFailed": "Autotest circuito bilanciato fallito",
"alarm_HighDcComponentOutputVoltage": "Tensione di uscita DC elevata",
"alarm_OffGridParallelSignalAbnormal": "Segnale parallelo off-grid anomalo",
"alarm_AFCIFault": "Guasto AFCI",
"alarm_GFCIHigh": "Corrente di guasto a terra elevata",
"alarm_PVVoltageHigh": "Tensione PV elevata",
"alarm_OffGridBusVoltageTooLow": "Tensione Bus Fuori Rete Troppo Bassa"
}