diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 5e5117c..99b5b74 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -14,7 +14,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.100 + dotnet-version: 6.x - name: Install dependencies run: dotnet restore - name: Build @@ -26,7 +26,7 @@ jobs: uses: notiz-dev/github-action-json-property@release with: # path to json file - path: ./GlucoseTraycore/appsettings.json + path: ./GlucoseTray/appsettings.json # which property to read prop_path: appsettings.Version - name: Create Release @@ -37,8 +37,8 @@ jobs: # An optional set of paths representing artifacts to upload to the release. This may be a single path or a comma delimited list of paths (or globs) #artifact: # An optional set of paths representing artifacts to upload to the release. This may be a single path or a comma delimited list of paths (or globs) - #artifacts: './GlucoseTrayCore/bin/Release/net5.0-windows/win-x64/publish/' - artifacts: './GlucoseTrayCore/bin/Release/net5.0-windows/win-x64/publish/GlucoseTrayCore.exe' + #artifacts: './GlucoseTray/bin/Release/net5.0-windows/win-x64/publish/' + artifacts: './GlucoseTray/bin/Release/net6.0-windows/win-x64/publish/GlucoseTray.exe' # The content type of the artifact. Defaults to raw #artifactContentType: # optional, default is # An optional body for the release. diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 08fe442..86a397f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.100 + dotnet-version: 6.x - name: Install dependencies run: dotnet restore - name: Build @@ -28,6 +28,6 @@ jobs: uses: notiz-dev/github-action-json-property@release with: # path to json file - path: ./GlucoseTraycore/appsettings.json + path: ./GlucoseTray/appsettings.json # which property to read prop_path: appsettings.Version diff --git a/.gitignore b/.gitignore index cf559dc..e318b7b 100644 --- a/.gitignore +++ b/.gitignore @@ -172,7 +172,7 @@ publish/ *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted -*.pubxml +#*.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to diff --git a/GlucoseTray.sln b/GlucoseTray.sln index f122064..d893ca5 100644 --- a/GlucoseTray.sln +++ b/GlucoseTray.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28803.202 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GlucoseTrayCore", "GlucoseTrayCore\GlucoseTrayCore.csproj", "{2778CBCC-020C-4F3D-99BC-E8C231FB7F53}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GlucoseTray", "GlucoseTray\GlucoseTray.csproj", "{0A0B15BE-CA2F-4B32-A338-C8B2DF04060E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,10 +11,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2778CBCC-020C-4F3D-99BC-E8C231FB7F53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2778CBCC-020C-4F3D-99BC-E8C231FB7F53}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2778CBCC-020C-4F3D-99BC-E8C231FB7F53}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2778CBCC-020C-4F3D-99BC-E8C231FB7F53}.Release|Any CPU.Build.0 = Release|Any CPU + {0A0B15BE-CA2F-4B32-A338-C8B2DF04060E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A0B15BE-CA2F-4B32-A338-C8B2DF04060E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A0B15BE-CA2F-4B32-A338-C8B2DF04060E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A0B15BE-CA2F-4B32-A338-C8B2DF04060E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GlucoseTrayCore/AppContext.cs b/GlucoseTray/AppContext.cs similarity index 70% rename from GlucoseTrayCore/AppContext.cs rename to GlucoseTray/AppContext.cs index dd9905c..0384a00 100644 --- a/GlucoseTrayCore/AppContext.cs +++ b/GlucoseTray/AppContext.cs @@ -1,6 +1,5 @@ -using GlucoseTrayCore.Data; -using GlucoseTrayCore.Enums; -using GlucoseTrayCore.Services; +using GlucoseTray.Enums; +using GlucoseTray.Services; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; @@ -9,18 +8,17 @@ using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; -using GlucoseTrayCore.Extensions; -using GlucoseTrayCore.Models; +using GlucoseTray.Extensions; +using GlucoseTray.Models; using System.Collections.Generic; -using GlucoseTrayCore.Views.Settings; +using GlucoseTray.Views.Settings; -namespace GlucoseTrayCore +namespace GlucoseTray { public class AppContext : ApplicationContext { private readonly ILogger _logger; private readonly IOptionsMonitor _options; - private readonly IGlucoseTrayDbContext _context; private readonly IGlucoseFetchService _fetchService; private readonly NotifyIcon trayIcon; @@ -28,10 +26,9 @@ public class AppContext : ApplicationContext private readonly IconService _iconService; private readonly TaskSchedulerService _taskScheduler; - public AppContext(ILogger logger, IGlucoseTrayDbContext context, IconService iconService, IGlucoseFetchService fetchService, IOptionsMonitor options, TaskSchedulerService taskScheduler) + public AppContext(ILogger logger, IconService iconService, IGlucoseFetchService fetchService, IOptionsMonitor options, TaskSchedulerService taskScheduler) { _logger = logger; - _context = context; _iconService = iconService; _fetchService = fetchService; _options = options; @@ -76,8 +73,6 @@ private void ToggleTask(bool enable) private async void BeginCycle() { - await CheckForMissingReadings(); - while (true) { try @@ -87,11 +82,7 @@ private async void BeginCycle() var results = await _fetchService.GetLatestReadings(GlucoseResult?.DateTimeUTC).ConfigureAwait(false); if (results.Any()) - { - LogResultToDb(results); - GlucoseResult = results.Last(); - } CreateIcon(); AlertNotification(); @@ -100,8 +91,7 @@ private async void BeginCycle() } catch (Exception e) { - if (_options.CurrentValue.EnableDebugMode) - MessageBox.Show($"ERROR: {e}", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show($"ERROR: {e}", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error); _logger.LogError(e.ToString()); trayIcon.Visible = false; trayIcon?.Dispose(); @@ -169,43 +159,6 @@ private bool IsAlertTriggered(double glucoseValueMG, double glucoseValueMMOL, do ? directionGlucoseShouldBeToNotAlert == UpDown.Down ? glucoseValueMG >= alertThreshold : glucoseValueMG <= alertThreshold : directionGlucoseShouldBeToNotAlert == UpDown.Down ? glucoseValueMMOL >= alertThreshold : glucoseValueMMOL <= alertThreshold; - private async Task CheckForMissingReadings() - { - if (_options.CurrentValue.FetchMethod != FetchMethod.NightscoutApi) - return; - - GlucoseResult = _context.GlucoseResults.OrderByDescending(a => a.DateTimeUTC).FirstOrDefault(); - - if (GlucoseResult == null && MessageBox.Show("Do you want to import readings from NightScout?\r\n\r\n(Warning this may take some time.)", "GlucoseTrayCore : No Readings found in local database.", MessageBoxButtons.YesNo) == DialogResult.No) - return; - - DateTime startDate = GlucoseResult?.DateTimeUTC ?? DateTime.UtcNow.AddYears(-100); - - var sw = new Stopwatch(); - sw.Start(); - var missingResults = await _fetchService.GetLatestReadings(startDate).ConfigureAwait(false); - sw.Stop(); - int count = missingResults.Count; - if (count > 0) - { - var sinceMessage = (GlucoseResult != null) ? $" since last database record at {GlucoseResult.DateTimeUTC} UTC" : ""; - - if (count == 1) - _logger.LogWarning($"Starting Up : Found 1 reading recorded at {missingResults[0].DateTimeUTC} UTC{sinceMessage}."); - else - _logger.LogWarning($"Found {count} readings between {missingResults[0].DateTimeUTC} and {missingResults[count - 1].DateTimeUTC} UTC{sinceMessage}. Retrieving them took {sw.Elapsed.TotalSeconds:#,##0.##} seconds"); - - sw.Restart(); - _context.GlucoseResults.AddRange(missingResults); // None of these records will be in the database, so just add them all now. - _context.SaveChanges(); - sw.Stop(); - if (sw.Elapsed.TotalSeconds > 5) - _logger.LogWarning($"Saving {missingResults.Count()} records took {sw.Elapsed.TotalSeconds:#,##0.##} seconds"); - - GlucoseResult = missingResults.Last(); - } - } - private void Exit(object sender, EventArgs e) { _logger.LogInformation("Exiting application."); @@ -237,20 +190,5 @@ private void CreateIcon() private void ShowBalloon(object sender, EventArgs e) => trayIcon.ShowBalloonTip(2000, "Glucose", GetGlucoseMessage(GlucoseResult), ToolTipIcon.Info); private string GetGlucoseMessage(GlucoseResult result) => $"{result.GetFormattedStringValue(_options.CurrentValue.GlucoseUnit)} {result.DateTimeUTC.ToLocalTime().ToLongTimeString()} {result.Trend.GetTrendArrow()}{result.StaleMessage(_options.CurrentValue.StaleResultsThreshold)}"; - - private void LogResultToDb(List results) - { - if (results.Count > 1) - _logger.LogWarning($"Found {results.Count} readings between {results[0].DateTimeUTC} and {results[results.Count - 1].DateTimeUTC} UTC{(System.Diagnostics.Debugger.IsAttached ? " (Debugging Mode)" : "")}"); - - foreach (var result in results) - { - if (!_context.GlucoseResults.Any(g => g.DateTimeUTC == result.DateTimeUTC && !result.WasError && g.MgValue == result.MgValue)) - { - _context.GlucoseResults.Add(result); - _context.SaveChanges(); - } - } - } } } \ No newline at end of file diff --git a/GlucoseTrayCore/Enums/AlertLevel.cs b/GlucoseTray/Enums/AlertLevel.cs similarity index 80% rename from GlucoseTrayCore/Enums/AlertLevel.cs rename to GlucoseTray/Enums/AlertLevel.cs index 1d0d4e4..f9d3aa3 100644 --- a/GlucoseTrayCore/Enums/AlertLevel.cs +++ b/GlucoseTray/Enums/AlertLevel.cs @@ -1,4 +1,4 @@ -namespace GlucoseTrayCore.Enums +namespace GlucoseTray.Enums { public enum AlertLevel { diff --git a/GlucoseTrayCore/Enums/DexcomServer.cs b/GlucoseTray/Enums/DexcomServer.cs similarity index 89% rename from GlucoseTrayCore/Enums/DexcomServer.cs rename to GlucoseTray/Enums/DexcomServer.cs index d2c8864..beff588 100644 --- a/GlucoseTrayCore/Enums/DexcomServer.cs +++ b/GlucoseTray/Enums/DexcomServer.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace GlucoseTrayCore.Enums +namespace GlucoseTray.Enums { public enum DexcomServerLocation { diff --git a/GlucoseTrayCore/Enums/FetchMethod.cs b/GlucoseTray/Enums/FetchMethod.cs similarity index 73% rename from GlucoseTrayCore/Enums/FetchMethod.cs rename to GlucoseTray/Enums/FetchMethod.cs index c1680ce..b8bcfbf 100644 --- a/GlucoseTrayCore/Enums/FetchMethod.cs +++ b/GlucoseTray/Enums/FetchMethod.cs @@ -1,4 +1,4 @@ -namespace GlucoseTrayCore.Enums +namespace GlucoseTray.Enums { public enum FetchMethod { diff --git a/GlucoseTrayCore/Enums/GlucoseUnitType.cs b/GlucoseTray/Enums/GlucoseUnitType.cs similarity index 69% rename from GlucoseTrayCore/Enums/GlucoseUnitType.cs rename to GlucoseTray/Enums/GlucoseUnitType.cs index 15eb06e..3977f89 100644 --- a/GlucoseTrayCore/Enums/GlucoseUnitType.cs +++ b/GlucoseTray/Enums/GlucoseUnitType.cs @@ -1,4 +1,4 @@ -namespace GlucoseTrayCore.Enums +namespace GlucoseTray.Enums { public enum GlucoseUnitType { diff --git a/GlucoseTrayCore/Enums/TrendResult.cs b/GlucoseTray/Enums/TrendResult.cs similarity index 88% rename from GlucoseTrayCore/Enums/TrendResult.cs rename to GlucoseTray/Enums/TrendResult.cs index d210bb2..65036f2 100644 --- a/GlucoseTrayCore/Enums/TrendResult.cs +++ b/GlucoseTray/Enums/TrendResult.cs @@ -1,4 +1,4 @@ -namespace GlucoseTrayCore.Enums +namespace GlucoseTray.Enums { public enum TrendResult { diff --git a/GlucoseTrayCore/Enums/UpDown.cs b/GlucoseTray/Enums/UpDown.cs similarity index 64% rename from GlucoseTrayCore/Enums/UpDown.cs rename to GlucoseTray/Enums/UpDown.cs index b472c16..3103552 100644 --- a/GlucoseTrayCore/Enums/UpDown.cs +++ b/GlucoseTray/Enums/UpDown.cs @@ -1,4 +1,4 @@ -namespace GlucoseTrayCore.Enums +namespace GlucoseTray.Enums { public enum UpDown { diff --git a/GlucoseTrayCore/Extensions/GlucoseFetchResultExtensions.cs b/GlucoseTray/Extensions/GlucoseFetchResultExtensions.cs similarity index 67% rename from GlucoseTrayCore/Extensions/GlucoseFetchResultExtensions.cs rename to GlucoseTray/Extensions/GlucoseFetchResultExtensions.cs index 562ae1b..5695f22 100644 --- a/GlucoseTrayCore/Extensions/GlucoseFetchResultExtensions.cs +++ b/GlucoseTray/Extensions/GlucoseFetchResultExtensions.cs @@ -1,22 +1,18 @@ -using GlucoseTrayCore.Enums; -using GlucoseTrayCore.Models; +using GlucoseTray.Enums; +using GlucoseTray.Models; -namespace GlucoseTrayCore.Extensions +namespace GlucoseTray.Extensions { public static class GlucoseFetchResultExtensions { public static string GetFormattedStringValue(this GlucoseResult fetchResult, GlucoseUnitType type) => type == GlucoseUnitType.MG ? fetchResult.MgValue.ToString() : fetchResult.MmolValue.ToString("0.0"); - public static bool IsStale(this GlucoseResult fetchResult, int minutes) - { - var ts = System.DateTime.Now.ToUniversalTime() - fetchResult.DateTimeUTC; - return ts.TotalMinutes > minutes; - } + public static bool IsStale(this GlucoseResult fetchResult, int minutes) => (System.DateTime.Now.ToUniversalTime() - fetchResult.DateTimeUTC).TotalMinutes > minutes; public static string StaleMessage(this GlucoseResult fetchResult, int minutes) { var ts = System.DateTime.Now.ToUniversalTime() - fetchResult.DateTimeUTC; - return ts.TotalMinutes > minutes ? $"\r\n{ts.TotalMinutes:#} minutes ago" : ""; + return ts.TotalMinutes > minutes ? $"\r\n{ts.TotalMinutes:#} minutes ago" : string.Empty; } } } diff --git a/GlucoseTray/Extensions/StringExtensions.cs b/GlucoseTray/Extensions/StringExtensions.cs new file mode 100644 index 0000000..6f19c9b --- /dev/null +++ b/GlucoseTray/Extensions/StringExtensions.cs @@ -0,0 +1,43 @@ +using GlucoseTray.Enums; + +namespace GlucoseTray.Extensions +{ + public static class StringExtensions + { + public static string GetTrendArrow(this TrendResult input) + { + return input switch + { + TrendResult.TripleUp => "⤊", + TrendResult.DoubleUp => "⮅", + TrendResult.SingleUp => "↑", + TrendResult.FortyFiveUp => "↗", + TrendResult.Flat => "→", + TrendResult.FortFiveDown => "↘", + TrendResult.SingleDown => "↓", + TrendResult.DoubleDown => "⮇", + TrendResult.TripleDown => "⤋", + TrendResult.Unknown => "Unknown", + _ => string.Empty, + }; + } + + public static TrendResult GetTrend(this string direction) + { + // Values for Direction copied from https://github.com/nightscout/cgm-remote-monitor/blob/41ac93f7217b1b7023ec6ad6fc35d29dcf2e4f88/lib/plugins/direction.js + return direction switch + { + "TripleUp" => TrendResult.TripleUp, + "DoubleUp" => TrendResult.DoubleUp, + "SingleUp" => TrendResult.SingleUp, + "FortyFiveUp" => TrendResult.FortyFiveUp, + "Flat" => TrendResult.Flat, + "FortyFiveDown" => TrendResult.FortFiveDown, + "SingleDown" => TrendResult.SingleDown, + "DoubleDown" => TrendResult.DoubleDown, + "TripleDown" => TrendResult.TripleDown, + _ => TrendResult.Unknown, + }; + } + } +} diff --git a/GlucoseTrayCore/GlucoseTrayCore.csproj b/GlucoseTray/GlucoseTray.csproj similarity index 57% rename from GlucoseTrayCore/GlucoseTrayCore.csproj rename to GlucoseTray/GlucoseTray.csproj index 6de48a9..386f362 100644 --- a/GlucoseTrayCore/GlucoseTrayCore.csproj +++ b/GlucoseTray/GlucoseTray.csproj @@ -2,11 +2,10 @@ WinExe - net5.0-windows - GlucoseTrayCore + net6.0-windows + GlucoseTray true true - true win-x64 @@ -16,29 +15,27 @@ true true AnyCPU - app.manifest - + - - - - - - - - - - - - + + + + + + + + + + + - - + + diff --git a/GlucoseTrayCore/GlucoseTraySettings.cs b/GlucoseTray/GlucoseTraySettings.cs similarity index 88% rename from GlucoseTrayCore/GlucoseTraySettings.cs rename to GlucoseTray/GlucoseTraySettings.cs index 74628ce..12045ce 100644 --- a/GlucoseTrayCore/GlucoseTraySettings.cs +++ b/GlucoseTray/GlucoseTraySettings.cs @@ -1,11 +1,10 @@ -using GlucoseTrayCore.Enums; -using GlucoseTrayCore.Services; -using Serilog.Events; +using GlucoseTray.Enums; +using GlucoseTray.Services; using System; using System.ComponentModel; using System.Text.Json.Serialization; -namespace GlucoseTrayCore +namespace GlucoseTray { public class GlucoseTraySettings : INotifyPropertyChanged { @@ -107,24 +106,6 @@ public int PollingThreshold [JsonIgnore] public TimeSpan PollingThresholdTimeSpan => TimeSpan.FromSeconds(PollingThreshold); - private string databaseLocation; - public string DatabaseLocation - { - get => databaseLocation; set { databaseLocation = value; OnPropertyChanged(nameof(DatabaseLocation)); } - } - - private bool enableDebugMode; - public bool EnableDebugMode - { - get => enableDebugMode; set { enableDebugMode = value; OnPropertyChanged(nameof(EnableDebugMode)); } - } - - private LogEventLevel logLevel; - public LogEventLevel LogLevel - { - get => logLevel; set { logLevel = value; OnPropertyChanged(nameof(LogLevel)); } - } - private int staleResultsThreshold; public int StaleResultsThreshold { diff --git a/GlucoseTrayCore/Models/DexcomResult.cs b/GlucoseTray/Models/DexcomResult.cs similarity index 82% rename from GlucoseTrayCore/Models/DexcomResult.cs rename to GlucoseTray/Models/DexcomResult.cs index 44a30a1..1cb4af3 100644 --- a/GlucoseTrayCore/Models/DexcomResult.cs +++ b/GlucoseTray/Models/DexcomResult.cs @@ -1,9 +1,9 @@ -namespace GlucoseTrayCore.Models +namespace GlucoseTray.Models { /// /// Class that maps to the JSON received from DexCom queries. /// - internal class DexcomResult + public class DexcomResult { public string ST { get; set; } public string DT { get; set; } diff --git a/GlucoseTrayCore/Models/GlucoseResult.cs b/GlucoseTray/Models/GlucoseResult.cs similarity index 76% rename from GlucoseTrayCore/Models/GlucoseResult.cs rename to GlucoseTray/Models/GlucoseResult.cs index 77e2778..328ddcc 100644 --- a/GlucoseTrayCore/Models/GlucoseResult.cs +++ b/GlucoseTray/Models/GlucoseResult.cs @@ -1,26 +1,17 @@ -using GlucoseTrayCore.Enums; +using GlucoseTray.Enums; using System; -using System.ComponentModel.DataAnnotations; -namespace GlucoseTrayCore.Models +namespace GlucoseTray.Models { public class GlucoseResult { - [Key] public int Id { get; set; } - public int MgValue { get; set; } - public double MmolValue { get; set; } - public DateTime DateTimeUTC { get; set; } - public TrendResult Trend { get; set; } - public bool WasError { get; set; } - public FetchMethod Source { get; set; } - public bool IsCriticalLow { get; set; } } } diff --git a/GlucoseTray/Models/NightScoutResult.cs b/GlucoseTray/Models/NightScoutResult.cs new file mode 100644 index 0000000..a511dab --- /dev/null +++ b/GlucoseTray/Models/NightScoutResult.cs @@ -0,0 +1,37 @@ +using System.Text.Json.Serialization; + +namespace GlucoseTray.Models +{ + /// + /// Class that maps to the JSON from NightScout queries. + /// + public class NightScoutResult + { + [JsonPropertyName("_id")] + public string Id { get; set; } + + [JsonPropertyName("sgv")] + public double Sgv { get; set; } + + [JsonPropertyName("date")] + public long Date { get; set; } + + [JsonPropertyName("dateString")] + public string DateString { get; set; } + + [JsonPropertyName("direction")] + public string Direction { get; set; } + + [JsonPropertyName("device")] + public string Device { get; set; } + + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("utcOffset")] + public long UtcOffset { get; set; } + + [JsonPropertyName("sysTime")] + public string SystemTime { get; set; } + } +} diff --git a/GlucoseTrayCore/Models/NightScoutStatus.cs b/GlucoseTray/Models/NightScoutStatus.cs similarity index 61% rename from GlucoseTrayCore/Models/NightScoutStatus.cs rename to GlucoseTray/Models/NightScoutStatus.cs index 8c8d675..494b36e 100644 --- a/GlucoseTrayCore/Models/NightScoutStatus.cs +++ b/GlucoseTray/Models/NightScoutStatus.cs @@ -1,4 +1,6 @@ -namespace GlucoseTrayCore.Models +using System.Text.Json.Serialization; + +namespace GlucoseTray.Models { /// /// Class that maps to the JSON from NightScout status. @@ -8,9 +10,9 @@ /// /// Would it be possible to read the units and alarm thresholds from nightscout? /// - internal class NightScoutStatus + public class NightScoutStatus { - public string status { get; set; } - + [JsonPropertyName("status")] + public string Status { get; set; } } } diff --git a/GlucoseTray/Models/NightScoutStatusResult.cs b/GlucoseTray/Models/NightScoutStatusResult.cs new file mode 100644 index 0000000..f27a25e --- /dev/null +++ b/GlucoseTray/Models/NightScoutStatusResult.cs @@ -0,0 +1,20 @@ +using GlucoseTray.Enums; +using System.Text.Json.Serialization; + +namespace GlucoseTray.Models +{ + public class NightScoutStatusResult + { + [JsonPropertyName("settings")] + public NightScoutSettingsResult Settings { get; set; } + } + + public class NightScoutSettingsResult + { + [JsonPropertyName("units")] + public string Units { get; set; } + + [JsonIgnore] + public GlucoseUnitType UnitType => Units == "mg/dl" ? GlucoseUnitType.MG : GlucoseUnitType.MMOL; + } +} diff --git a/GlucoseTray/Program.cs b/GlucoseTray/Program.cs new file mode 100644 index 0000000..e72bda7 --- /dev/null +++ b/GlucoseTray/Program.cs @@ -0,0 +1,68 @@ +using GlucoseTray.Services; +using GlucoseTray.Views.Settings; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.IO; +using System.Text.Json; +using System.Threading; +using System.Windows.Forms; + +namespace GlucoseTray +{ + public class Program + { + private static IConfiguration Configuration { get; set; } + public static string SettingsFile { get; set; } + + [STAThread] + private static void Main(string[] args) + { + if (!LoadApplicationSettings()) + return; + + var host = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((context, builder) => builder.AddJsonFile(SettingsFile, optional: false, reloadOnChange: true)) + .ConfigureServices((context, services) => ConfigureServices(context.Configuration, services)) + .Build(); + + Application.ThreadException += ApplicationThreadException; + Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + + var services = host.Services; + + var app = services.GetRequiredService(); + Application.Run(app); + } + + private static void ConfigureServices(IConfiguration configuration, IServiceCollection services) + { + Configuration = configuration; + services.Configure(Configuration) + .AddHttpClient() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(); + } + + private static bool LoadApplicationSettings() + { + Environment.SetEnvironmentVariable("windir", Environment.GetEnvironmentVariable("SystemRoot"), EnvironmentVariableTarget.User); + SettingsFile = Application.UserAppDataPath + @"\glucose_tray_settings.json"; + if (!File.Exists(SettingsFile) || SettingsService.ValidateSettings().Count != 0) + { + var settingsWindow = new SettingsWindow(); + if (settingsWindow.ShowDialog() != true) // Did not want to setup application. + { + Application.Exit(); + return false; + } + } + return true; + } + + private static void ApplicationThreadException(object sender, ThreadExceptionEventArgs e) => MessageBox.Show(JsonSerializer.Serialize(e), "Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } +} \ No newline at end of file diff --git a/GlucoseTrayCore/Properties/PublishProfiles/PublishToSingleSelfContainedExe.pubxml b/GlucoseTray/Properties/PublishProfiles/PublishToSingleSelfContainedExe.pubxml similarity index 80% rename from GlucoseTrayCore/Properties/PublishProfiles/PublishToSingleSelfContainedExe.pubxml rename to GlucoseTray/Properties/PublishProfiles/PublishToSingleSelfContainedExe.pubxml index 698c440..b547112 100644 --- a/GlucoseTrayCore/Properties/PublishProfiles/PublishToSingleSelfContainedExe.pubxml +++ b/GlucoseTray/Properties/PublishProfiles/PublishToSingleSelfContainedExe.pubxml @@ -7,13 +7,12 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release Any CPU - net5.0-windows - bin\Release\net5.0-windows\win-x64\publish\ + net6.0-windows + bin\Release\net6.0-windows\win-x64\publish\ win-x64 true True True - True true \ No newline at end of file diff --git a/GlucoseTrayCore/Services/FileService.cs b/GlucoseTray/Services/FileService.cs similarity index 95% rename from GlucoseTrayCore/Services/FileService.cs rename to GlucoseTray/Services/FileService.cs index fec796c..f2c9ab4 100644 --- a/GlucoseTrayCore/Services/FileService.cs +++ b/GlucoseTray/Services/FileService.cs @@ -1,7 +1,7 @@ using System.IO; using System.Text.Json; -namespace GlucoseTrayCore.Services +namespace GlucoseTray.Services { public static class FileService { diff --git a/GlucoseTrayCore/Services/GlucoseFetchService.cs b/GlucoseTray/Services/GlucoseFetchService.cs similarity index 95% rename from GlucoseTrayCore/Services/GlucoseFetchService.cs rename to GlucoseTray/Services/GlucoseFetchService.cs index c00f7b6..9a353f0 100644 --- a/GlucoseTrayCore/Services/GlucoseFetchService.cs +++ b/GlucoseTray/Services/GlucoseFetchService.cs @@ -1,4 +1,4 @@ -using GlucoseTrayCore.Enums; +using GlucoseTray.Enums; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; @@ -7,11 +7,11 @@ using System.Net.Http; using System.Text; using System.Threading.Tasks; -using GlucoseTrayCore.Extensions; -using GlucoseTrayCore.Models; +using GlucoseTray.Extensions; +using GlucoseTray.Models; using System.Text.Json; -namespace GlucoseTrayCore.Services +namespace GlucoseTray.Services { public interface IGlucoseFetchService { @@ -116,12 +116,12 @@ private async Task> GetResultsFromNightscout(DateTime? timeO var fetchResult = new GlucoseResult { Source = FetchMethod.NightscoutApi, - DateTimeUTC = DateTime.Parse(record.dateString).ToUniversalTime(), - Trend = record.direction.GetTrend() + DateTimeUTC = DateTime.Parse(record.DateString).ToUniversalTime(), + Trend = record.Direction.GetTrend() }; - CalculateValues(fetchResult, record.sgv); + CalculateValues(fetchResult, record.Sgv); if (fetchResult.Trend == TrendResult.Unknown) - _logger.LogWarning($"Un-expected value for direction/Trend {record.direction}"); + _logger.LogWarning($"Un-expected value for direction/Trend {record.Direction}"); results.Add(fetchResult); } diff --git a/GlucoseTrayCore/Services/IconService.cs b/GlucoseTray/Services/IconService.cs similarity index 93% rename from GlucoseTrayCore/Services/IconService.cs rename to GlucoseTray/Services/IconService.cs index 00832ab..79a7fcb 100644 --- a/GlucoseTrayCore/Services/IconService.cs +++ b/GlucoseTray/Services/IconService.cs @@ -1,14 +1,14 @@ -using GlucoseTrayCore.Enums; +using GlucoseTray.Enums; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; -using GlucoseTrayCore.Extensions; -using GlucoseTrayCore.Models; +using GlucoseTray.Extensions; +using GlucoseTray.Models; -namespace GlucoseTrayCore.Services +namespace GlucoseTray.Services { public class IconService { @@ -31,7 +31,7 @@ public IconService(ILogger logger, IOptionsMonitor DestroyIcon(handle); - internal Brush SetColor(double val) => val switch + public Brush SetColor(double val) => val switch { double n when n < _options.CurrentValue.WarningHighBg && n > _options.CurrentValue.WarningLowBg => new SolidBrush(Color.White), double n when n >= _options.CurrentValue.WarningHighBg && n < _options.CurrentValue.HighBg => new SolidBrush(Color.Yellow), @@ -42,7 +42,7 @@ public IconService(ILogger logger, IOptionsMonitor new SolidBrush(Color.White), }; - internal void CreateTextIcon(GlucoseResult result, NotifyIcon trayIcon) + public void CreateTextIcon(GlucoseResult result, NotifyIcon trayIcon) { var glucoseValue = result.GetFormattedStringValue(_options.CurrentValue.GlucoseUnit).Replace('.', '\''); // Use ' instead of . since it is narrower and allows a better display of a two digit number + decimal place. diff --git a/GlucoseTrayCore/Services/SettingsService.cs b/GlucoseTray/Services/SettingsService.cs similarity index 57% rename from GlucoseTrayCore/Services/SettingsService.cs rename to GlucoseTray/Services/SettingsService.cs index 4bed217..cb8e0bb 100644 --- a/GlucoseTrayCore/Services/SettingsService.cs +++ b/GlucoseTray/Services/SettingsService.cs @@ -1,9 +1,11 @@ -using GlucoseTrayCore.Enums; +using GlucoseTray.Enums; using System.Collections.Generic; using System; using System.Net.Http; +using System.Text.Json; +using System.Net.Http.Headers; -namespace GlucoseTrayCore.Services +namespace GlucoseTray.Services { public static class SettingsService { @@ -35,11 +37,9 @@ public static List ValidateSettings(GlucoseTraySettings model = null) else { string nightScoutError = ValidateNightScout(model); - if (!String.IsNullOrWhiteSpace(nightScoutError)) + if (!string.IsNullOrWhiteSpace(nightScoutError)) errors.Add(nightScoutError); } - if (string.IsNullOrWhiteSpace(model.DatabaseLocation)) - errors.Add("Database Location is missing"); if (!(model.HighBg > model.WarningHighBg && model.WarningHighBg > model.WarningLowBg && model.WarningLowBg > model.LowBg && model.LowBg > model.CriticalLowBg)) errors.Add("Thresholds overlap "); @@ -51,55 +51,40 @@ private static string ValidateNightScout(GlucoseTraySettings model) { if (string.IsNullOrWhiteSpace(model.NightscoutUrl)) return "Nightscout Url is missing"; - - if (!model.NightscoutUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase) && - !model.NightscoutUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) + if (!model.NightscoutUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase) && !model.NightscoutUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) return "Nightscout URL must start with http:// or https://"; - - Uri testUrl; - if (!Uri.TryCreate(model.NightscoutUrl, System.UriKind.Absolute, out testUrl)) + if (!Uri.TryCreate(model.NightscoutUrl, UriKind.Absolute, out _)) return "Invalid Nightscout URL"; - + try { var url = $"{model.NightscoutUrl}/api/v1/status.json"; - url += !string.IsNullOrWhiteSpace(model.AccessToken) ? $"?token={model.AccessToken}" : string.Empty; - using (var httpClient = new HttpClient()) - { - var request = new HttpRequestMessage(HttpMethod.Get, new Uri(url)); - request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); - - var response = httpClient.SendAsync(request).Result; - - if (!response.IsSuccessStatusCode) - return "Invalid Nightscout url - Error : " + response.ReasonPhrase; + using var httpClient = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Get, new Uri(url)); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + var response = httpClient.SendAsync(request).Result; - var result = response.Content.ReadAsStringAsync().Result; + if (!response.IsSuccessStatusCode) + return "Invalid Nightscout url - Error : " + response.ReasonPhrase; - try - { - var status = System.Text.Json.JsonSerializer.Deserialize(result); - - if (String.Equals(status.status, "ok", StringComparison.CurrentCultureIgnoreCase)) - return null; - - return "Nightscout status is " + status.status; - } - catch (System.Text.Json.JsonException) - { - return "Nightscout URL returned invalid staus " + result; - } + var result = response.Content.ReadAsStringAsync().Result; + try + { + var status = JsonSerializer.Deserialize(result); + return string.Equals(status.Status, "ok", StringComparison.CurrentCultureIgnoreCase) ? null : "Nightscout status is " + status.Status; + } + catch (JsonException) + { + return "Nightscout URL returned invalid staus " + result; } } catch (Exception ex) { return "Can not access Nightscout : Error " + ex.Message; } - } - } } diff --git a/GlucoseTrayCore/Services/StringEncryptionService.cs b/GlucoseTray/Services/StringEncryptionService.cs similarity index 94% rename from GlucoseTrayCore/Services/StringEncryptionService.cs rename to GlucoseTray/Services/StringEncryptionService.cs index a197b11..3f93a42 100644 --- a/GlucoseTrayCore/Services/StringEncryptionService.cs +++ b/GlucoseTray/Services/StringEncryptionService.cs @@ -3,7 +3,7 @@ using System.Security.Cryptography; using System.Text; -namespace GlucoseTrayCore.Services +namespace GlucoseTray.Services { /// /// Used example from here: https://tekeye.uk/visual_studio/encrypt-decrypt-c-sharp-string @@ -23,7 +23,7 @@ public static string EncryptString(string plainText, string passPhrase) byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); var password = new PasswordDeriveBytes(passPhrase, null); byte[] keyBytes = password.GetBytes(keysize / 8); - var symmetricKey = new RijndaelManaged(); + var symmetricKey = Aes.Create("AesManaged"); symmetricKey.Mode = CipherMode.CBC; ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes); var memoryStream = new MemoryStream(); @@ -42,7 +42,7 @@ public static string DecryptString(string cipherText, string passPhrase) byte[] cipherTextBytes = Convert.FromBase64String(cipherText); var password = new PasswordDeriveBytes(passPhrase, null); byte[] keyBytes = password.GetBytes(keysize / 8); - var symmetricKey = new RijndaelManaged(); + var symmetricKey = Aes.Create("AesManaged"); symmetricKey.Mode = CipherMode.CBC; ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes); var memoryStream = new MemoryStream(cipherTextBytes); @@ -61,7 +61,7 @@ public static bool IsEncrypted(string cipherText, string passPhrase) { var result = DecryptString(cipherText, passPhrase); } - catch (Exception e) + catch { isEncrypted = false; } diff --git a/GlucoseTrayCore/Services/TaskSchedulerService.cs b/GlucoseTray/Services/TaskSchedulerService.cs similarity index 87% rename from GlucoseTrayCore/Services/TaskSchedulerService.cs rename to GlucoseTray/Services/TaskSchedulerService.cs index 406a9f3..bff010b 100644 --- a/GlucoseTrayCore/Services/TaskSchedulerService.cs +++ b/GlucoseTray/Services/TaskSchedulerService.cs @@ -1,13 +1,12 @@ using Microsoft.Win32.TaskScheduler; using System; -using System.Diagnostics; using System.IO; -namespace GlucoseTrayCore.Services +namespace GlucoseTray.Services { public class TaskSchedulerService { - private readonly string ExecutablePath = "\"" + Process.GetCurrentProcess().MainModule.FileName + "\""; // Environment.ProcessPath should be available in the future for this + private readonly string ExecutablePath = "\"" + Environment.ProcessPath + "\""; private readonly string TaskName = "GlucoseTray-" + Environment.UserName; private readonly string WorkingDirectory = Directory.GetCurrentDirectory(); diff --git a/GlucoseTrayCore/Views/SettingsWindow.xaml b/GlucoseTray/Views/SettingsWindow.xaml similarity index 89% rename from GlucoseTrayCore/Views/SettingsWindow.xaml rename to GlucoseTray/Views/SettingsWindow.xaml index 6315d56..fa20873 100644 --- a/GlucoseTrayCore/Views/SettingsWindow.xaml +++ b/GlucoseTray/Views/SettingsWindow.xaml @@ -1,9 +1,9 @@ - @@ -67,11 +67,6 @@