public async Task <GlucoseResult> GetLatestReading() { var fetchResult = new GlucoseResult(); try { if (_options.FetchMethod == FetchMethod.DexcomShare) { await GetFetchResultFromDexcom(fetchResult).ConfigureAwait(false); } else if (_options.FetchMethod == FetchMethod.NightscoutApi) { await GetFetchResultFromNightscout(fetchResult).ConfigureAwait(false); } else { _logger.LogError("Invalid fetch method specified."); throw new InvalidOperationException("Fetch Method either not specified or invalid specification."); } fetchResult.IsCriticalLow = IsCriticalLow(fetchResult); } catch (Exception ex) { _logger.LogError("Failed to get data. {0}", ex); fetchResult = GetDefaultFetchResult(); } return(fetchResult); }
private async void BeginCycle() { while (true) { try { Application.DoEvents(); var results = await _fetchService.GetLatestReadings(GlucoseResult?.DateTimeUTC).ConfigureAwait(false); if (results.Any()) { GlucoseResult = results.Last(); } CreateIcon(); AlertNotification(); await Task.Delay(_options.CurrentValue.PollingThresholdTimeSpan); } catch (Exception e) { MessageBox.Show($"ERROR: {e}", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error); _logger.LogError(e.ToString()); trayIcon.Visible = false; trayIcon?.Dispose(); Environment.Exit(0); } } }
public async Task <List <GlucoseResult> > GetLatestReadings(DateTime?timeOflastGoodResult) { var fetchResult = new GlucoseResult(); var results = new List <GlucoseResult>(); try { if (_options.CurrentValue.FetchMethod == FetchMethod.DexcomShare) { await GetFetchResultFromDexcom(fetchResult).ConfigureAwait(false); results.Add(fetchResult); } else if (_options.CurrentValue.FetchMethod == FetchMethod.NightscoutApi) { results = await GetResultsFromNightscout(timeOflastGoodResult).ConfigureAwait(false); } else { _logger.LogError("Invalid fetch method specified."); throw new InvalidOperationException("Fetch Method either not specified or invalid specification."); } } catch (Exception ex) { _logger.LogError("Failed to get data. {0}", ex); } return(results); }
private async Task <List <GlucoseResult> > GetResultsFromNightscout(DateTime?timeOflastGoodResult) { var url = $"{_options.CurrentValue.NightscoutUrl?.TrimEnd('/')}/api/v1/entries/sgv?"; var count = 1; if (timeOflastGoodResult.HasValue) { count = 1000000; // Without sending a maximum count, Nightscout will only return 10 results. var fromDate = timeOflastGoodResult.Value.AddSeconds(1).ToString("s") + "Z"; var toDate = DateTime.UtcNow.ToString("s") + "Z"; url += $"find[dateString][$gte]={fromDate}&find[dateString][$lte]={toDate}&"; } url += $"count={count}"; url += !string.IsNullOrWhiteSpace(_options.CurrentValue.AccessToken) ? $"&token={_options.CurrentValue.AccessToken}" : string.Empty; var request = new HttpRequestMessage(HttpMethod.Get, new Uri(url)); request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); var results = new List <GlucoseResult>(); var client = _httpClientFactory.CreateClient(); try { var response = await client.SendAsync(request).ConfigureAwait(false); var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); var content = JsonSerializer.Deserialize <List <NightScoutResult> >(result).ToList(); foreach (var record in content) { var fetchResult = new GlucoseResult { Source = FetchMethod.NightscoutApi, DateTimeUTC = DateTime.Parse(record.dateString).ToUniversalTime(), Trend = record.direction.GetTrend() }; CalculateValues(fetchResult, record.sgv); if (fetchResult.Trend == TrendResult.Unknown) { _logger.LogWarning($"Un-expected value for direction/Trend {record.direction}"); } results.Add(fetchResult); } response.Dispose(); } catch (Exception ex) { _logger.LogError("Nightscout fetching failed or received incorrect format. {0}", ex); } finally { request.Dispose(); } return(results.OrderBy(a => a.DateTimeUTC).ToList()); }
private async Task CreateIcon() { GlucoseResult = await _fetchService.GetLatestReading().ConfigureAwait(false); LogResultToDb(GlucoseResult); trayIcon.Text = GetGlucoseMessage(GlucoseResult); _iconService.CreateTextIcon(GlucoseResult, trayIcon); }
private void LogResultToDb(GlucoseResult result) { if (_context.GlucoseResults.Any(g => g.DateTimeUTC == result.DateTimeUTC && !result.WasError && g.MgValue == result.MgValue)) { return; } _context.GlucoseResults.Add(result); _context.SaveChanges(); }
private async Task CheckForMissingReadings() { if (_options.FetchMethod != FetchMethod.NightscoutApi) { return; } var newestExistingRecord = _context.GlucoseResults.OrderByDescending(a => a.DateTimeUTC).FirstOrDefault(); if (newestExistingRecord == null) { if (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; } newestExistingRecord = new GlucoseResult() { DateTimeUTC = DateTime.UtcNow.AddYears(-100) }; } Stopwatch sw = new Stopwatch(); sw.Start(); var missingResults = await _fetchService.FetchMissingReadings(newestExistingRecord.DateTimeUTC); sw.Stop(); int count = missingResults.Count(); if (count > 0) { if (count == 1) { _logger.LogWarning($"Found 1 reading recorded at {missingResults[0].DateTimeUTC} UTC since last database record at {newestExistingRecord.DateTimeUTC} UTC "); } else { _logger.LogWarning($"Found {count} readings between {missingResults[0].DateTimeUTC} and {missingResults[count-1].DateTimeUTC} UTC since last database record at {newestExistingRecord.DateTimeUTC} UTC. 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"); } } }
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 async Task <GlucoseResult> GetFetchResultFromDexcom(GlucoseResult fetchResult) { var host = _options.DexcomServer switch { DexcomServerLocation.DexcomShare1 => "share1.dexcom.com", DexcomServerLocation.DexcomShare2 => "share2.dexcom.com", DexcomServerLocation.DexcomInternational => "shareous1.dexcom.com", _ => "share1.dexcom.com", }; // Get Session Id var request = new HttpRequestMessage(HttpMethod.Post, new Uri($"https://{host}/ShareWebServices/Services/General/LoginPublisherAccountByName")) { Content = new StringContent("{\"accountName\":\"" + _options.DexcomUsername + "\"," + "\"applicationId\":\"d8665ade-9673-4e27-9ff6-92db4ce13d13\"," + "\"password\":\"" + _options.DexcomPassword + "\"}", Encoding.UTF8, "application/json") }; var client = _httpClientFactory.CreateClient(); try { var response = await client.SendAsync(request).ConfigureAwait(false); var sessionId = (await response.Content.ReadAsStringAsync().ConfigureAwait(false)).Replace("\"", ""); request = new HttpRequestMessage(HttpMethod.Post, new Uri($"https://{host}/ShareWebServices/Services/Publisher/ReadPublisherLatestGlucoseValues?sessionId={sessionId}&minutes=1440&maxCount=1")); var result = JsonSerializer.Deserialize <List <DexcomResult> >(await(await client.SendAsync(request).ConfigureAwait(false)).Content.ReadAsStringAsync().ConfigureAwait(false)).First(); var unixTime = string.Join("", result.ST.Where(char.IsDigit)); var trend = result.Trend; CalculateValues(fetchResult, result.Value); fetchResult.DateTimeUTC = !string.IsNullOrWhiteSpace(unixTime) ? DateTimeOffset.FromUnixTimeMilliseconds(long.Parse(unixTime)).UtcDateTime : DateTime.MinValue; fetchResult.Trend = (TrendResult)trend; response.Dispose(); } catch (Exception ex) { _logger.LogError("Dexcom fetching failed or received incorrect format. {0}", ex); fetchResult = GetDefaultFetchResult(); } finally { request.Dispose(); } fetchResult.Source = FetchMethod.DexcomShare; return(fetchResult); }
private void CalculateValues(GlucoseResult result, double value) { // 25 MMOL is > 540 MG, most readers wont go below 40 or above 400. // Catch any full value MMOL readings that may be perfect integers that are delivered without a '.', i.e., 7.0 coming in as just 7 if (value.ToString().Contains(".") || value <= 30) { result.MmolValue = value; result.MgValue = Convert.ToInt32(value *= 18); } else { result.MgValue = Convert.ToInt32(value); result.MmolValue = value /= 18; } }
private void CalculateValues(GlucoseResult result, double value) { if (value == 0) { result.MmolValue = value; result.MgValue = Convert.ToInt32(value); } else if (_options.CurrentValue.IsServerDataUnitTypeMmol) { result.MmolValue = value; result.MgValue = Convert.ToInt32(value * 18); } else { result.MmolValue = value / 18; result.MgValue = Convert.ToInt32(value); } result.IsCriticalLow = IsCriticalLow(result); }
private async Task <GlucoseResult> GetFetchResultFromNightscout(GlucoseResult fetchResult) { var request = new HttpRequestMessage(HttpMethod.Get, new Uri($"{_options.NightscoutUrl}/api/v1/entries/sgv?count=1" + (!string.IsNullOrWhiteSpace(_options.AccessToken) ? $"&token={_options.AccessToken}" : ""))); request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); var client = _httpClientFactory.CreateClient(); try { var response = await client.SendAsync(request).ConfigureAwait(false); var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); var content = JsonSerializer.Deserialize <List <NightScoutResult> >(result).FirstOrDefault(); CalculateValues(fetchResult, content.sgv); fetchResult.DateTimeUTC = DateTime.Parse(content.dateString).ToUniversalTime(); fetchResult.Trend = content.direction.GetTrend(); if (fetchResult.Trend == TrendResult.Unknown) { _logger.LogWarning($"Un-expected value for direction/Trend {content.direction}"); } response.Dispose(); } catch (Exception ex) { _logger.LogError("Nightscout fetching failed or received incorrect format. {0}", ex); fetchResult = GetDefaultFetchResult(); } finally { request.Dispose(); } fetchResult.Source = FetchMethod.NightscoutApi; return(fetchResult); }
public static bool IsStale(this GlucoseResult fetchResult, int minutes) => (System.DateTime.Now.ToUniversalTime() - fetchResult.DateTimeUTC).TotalMinutes > minutes;
private bool IsCriticalLow(GlucoseResult result) => (_options.CurrentValue.GlucoseUnit == GlucoseUnitType.MMOL && result.MmolValue <= _options.CurrentValue.CriticalLowBg) || (_options.CurrentValue.GlucoseUnit == GlucoseUnitType.MG && result.MgValue <= _options.CurrentValue.CriticalLowBg);
public static bool IsStale(this GlucoseResult fetchResult, int minutes) { var ts = System.DateTime.Now.ToUniversalTime() - fetchResult.DateTimeUTC; return(ts.TotalMinutes > minutes); }
private string GetGlucoseMessage(GlucoseResult result) => $"{result.GetFormattedStringValue(_options.CurrentValue.GlucoseUnit)} {result.DateTimeUTC.ToLocalTime().ToLongTimeString()} {result.Trend.GetTrendArrow()}{result.StaleMessage(_options.CurrentValue.StaleResultsThreshold)}";
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" : string.Empty); }
public static string GetFormattedStringValue(this GlucoseResult fetchResult, GlucoseUnitType type) => type == GlucoseUnitType.MG ? fetchResult.MgValue.ToString() : fetchResult.MmolValue.ToString("0.0");