diff --git a/src/Client/Pages/BMS.razor b/src/Client/Pages/BMS.razor index 5a40891..3db61f7 100644 --- a/src/Client/Pages/BMS.razor +++ b/src/Client/Pages/BMS.razor @@ -5,7 +5,7 @@ JK BMS Status - + @if (status is not null) { @@ -24,8 +24,8 @@
@status.CapacityPct%
-
- @Math.Round(status.AvailableCapacity, 1) Ah / @status.PackCapacity Ah +
+ @(GetPackCapacity())
@@ -41,14 +41,14 @@
@($"{status.CRate:0.00} C") / @($"{status.AvgPowerWatts:0} W")
-
- @status.TimeHrs Hrs @status.TimeMins Mins +
+ @(GetTimeLeft())
} @if (status.AvgCurrentAmps == 0) {
- Holding
Voltage + Holding
Voltage
} @if (status.IsWarning) @@ -127,6 +127,8 @@ private static event Action? onStatusUpdated; private static event Action? onStatusRetrievalError; private static BMSStatus? status; + private static bool ShowEndDateAndTime; + private static bool ShowCapacityKwh; protected override void OnInitialized() { @@ -146,6 +148,26 @@ StateHasChanged(); } + private string GetTimeLeft() + { + if (ShowEndDateAndTime) + { + return status!.GetTimeString(); + } + return $"{status!.TimeHrs} Hrs {status.TimeMins} Mins"; + } + + private string GetPackCapacity() + { + if (ShowCapacityKwh) + { + var avlCap = Math.Round((status!.AvailableCapacity * status.PackNominalVoltage) / 1000, 1); + var packCap = Math.Round((status!.PackCapacity * status.PackNominalVoltage) / 1000, 1); + return $"{avlCap} kWh / {packCap} kWh"; + } + return $"{Math.Round(status!.AvailableCapacity, 1)} Ah / {status!.PackCapacity} Ah"; + } + public void Dispose() { onStatusUpdated -= UpdateState; @@ -160,10 +182,10 @@ // which leads to a memory leak/ connection exhaustion. using var client = new HttpClient - { - BaseAddress = new(basePath), - Timeout = TimeSpan.FromSeconds(5) - }; + { + BaseAddress = new(basePath), + Timeout = TimeSpan.FromSeconds(5) + }; var retryDelay = 1000; @@ -183,10 +205,10 @@ JsonSerializer.DeserializeAsyncEnumerable( stream, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true, - DefaultBufferSize = 64 - })) + { + PropertyNameCaseInsensitive = true, + DefaultBufferSize = 64 + })) { onStatusUpdated?.Invoke(s); retryDelay = 1000; diff --git a/src/Client/Pages/Settings.razor b/src/Client/Pages/Settings.razor index d804a0b..e29f180 100644 --- a/src/Client/Pages/Settings.razor +++ b/src/Client/Pages/Settings.razor @@ -253,6 +253,19 @@
+
+
+ Battery Voltage: +
+
+
+
+ +
+
V
+
+
+
Daylight Start (24hr format): diff --git a/src/Server/BatteryService/JK-BMS-RS485-Service.cs b/src/Server/BatteryService/JK-BMS-RS485-Service.cs index 50a5676..7fff4c9 100644 --- a/src/Server/BatteryService/JK-BMS-RS485-Service.cs +++ b/src/Server/BatteryService/JK-BMS-RS485-Service.cs @@ -1,4 +1,5 @@ -using InverterMon.Shared.Models; +using InverterMon.Server.Persistance.Settings; +using InverterMon.Shared.Models; using SerialPortLib; namespace InverterMon.Server.BatteryService; @@ -12,7 +13,7 @@ public class JkBms readonly AmpValQueue _recentAmpReadings = new(5); //avg value over 5 readings (~5secs) readonly SerialPortInput _bms = new(); - public JkBms(IConfiguration config, ILogger logger, IWebHostEnvironment env, IHostApplicationLifetime appLife) + public JkBms(UserSettings userSettings, IConfiguration config, ILogger logger, IWebHostEnvironment env, IHostApplicationLifetime appLife) { if (env.IsDevelopment()) { @@ -21,6 +22,7 @@ public JkBms(IConfiguration config, ILogger logger, IWebHostEnvironment e return; } + Status.PackNominalVoltage = userSettings.BatteryNominalVoltage; var bmsAddress = config["LaunchSettings:JkBmsAddress"] ?? "/dev/ttyUSB0"; _bms.SetPort(bmsAddress); _bms.ConnectionStatusChanged += ConnectionStatusChanged; @@ -126,11 +128,12 @@ void FillDummyData() Status.PackVoltage = 25.6f; Status.IsCharging = true; Status.AvgCurrentAmps = 21.444f; - Status.CapacityPct = 50; + Status.CapacityPct = 48; Status.PackCapacity = 120; + Status.PackNominalVoltage = 50; Status.IsWarning = false; - Status.TimeHrs = 3; - Status.TimeMins = 11; + Status.TimeHrs = 24; + Status.TimeMins = 10; for (byte i = 1; i <= 8; i++) Status.Cells.Add(i, 1.110f); } diff --git a/src/Server/Endpoints/Settings/SetSystemSpec/Validator.cs b/src/Server/Endpoints/Settings/SetSystemSpec/Validator.cs index 842776d..a526500 100644 --- a/src/Server/Endpoints/Settings/SetSystemSpec/Validator.cs +++ b/src/Server/Endpoints/Settings/SetSystemSpec/Validator.cs @@ -18,7 +18,11 @@ public Validator() RuleFor(x => x.SunlightEndHour) .GreaterThanOrEqualTo(0) .LessThanOrEqualTo(24) - .Must((s, h) => h > s.SunlightStartHour).WithMessage("Sunlight end hour must be later than start hour!"); ; + .Must((s, h) => h > s.SunlightStartHour).WithMessage("Sunlight end hour must be later than start hour!"); + + RuleFor(x => x.BatteryNominalVoltage) + .GreaterThan(5) + .WithMessage("Battery nominal voltage required!"); //todo: display validation errors on ui } diff --git a/src/Server/Persistance/Database.cs b/src/Server/Persistance/Database.cs index 5be5d0f..226bd63 100644 --- a/src/Server/Persistance/Database.cs +++ b/src/Server/Persistance/Database.cs @@ -88,6 +88,7 @@ public void RestoreUserSettings() { _settings.PV_MaxCapacity = settings.PV_MaxCapacity; _settings.BatteryCapacity = settings.BatteryCapacity; + _settings.BatteryNominalVoltage = settings.BatteryNominalVoltage; _settings.SunlightStartHour = settings.SunlightStartHour; _settings.SunlightEndHour = settings.SunlightEndHour; } diff --git a/src/Server/Persistance/Settings/UserSettings.cs b/src/Server/Persistance/Settings/UserSettings.cs index 7893466..c6fb42d 100644 --- a/src/Server/Persistance/Settings/UserSettings.cs +++ b/src/Server/Persistance/Settings/UserSettings.cs @@ -8,6 +8,7 @@ public class UserSettings public int Id { get; set; } = 1; public int PV_MaxCapacity { get; set; } = 1000; public int BatteryCapacity { get; set; } = 100; + public float BatteryNominalVoltage { get; set; } = 25.6f; public int SunlightStartHour { get; set; } = 6; public int SunlightEndHour { get; set; } = 18; public int[] PVGraphRange => new[] { 0, (SunlightEndHour - SunlightStartHour) * 60 }; @@ -18,6 +19,7 @@ public SystemSpec ToSystemSpec() { PV_MaxCapacity = PV_MaxCapacity, BatteryCapacity = BatteryCapacity, + BatteryNominalVoltage = BatteryNominalVoltage, SunlightStartHour = SunlightStartHour, SunlightEndHour = SunlightEndHour }; @@ -27,6 +29,7 @@ public void FromSystemSpec(SystemSpec spec) Id = 1; PV_MaxCapacity = spec.PV_MaxCapacity; BatteryCapacity = spec.BatteryCapacity; + BatteryNominalVoltage = spec.BatteryNominalVoltage; SunlightStartHour = spec.SunlightStartHour; SunlightEndHour = spec.SunlightEndHour; } diff --git a/src/Server/Program.cs b/src/Server/Program.cs index 4c676a3..61465a4 100644 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -19,8 +19,8 @@ bld.Services .AddSingleton() .AddSingleton() - .AddSingleton() - .AddSingleton(); + .AddSingleton() + .AddSingleton(); if (!bld.Environment.IsDevelopment()) { diff --git a/src/Shared/Models/BMSStatus.cs b/src/Shared/Models/BMSStatus.cs index 6fcec8c..300160a 100644 --- a/src/Shared/Models/BMSStatus.cs +++ b/src/Shared/Models/BMSStatus.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Serialization; +using System.Globalization; +using System.Text.Json.Serialization; namespace InverterMon.Shared.Models; @@ -63,4 +64,20 @@ public class BMSStatus [JsonPropertyName("t")] public double AvgPowerWatts => Math.Round(AvgCurrentAmps * PackVoltage, 0, MidpointRounding.AwayFromZero); + + [JsonPropertyName("u")] + public float PackNominalVoltage { get; set; } + + public string GetTimeString() + { + var currentTime = DateTime.UtcNow + .AddHours(5).AddMinutes(30); //only supports IST time zone :-( + + var futureTime = currentTime.AddHours(TimeHrs).AddMinutes(TimeMins); + + if (futureTime.Date == currentTime.Date) + return futureTime.ToString("h:mm tt"); + else + return futureTime.ToString("dddd h:mm tt"); + } } \ No newline at end of file diff --git a/src/Shared/Models/SystemSpec.cs b/src/Shared/Models/SystemSpec.cs index b5c3a7d..276c326 100644 --- a/src/Shared/Models/SystemSpec.cs +++ b/src/Shared/Models/SystemSpec.cs @@ -4,6 +4,7 @@ public class SystemSpec { public int PV_MaxCapacity { get; set; } = 1000; public int BatteryCapacity { get; set; } = 100; + public float BatteryNominalVoltage { get; set; } = 25.6f; public int SunlightStartHour { get; set; } = 6; public int SunlightEndHour { get; set; } = 18; } \ No newline at end of file diff --git a/src/changelog.md b/src/changelog.md index b925868..bbaebe4 100644 --- a/src/changelog.md +++ b/src/changelog.md @@ -1,6 +1,4 @@ ## changelog -- show decimal point of jk bms temperature sensor values -- show battery power in watts in jk bms section -- improve bms cell voltage display -- improve pv watts display \ No newline at end of file +- toggle for displaying bms battery capacities in kWh +- toggle for displaying bms remaining time as date and time string \ No newline at end of file