diff --git a/csharp/App/VrmGrabber/Controller.cs b/csharp/App/VrmGrabber/Controller.cs index 9e699796c..99aa28ae9 100644 --- a/csharp/App/VrmGrabber/Controller.cs +++ b/csharp/App/VrmGrabber/Controller.cs @@ -2,6 +2,7 @@ using CliWrap; using CliWrap.Buffered; using HandlebarsDotNet; using InnovEnergy.App.VrmGrabber.Database; +using InnovEnergy.App.VrmGrabber.DataTypes; using InnovEnergy.Lib.Utils; using Microsoft.AspNetCore.Mvc; using FILE=System.IO.File; @@ -167,22 +168,55 @@ th { /* header cell */ { //We need the DeviceName of the battery (ttyUSB?) var pathToBattery = await Db.ExecuteBufferedAsyncCommandOnIp(installationIp, "dbus-send --system --dest=com.victronenergy.system --type=method_call --print-reply /ServiceMapping/com_victronenergy_battery_1 com.victronenergy.BusItem.GetText"); + + var split = pathToBattery.Split('"'); + var split2 = pathToBattery.Split(' '); - if (pathToBattery.Split('"')[1] == "Failed" || pathToBattery.Split(' ')[0] == "Error") return "Update failed"; - + if (split.Length < 2 || split2.Length < 1) + { + Console.WriteLine(pathToBattery + " Split failed "); + return "Update failed"; + } + if (split[1] == "Failed" || split2[0] == "Error") return "Update failed"; + + await UpdateVrmTagsToNewFirmware(installationIp); + SendNewBatteryFirmware(installationIp); for (var batteryId = 2; batteryId <= Int64.Parse(numberOfBatteries) + 1; batteryId++) { - var batteryTtyName = pathToBattery.Split('"')[1].Split(".").Last(); + var batteryTtyName = split[1].Split(".").Last(); var localCommand = $"/opt/innovenergy/scripts/upload-bms-firmware {batteryTtyName} {batteryId} /opt/innovenergy/bms-firmware/{FirmwareVersion}.bin"; - var remoteUpdateCommandResult = await Db.ExecuteBufferedAsyncCommandOnIp(installationIp, localCommand); + #pragma warning disable CS4014 + Db.ExecuteBufferedAsyncCommandOnIp(installationIp, localCommand); + #pragma warning restore CS4014 + // Console.WriteLine(remoteUpdateCommandResult); } return "Battery update is successfully initiated! You can close this page now."; } + private static async Task UpdateVrmTagsToNewFirmware(String installationIp) + { + var vrmInstallation = await FindVrmInstallationByIp(installationIp); + var tags = await vrmInstallation.GetTags(); + + async void RemoveTag(String t) => await vrmInstallation.RemoveTags(t); + + tags.Where(tag => tag.StartsWith("FM-")) + .Do(RemoveTag); + + await vrmInstallation.AddTags("FM-" + FirmwareVersion); + } + + private static async Task FindVrmInstallationByIp(String installationIp) + { + var installationId = Db.Installations.Where(i => i.Ip == installationIp).Select(i => i.Vrm).First(); + var vrmAccount = await Db.GetVrmAccount(); + return await vrmAccount.GetInstallation(installationId!); + } + private static void SendNewBatteryFirmware(String installationIp) { Cli.Wrap("scp") diff --git a/csharp/App/VrmGrabber/Database/Db.cs b/csharp/App/VrmGrabber/Database/Db.cs index 9a47a977c..50ce83b48 100644 --- a/csharp/App/VrmGrabber/Database/Db.cs +++ b/csharp/App/VrmGrabber/Database/Db.cs @@ -1,24 +1,27 @@ using System.Diagnostics.CodeAnalysis; -using System.Reactive.Concurrency; -using System.Reactive.Linq; -using System.Reactive.Threading.Tasks; +using System.Runtime.InteropServices; using System.Web; using CliWrap; using CliWrap.Buffered; using InnovEnergy.App.RemoteSupportConsole; using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Victron.VictronVRM; -using Microsoft.AspNetCore.Mvc.TagHelpers.Cache; using SQLite; using static System.Text.Json.JsonSerializer; +using static InnovEnergy.App.VrmGrabber.Database.Systemd; using Installation = InnovEnergy.App.VrmGrabber.DataTypes.Installation; using VrmInstallation = InnovEnergy.Lib.Victron.VictronVRM.Installation; using FILE=System.IO.File; - namespace InnovEnergy.App.VrmGrabber.Database; +public static class Systemd +{ + [DllImport("libsystemd.so.0", CharSet = CharSet.Unicode)] + public static extern Int32 sd_notify(Int32 unsetEnvironment, String state); +} + public class InstallationDetails { public InstallationDetails(String? ip, IReadOnlyList details) @@ -54,9 +57,10 @@ public static partial class Db Connection.RunInTransaction(() => { Connection.CreateTable(); }); } - + public static async Task UpdateDetailsAndInstallations() { + sd_notify(0, "READY=1"); do { await UpdateInstallationsAndDetailsFromVrm(0); } @@ -66,10 +70,8 @@ public static partial class Db [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "")] private static async Task UpdateInstallationsAndDetailsFromVrm(Int32 _) { - var fileContent = await File.ReadAllTextAsync("./token.json"); - - var acc = Deserialize(fileContent); - var user = VrmAccount.Token(acc!.idUser, acc.token); + sd_notify(0, "WATCHDOG=1"); + var user = await GetVrmAccount(); var installations = await user.GetInstallations(); // var returnDictionary = new Dictionary(); @@ -105,6 +107,14 @@ public static partial class Db } } + public static async Task GetVrmAccount() + { + var fileContent = await File.ReadAllTextAsync("./token.json"); + var acc = Deserialize(fileContent); + var user = VrmAccount.Token(acc!.idUser, acc.token); + return user; + } + private static async Task BatteryFirmwareVersion(String? ip, String? online) { if (ip is null or "Unknown" || online == "❌") return "Unknown"; @@ -138,7 +148,7 @@ public static partial class Db var cmd = await Cli.Wrap("ssh") .WithArguments($@"root@{ip}") - .AppendArgument("-o StrictHostKeyChecking=accept-new") + .AppendArgument("-o StrictHostKeyChecking=no") .AppendArgument(command) .WithValidation(CommandResultValidation.None).ExecuteBufferedAsync(); return cmd.ExitCode == 0 ? cmd.StandardOutput : cmd.StandardError; diff --git a/csharp/App/VrmGrabber/Program.cs b/csharp/App/VrmGrabber/Program.cs index 60a685763..693f847cc 100644 --- a/csharp/App/VrmGrabber/Program.cs +++ b/csharp/App/VrmGrabber/Program.cs @@ -7,20 +7,20 @@ public static class Program { public static async Task Main(String[] args) { - var updateTask =Db.UpdateDetailsAndInstallations(); + var updateTask = Db.UpdateDetailsAndInstallations(); var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); - builder.Services.AddSwaggerGen(c => - { - c.UseAllOfToExtendReferenceSchemas(); - c.SupportNonNullableReferenceTypes(); - }); + // builder.Services.AddSwaggerGen(c => + // { + // c.UseAllOfToExtendReferenceSchemas(); + // c.SupportNonNullableReferenceTypes(); + // }); var app = builder.Build(); - app.UseSwagger(); - app.UseSwaggerUI(); - app.UseHttpsRedirection(); + // app.UseSwagger(); + // app.UseSwaggerUI(); + // app.UseHttpsRedirection(); app.MapControllers(); // app.MapGet("/", () => Controller.Index()); var webTask = app.RunAsync(); diff --git a/csharp/App/VrmGrabber/db.sqlite b/csharp/App/VrmGrabber/db.sqlite index 78ada0c56..30eea1250 100644 Binary files a/csharp/App/VrmGrabber/db.sqlite and b/csharp/App/VrmGrabber/db.sqlite differ diff --git a/csharp/Lib/Victron/VictronVRM/Requests.cs b/csharp/Lib/Victron/VictronVRM/Requests.cs index 5ec6c4214..99684e724 100644 --- a/csharp/Lib/Victron/VictronVRM/Requests.cs +++ b/csharp/Lib/Victron/VictronVRM/Requests.cs @@ -31,6 +31,16 @@ public static class Requests .WithHeader("X-Authorization", account.Auth); } + public static IFlurlRequest SpecificInstallationRequest(this VrmAccount account, UInt64 installationId) + { + return ApiRoot + .AppendPathSegment("users") + .AppendPathSegment(account.UserId) + .AppendPathSegment("installations") + .AppendPathSegment(installationId) + .WithHeader("X-Authorization", account.Auth); + } + public static IFlurlRequest AllInstallationsRequest(this VrmAccount account) { return ApiRoot diff --git a/csharp/Lib/Victron/VictronVRM/VrmAccount.cs b/csharp/Lib/Victron/VictronVRM/VrmAccount.cs index a5266ee4b..a0e659598 100644 --- a/csharp/Lib/Victron/VictronVRM/VrmAccount.cs +++ b/csharp/Lib/Victron/VictronVRM/VrmAccount.cs @@ -49,6 +49,20 @@ public class VrmAccount : IDisposable .Select(r => new Installation(this, r!)) .ToArray(vrmReply.Records.Count); } + + public async Task GetInstallation(Int64 installationId) + { + var reply = await this.SpecificInstallationRequest((UInt64)installationId).TryGetJson(); + var vrmReply = new Reply(reply); + + if (!vrmReply.Success) + throw new Exception(nameof(GetInstallations) + " failed"); + + return vrmReply + .Records + .Select(r => new Installation(this, r!)) + .First(); + } public void Dispose() {