├── FloatingGlucose
├── installer
│ ├── Output
│ │ └── .gitignore
│ ├── floatingglucose.iss
│ └── floatingglucosenosign.iss
├── bin
│ └── .gitignore
├── obj
│ └── .gitignore
├── Resources
│ ├── alarm.mp3
│ ├── alarm2.mp3
│ ├── browse-file.ico
│ └── noun_335372_cc_v2.ico
├── noun_335372_cc_v2.ico
├── Classes
│ ├── Utils
│ │ ├── GlucoseAlarmStatusEnum.cs
│ │ ├── BGReading.cs
│ │ ├── Validators.cs
│ │ ├── DataProtectorConverter.cs
│ │ ├── DataProtector.cs
│ │ └── GlucoseMath.cs
│ ├── DataSources
│ │ ├── Exceptions
│ │ │ ├── NoSuchPluginException.cs
│ │ │ ├── NoPluginChosenException.cs
│ │ │ ├── MissingDataException.cs
│ │ │ ├── InvalidJsonDataException.cs
│ │ │ └── ConfigValidationException.cs
│ │ ├── Plugins
│ │ │ ├── DexcomShare
│ │ │ │ ├── DexcomShareNonUS.cs
│ │ │ │ └── DexcomShareUS.cs
│ │ │ ├── NightscoutPebbleEndpoint
│ │ │ │ ├── GeneratedNsData.cs
│ │ │ │ └── NightscoutPebbleEndpoint.cs
│ │ │ ├── NightscoutPebbleFileEndpoint.cs
│ │ │ ├── GlimpFileEndpoint.cs
│ │ │ └── YrWeatherService
│ │ │ │ ├── YrWeatherServiceEndpoint.cs
│ │ │ │ └── YrHourByHour.cs
│ │ ├── DataSourceInfo.cs
│ │ ├── IDataSourcePlugin.cs
│ │ └── PluginLoader.cs
│ ├── Extensions
│ │ ├── ColorExtensions.cs
│ │ ├── FormExtensions.cs
│ │ ├── DatetimeExtensions.cs
│ │ └── IDataSourcePluginExtensions.cs
│ ├── AppShared.cs
│ └── Audio
│ │ ├── LoopStream.cs
│ │ └── SoundAlarm.cs
├── Properties
│ ├── FormSettings.settings
│ ├── AssemblyInfo.cs
│ ├── FormSettings.Designer.cs
│ ├── Settings.settings
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ └── Settings.Designer.cs
├── README_attributions.txt
├── Program.cs
├── Settings.cs
├── FormWebbrowser.Designer.cs
├── FormWebbrowser.cs
├── buildscripts
│ └── create-zip-file.ps1
├── app.manifest
├── FloatingGlucose.csproj
├── App.config
├── FormWebbrowser.resx
├── FormGlucoseSettings.resx
├── .editorconfig
├── FloatingGlucose.resx
└── FloatingGlucose.Designer.cs
├── floatingglucose.png
├── .upgrade-assistant
├── .gitmodules
├── .github
└── workflows
│ └── FloatingGlucose.yaml
├── README.md
├── FloatingGlucose.sln
├── .gitignore
└── log.txt
/FloatingGlucose/installer/Output/.gitignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/floatingglucose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanselman/FloatingGlucose/master/floatingglucose.png
--------------------------------------------------------------------------------
/FloatingGlucose/bin/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanselman/FloatingGlucose/master/FloatingGlucose/bin/.gitignore
--------------------------------------------------------------------------------
/FloatingGlucose/obj/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanselman/FloatingGlucose/master/FloatingGlucose/obj/.gitignore
--------------------------------------------------------------------------------
/FloatingGlucose/Resources/alarm.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanselman/FloatingGlucose/master/FloatingGlucose/Resources/alarm.mp3
--------------------------------------------------------------------------------
/FloatingGlucose/Resources/alarm2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanselman/FloatingGlucose/master/FloatingGlucose/Resources/alarm2.mp3
--------------------------------------------------------------------------------
/FloatingGlucose/noun_335372_cc_v2.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanselman/FloatingGlucose/master/FloatingGlucose/noun_335372_cc_v2.ico
--------------------------------------------------------------------------------
/.upgrade-assistant:
--------------------------------------------------------------------------------
1 | {"Build":"0.2.226201\u002B4598a23e60b7571063570ee21e868f0f446b5b0f","CurrentProject":"","EntryPoints":[],"Properties":{}}
--------------------------------------------------------------------------------
/FloatingGlucose/Resources/browse-file.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanselman/FloatingGlucose/master/FloatingGlucose/Resources/browse-file.ico
--------------------------------------------------------------------------------
/FloatingGlucose/Resources/noun_335372_cc_v2.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanselman/FloatingGlucose/master/FloatingGlucose/Resources/noun_335372_cc_v2.ico
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "ShareClientDotNet"]
2 | path = ShareClientDotNet
3 | url = https://github.com/shanselman/ShareClientDotNet.git
4 | [submodule "winforms-datavisualization-net5"]
5 | path = winforms-datavisualization-net5
6 | url = https://github.com/AngeloCresta/winforms-datavisualization-net5.git
7 |
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Utils/GlucoseAlarmStatusEnum.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Text;
3 |
4 | namespace FloatingGlucose.Classes.Utils
5 | {
6 | public enum GlucoseAlarmStatusEnum
7 | {
8 | UrgentLow,
9 | Low,
10 | Normal,
11 | High,
12 | UrgentHigh,
13 | Unknown
14 | }
15 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Exceptions/NoSuchPluginException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FloatingGlucose.Classes.DataSources
4 | {
5 | internal class NoSuchPluginException : Exception
6 | {
7 | public NoSuchPluginException()
8 | {
9 | }
10 |
11 | public NoSuchPluginException(string message) : base(message)
12 | {
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Exceptions/NoPluginChosenException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FloatingGlucose.Classes.DataSources
4 | {
5 | internal class NoPluginChosenException : Exception
6 | {
7 | public NoPluginChosenException()
8 | {
9 | }
10 |
11 | public NoPluginChosenException(string message) : base(message)
12 | {
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Properties/FormSettings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0, 0, 0, 0
7 |
8 |
9 |
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Exceptions/MissingDataException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace FloatingGlucose.Classes.DataSources
6 | {
7 | internal class MissingDataException : Exception
8 | {
9 | public MissingDataException(string message) : base(message)
10 | {
11 | }
12 |
13 | public MissingDataException()
14 | {
15 | }
16 |
17 | public MissingDataException(string message, Exception inner) : base(message, inner)
18 | {
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Exceptions/InvalidJsonDataException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace FloatingGlucose.Classes.DataSources
6 | {
7 | internal class InvalidJsonDataException : Exception
8 | {
9 | public InvalidJsonDataException(string message) : base(message)
10 | {
11 | }
12 |
13 | public InvalidJsonDataException()
14 | {
15 | }
16 |
17 | public InvalidJsonDataException(string message, Exception inner) : base(message, inner)
18 | {
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Plugins/DexcomShare/DexcomShareNonUS.cs:
--------------------------------------------------------------------------------
1 | using ShareClientDotNet;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace FloatingGlucose.Classes.DataSources.Plugins
6 | {
7 | internal class DexcomShareNonUSEndpoint : DexcomShareEndpoint
8 | {
9 | //protected new ShareClient shareClient = new ShareClient(ShareServer.ServerNonUS);
10 | public override string DataSourceShortName => "Dexcom Share (Non-US)";
11 |
12 | public DexcomShareNonUSEndpoint()
13 | {
14 | this.shareClient.SetShareServer(ShareServer.ServerNonUS);
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/FloatingGlucose/README_attributions.txt:
--------------------------------------------------------------------------------
1 | Attributions:
2 |
3 | Application icon:
4 | This application's icon has kindly been provided by Marie Van den Broeck and modified by me.
5 | She maintains the copyright of the icon.
6 | https://creativecommons.org/licenses/by/3.0/us/
7 | The icon is under the creative commons license https://creativecommons.org/licenses/by/3.0/us/
8 | Please visit her site here: https://thenounproject.com/marie49/collection/health/?q=diabetes&i=335372s
9 |
10 | Alarm mp3s:
11 | This application uses alarms as defined by the nightscout foundation's remote-cgm-monitor project,
12 | please see https://github.com/nightscout/cgm-remote-monitor/tree/master/static/audio
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Extensions/ColorExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace FloatingGlucose.Classes.Extensions
7 | {
8 | internal static class ColorExtensions
9 | {
10 | public static string ToHexString(this Color color)
11 | {
12 | return color.ToArgb().ToString("X");
13 | }
14 |
15 | public static Color FromHexStringToColor(this String hexColor)
16 | {
17 | var argb = Convert.ToInt32(hexColor, 16);
18 | var color = Color.FromArgb(argb);//FF000000 - black with no transparency
19 | return color;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Extensions/FormExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace FloatingGlucose.Classes.Extensions
6 | {
7 | internal static class FormExtensions
8 | {
9 | public static void ShowDialogIfNonVisible(this FormGlucoseSettings form)
10 | {
11 | if (form.Visible)
12 | {
13 | return;
14 | }
15 | Debug.WriteLine("Stopping refresh timer as we are showing settings");
16 | AppShared.refreshGlucoseTimer?.Stop();
17 |
18 | AppShared.IsShowingSettings = true;
19 | form.ShowDialog();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Utils/BGReading.cs:
--------------------------------------------------------------------------------
1 | using FloatingGlucose.Classes.Extensions;
2 | using System;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace FloatingGlucose.Classes.Utils
7 | {
8 | internal class BgReading
9 | {
10 | public double _glucose;
11 | public DateTime DateReading;
12 |
13 | public double Timestamp => this.DateReading.ToUnixTimeStampMilliseconds();
14 | public double GlucoseMgdl => this._glucose;
15 | public double GlucoseMmol => this.GlucoseMgdl / 18.01559;
16 |
17 | public string GetRelativeGlucoseDirection(BgReading otherReading)
18 | {
19 | return GlucoseMath.GetGlucoseDirection(this, otherReading);
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Exceptions/ConfigValidationException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 |
4 | namespace FloatingGlucose.Classes.DataSources
5 | {
6 | [Serializable]
7 | internal class ConfigValidationException : Exception
8 | {
9 | public ConfigValidationException()
10 | {
11 | }
12 |
13 | public ConfigValidationException(string message) : base(message)
14 | {
15 | }
16 |
17 | public ConfigValidationException(string message, Exception innerException) : base(message, innerException)
18 | {
19 | }
20 |
21 | protected ConfigValidationException(SerializationInfo info, StreamingContext context) : base(info, context)
22 | {
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Program.cs:
--------------------------------------------------------------------------------
1 | using FloatingGlucose.Classes;
2 | using System;
3 | using System.Linq;
4 | using System.Windows.Forms;
5 |
6 | namespace FloatingGlucose
7 | {
8 | internal static class Program
9 | {
10 | ///
11 | /// The main entry point for the application.
12 | ///
13 | [STAThread]
14 | private static void Main()
15 | {
16 | Application.EnableVisualStyles();
17 | Application.SetCompatibleTextRenderingDefault(false);
18 | //Application.Run(new FormGlucoseSettings());
19 | //Application.Run(new FormAudioTester());
20 | //Application.Run(new AutoPositionedForm());
21 | //return;
22 | try
23 | {
24 | Application.Run(new FloatingGlucose());
25 | var manager = SoundAlarm.Instance;
26 | manager.StopAlarm();
27 | }
28 | catch (ObjectDisposedException)
29 | {
30 | // this happens when application.exit() is called when the form has a modal dialog open
31 | // ignore for now
32 | }
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/DataSourceInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace FloatingGlucose.Classes.DataSources
6 | {
7 | public class DataSourceInfo
8 | {
9 | public Type Type { get; set; }
10 | public string DataSourceShortName { get; set; }
11 | public string FullName { get; set; }
12 | public IDataSourcePlugin Instance { get; set; }
13 |
14 | public DataSourceInfo(Type plugin)
15 | {
16 | this.Type = plugin;
17 | this.Instance = (IDataSourcePlugin)Activator.CreateInstance(plugin);
18 | this.DataSourceShortName = this.Instance.DataSourceShortName;
19 | this.FullName = plugin.FullName;
20 | }
21 |
22 | public DataSourceInfo(IDataSourcePlugin plugin)
23 | {
24 | this.Type = plugin.GetType();
25 | this.Instance = plugin;
26 | this.DataSourceShortName = this.Instance.DataSourceShortName;
27 | this.FullName = this.Type.FullName;
28 | }
29 |
30 | public override string ToString()
31 | {
32 | return this.DataSourceShortName;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Plugins/NightscoutPebbleEndpoint/GeneratedNsData.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace FloatingGlucose.Classes.DataSources
6 | {
7 | //json2csharp.com
8 | public class GeneratedNsData
9 | {
10 | public List status { get; set; }
11 | public List bgs { get; set; }
12 | public List cals { get; set; }
13 | }
14 |
15 | public class Status
16 | {
17 | public long now { get; set; }
18 | }
19 |
20 | public class Bg
21 | {
22 | public string sgv { get; set; }
23 | public int trend { get; set; }
24 | public string direction { get; set; }
25 | public long datetime { get; set; }
26 | public double filtered { get; set; }
27 | public double unfiltered { get; set; }
28 | public double noise { get; set; }
29 | public string bgdelta { get; set; }
30 | public string battery { get; set; }
31 | }
32 |
33 | public class Cal
34 | {
35 | public double slope { get; set; }
36 | public double intercept { get; set; }
37 | public double scale { get; set; }
38 | }
39 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Utils/Validators.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace FloatingGlucose.Classes.Utils
7 | {
8 | internal class Validators
9 | {
10 | public static bool IsUrl(string url)
11 | {
12 | var isWellFormed = url != null && Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute);
13 | if (!isWellFormed)
14 | {
15 | return false;
16 | }
17 | System.Uri uriResult;
18 | return Uri.TryCreate(url, UriKind.Absolute, out uriResult) &&
19 | (
20 | (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps)
21 | );
22 | }
23 |
24 | public static bool IsReadableFile(string path)
25 | {
26 | var isFile = File.Exists(path);
27 | if (!isFile)
28 | {
29 | return false;
30 | }
31 |
32 | try
33 | {
34 | return File.ReadAllBytes(path) != null;
35 | }
36 | catch (Exception)
37 | {
38 | return false;
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("FloatingGlucose")]
8 | [assembly: AssemblyDescription("A windows app to display your glucose data from windows")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("Bjorns_opensource_utils")]
11 | [assembly: AssemblyProduct("FloatingGlucose")]
12 | [assembly: AssemblyCopyright("Copyright code.bjorninge.no© 2016")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("9006c00a-1ee6-407c-b236-b810ddea27bd")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.7")]
35 | [assembly: AssemblyFileVersion("1.0.0.8")]
--------------------------------------------------------------------------------
/.github/workflows/FloatingGlucose.yaml:
--------------------------------------------------------------------------------
1 | name: "CI Build"
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths-ignore:
8 | - '**/*.md'
9 | - '**/*.gitignore'
10 | - '**/*.gitattributes'
11 | workflow_dispatch:
12 | branches:
13 | - master
14 | paths-ignore:
15 | - '**/*.md'
16 | - '**/*.gitignore'
17 | - '**/*.gitattributes'
18 |
19 | jobs:
20 | build:
21 | name: Build
22 | runs-on: windows-latest
23 | env:
24 | DOTNET_CLI_TELEMETRY_OPTOUT: 1
25 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
26 | DOTNET_NOLOGO: true
27 | DOTNET_GENERATE_ASPNET_CERTIFICATE: false
28 | DOTNET_ADD_GLOBAL_TOOLS_TO_PATH: false
29 | DOTNET_MULTILEVEL_LOOKUP: 0
30 |
31 | steps:
32 | - uses: actions/checkout@v2
33 | with:
34 | submodules: recursive
35 |
36 | - name: Setup .NET Core SDK
37 | uses: actions/setup-dotnet@v1.8.0
38 | with:
39 | dotnet-version: 5.0.x
40 |
41 | - name: Restore
42 | run: dotnet restore
43 |
44 | - name: Build
45 | run: dotnet build --configuration Release --no-restore
46 |
47 | - name: Test
48 | run: dotnet test
49 |
50 | - name: Publish
51 | run: dotnet publish --configuration Release --runtime=win10-x64 --output ./publish -p:PublishReadyToRun=true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true --self-contained=true -p:DebugType=None .\FloatingGlucose\FloatingGlucose.csproj
52 |
53 | - name: Upload a Build Artifact
54 | uses: actions/upload-artifact@v2.2.3
55 | with:
56 | name: self-contained-build
57 | path: ./publish
58 |
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/IDataSourcePlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.Specialized;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace FloatingGlucose.Classes.DataSources
9 | {
10 | public interface IDataSourcePlugin
11 | {
12 | string Acknowledgment { get; }
13 | string AcknowledgmentUrl { get; }
14 | string Author { get; }
15 | bool RequiresUserNameAndPassword { get; }
16 | bool RequiresDataSource { get; }
17 | int SortOrder { get; }
18 | bool PluginHandlesFormatting { get; }
19 |
20 | List HandleFormatting();
21 |
22 | DateTime Date { get; }
23 |
24 | double Glucose { get; }
25 | double PreviousGlucose { get; }
26 | double Delta { get; }
27 | double RawDelta { get; }
28 |
29 | //this is a text describing in which direction the current glucose trend is heading
30 | string Direction { get; }
31 |
32 | double RawGlucose { get; }
33 | double PreviousRawGlucose { get; }
34 |
35 | double RoundedDelta();
36 |
37 | double RoundedRawDelta();
38 |
39 | DateTime LocalDate { get; }
40 |
41 | string DataSourceShortName { get; }
42 |
43 | Task GetDataSourceDataAsync(NameValueCollection datapath);
44 |
45 | bool VerifyConfig(Properties.Settings settings);
46 |
47 | //GUI related
48 | void OnPluginSelected(FormGlucoseSettings settingsForm);
49 |
50 | bool RequiresBrowseButton { get; }
51 | string BrowseDialogFileFilter { get; }
52 | bool PluginDisabled { get; }
53 | }
54 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Properties/FormSettings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace FloatingGlucose.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
16 | internal sealed partial class FormSettings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static FormSettings defaultInstance = ((FormSettings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new FormSettings())));
19 |
20 | public static FormSettings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 |
26 | [global::System.Configuration.UserScopedSettingAttribute()]
27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
28 | [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
29 | public global::System.Drawing.Rectangle WindowPosition {
30 | get {
31 | return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
32 | }
33 | set {
34 | this["WindowPosition"] = value;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/FloatingGlucose/Settings.cs:
--------------------------------------------------------------------------------
1 | namespace FloatingGlucose.Properties
2 | {
3 | // This class allows you to handle specific events on the settings class:
4 | // The SettingChanging event is raised before a setting's value is changed.
5 | // The PropertyChanged event is raised after a setting's value is changed.
6 | // The SettingsLoaded event is raised after the setting values are loaded.
7 | // The SettingsSaving event is raised before the setting values are saved.
8 | public sealed partial class Settings
9 | {
10 | public Settings()
11 | {
12 | // // To add event handlers for saving and changing settings, uncomment the lines below:
13 | //
14 | // this.SettingChanging += this.SettingChangingEventHandler;
15 | //
16 | // this.SettingsSaving += this.SettingsSavingEventHandler;
17 | //
18 | this.SettingsLoaded += this.SettingsLoadedEventHandler;
19 | }
20 |
21 | private void SettingsLoadedEventHandler(object sender, System.Configuration.SettingsLoadedEventArgs e)
22 | {
23 | // Add code to handle the SettingChangingEvent event here.
24 | //Debug.WriteLine("Got external settings loaded event");
25 | //AppShared.NotifyFormSettingsHaveChanged();
26 | }
27 |
28 | private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e)
29 | {
30 | // Add code to handle the SettingChangingEvent event here.
31 | }
32 |
33 | private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e)
34 | {
35 | // Add code to handle the SettingsSaving event here.
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/AppShared.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Windows.Forms;
6 |
7 | namespace FloatingGlucose.Classes
8 | {
9 | internal class AppShared
10 | {
11 | #if DEBUG
12 | public static bool isDebuggingBuild => true;
13 | #else
14 | public static bool isDebuggingBuild => false;
15 | #endif
16 |
17 | public static bool IsWorkStationLocked = false;
18 | public static bool IsShowingSettings = false;
19 | public static readonly string AppName = typeof(Program).Assembly.GetName().Name;
20 |
21 | public static string AppVersion
22 | {
23 | get
24 | {
25 | try
26 | {
27 | using (var reader = File.OpenText(Path.Combine(AppShared.ExecutableDir, "version.txt")))
28 | {
29 | return reader.ReadToEnd();
30 | }
31 | }
32 | catch (IOException)
33 | {
34 | return "unknown";
35 | }
36 | }
37 | }
38 |
39 | public static System.Windows.Forms.Timer refreshGlucoseTimer;
40 | public static string ExecutableDir => Path.GetDirectoryName(Application.ExecutablePath);
41 |
42 | public static bool SettingsFormShouldFocusAdvancedSettings = false;
43 | public static bool SettingsUpdatedSuccessfully = false;
44 |
45 | private static Func callback;
46 |
47 | public static void RegisterSettingsChangedCallback(Func lambda)
48 | {
49 | AppShared.callback = lambda;
50 | }
51 |
52 | public static void NotifyFormSettingsHaveChanged() => callback?.Invoke();
53 | }
54 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Extensions/DatetimeExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace FloatingGlucose.Classes.Extensions
6 | {
7 | internal static class DatetimeExtensions
8 | {
9 | public static double ToUnixTimeStampMilliseconds(this DateTime dt)
10 | {
11 | return dt.ToUniversalTime().Subtract(
12 | new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
13 | ).TotalMilliseconds;
14 | }
15 |
16 | public static string ToTimeAgo(this DateTime dt)
17 | {
18 | TimeSpan span = DateTime.Now - dt;
19 | if (span.Days > 365)
20 | {
21 | int years = (span.Days / 365);
22 | if (span.Days % 365 != 0)
23 | years += 1;
24 | return String.Format("{0} {1} ago",
25 | years, years == 1 ? "year" : "years");
26 | }
27 | if (span.Days > 30)
28 | {
29 | int months = (span.Days / 30);
30 | if (span.Days % 31 != 0)
31 | months += 1;
32 | return String.Format("{0} {1} ago",
33 | months, months == 1 ? "month" : "months");
34 | }
35 | if (span.Days > 0)
36 | return String.Format("{0} {1} ago",
37 | span.Days, span.Days == 1 ? "day" : "days");
38 | if (span.Hours > 0)
39 | return String.Format("{0} {1} ago",
40 | span.Hours, span.Hours == 1 ? "hour" : "hours");
41 | if (span.Minutes > 0)
42 | return String.Format("{0} {1} ago",
43 | span.Minutes, span.Minutes == 1 ? "minute" : "minutes");
44 |
45 | return "now";
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Extensions/IDataSourcePluginExtensions.cs:
--------------------------------------------------------------------------------
1 | using FloatingGlucose.Classes.DataSources;
2 | using System.Linq;
3 | using System.Text;
4 | using System;
5 | using System.Diagnostics;
6 | using static FloatingGlucose.Properties.Settings;
7 |
8 | namespace FloatingGlucose.Classes.Extensions
9 | {
10 | internal static class IDataSourcePluginExtensions
11 | {
12 | public static bool UserWantsMmolUnits(this IDataSourcePlugin plugin) => Default.GlucoseUnits == "mmol";
13 |
14 | public static string FormattedDelta(this IDataSourcePlugin plugin)
15 | {
16 | return $"{(plugin.RoundedDelta() >= 0.0 ? "+" : "")}{plugin.RoundedDelta():N1}";
17 | }
18 |
19 | public static string FormattedRawDelta(this IDataSourcePlugin plugin)
20 | {
21 | return $"{(plugin.RoundedRawDelta() >= 0.0 ? "+" : "")}{plugin.RoundedRawDelta():N1}";
22 | }
23 |
24 | public static void WriteDebug(this IDataSourcePlugin plugin, string line)
25 | {
26 | var now = DateTime.Now.ToUniversalTime();
27 | Debug.WriteLine(now + ":" + line);
28 | }
29 |
30 | public static string DirectionArrow(this IDataSourcePlugin plugin)
31 | {
32 | switch (plugin.Direction)
33 | {
34 | case "DoubleUp":
35 | return "⇈";
36 |
37 | case "SingleUp":
38 | return "↑";
39 |
40 | case "FortyFiveUp":
41 | return "↗";
42 |
43 | case "Flat":
44 | return "→";
45 |
46 | case "FortyFiveDown":
47 | return "↘";
48 |
49 | case "SingleDown":
50 | return "↓";
51 |
52 | case "DoubleDown":
53 | return "⇊";
54 |
55 | case "NOT COMPUTABLE":
56 | return "-";
57 |
58 | case "OUT OF RANGE":
59 | return "⇕";
60 |
61 | case "":
62 | return "";
63 |
64 | default:
65 | case "None":
66 | return "⇼";
67 | }
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FloatingGlucose
2 | FloatingGlucose is a windows program aiming to display your current bloogsugars live on your desktop.
3 |
4 | A transparent popup window will be displayed in the bottom right corner of your desktop, with your current blood glucose values fetched from your nightscout site.
5 |
6 | ## Screenshot
7 | 
8 |
9 | ## Installation
10 | Download The Setup.exe of the latest release, [which you can find here](https://github.com/dabear/FloatingGlucose/releases/) and run it. You should normally just install it to the default directory which is C:\Program Files (x86)\FloatingGlucose.
11 |
12 |
13 | ## Plugins ##
14 |
15 |
16 |
17 | - Dexcom Share Dexcom share plugins by Bjørn inge Vikhammermo. Connects to Dexcom share (both US and Non-US) servers to fetch glucose data
18 |
19 | - Nightscout Nightscout plugin by Bjørn inge Vikhammermo. The original - fetches glucose from a specified nightscout site.
20 |
21 |
22 |
23 | ## Running the program
24 | Should be as simple as double clicking the app. A settings dialog will appear asking you to specify an endpoint (plugin) you'd like to connect to
25 |
26 | ## Hidden / Nice to Know features
27 | * You can drag the window around and place it where you want to.
28 | * To show the settings again, please right click the notification area icon or the app. A menu will pop up allowing you to access the application settings
29 | * The same pop up menu can be used to temporarily disable sound alarms.
30 |
31 | ## Debugging
32 | Having troubles with crashes or with the widget not updating? Please make sure you have enabled exception logging to stderr (in the configfile) then run the program from command line:
33 |
34 | ```bash
35 | cd "C:\Program Files (x86)\FloatingGlucose"
36 | FloatingGlucose.exe 2> glucose_log.txt
37 | ```
38 |
39 | Then open glucose_log.txt
40 |
41 | ## Settings files
42 | Please note that you can edit the settings from inside the application. If you still need to access the conig files directly, please go to either of these locations
43 |
44 | * Installation directory, and open FloatingGlucose.exe.config
45 | * %localappdata%\Bjorns_opensource_utils and locate user.config
46 |
47 | The user.config file will override any values inside the FloatingGlucose.exe.config file
48 |
49 |
50 |
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Utils/DataProtectorConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace FloatingGlucose.Classes.Utils
7 | {
8 | public class DataProtectorConverter : TypeConverter
9 | {
10 | public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
11 | {
12 | return sourceType == typeof(string);
13 | }
14 |
15 | public override object ConvertFrom(
16 | ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
17 | {
18 | if (value is string)
19 | {
20 | //converts from encrypted text settings into plain text
21 | var val = (string)value;
22 | DataProtector protector;
23 | if (val == null || (val != null && val.Length == 0))
24 | {
25 | protector = new DataProtector("");
26 | }
27 | else
28 | {
29 | val = DataProtector.ConvertFromSecureTextString(DataProtector.Base64Decode(val));
30 |
31 | protector = new DataProtector(val);
32 | }
33 |
34 | return protector;
35 | }
36 | return base.ConvertFrom(context, culture, value);
37 | }
38 |
39 | public override object ConvertTo(
40 | ITypeDescriptorContext context, System.Globalization.CultureInfo culture,
41 | object value, Type destinationType)
42 | {
43 | if (destinationType == typeof(string))
44 | {
45 | // Room room = value as Room;
46 | // return string.Format("{0},{1}", room.RoomNumber, room.Location);
47 |
48 | var val = (DataProtector)value;
49 | //encrypts the plaintext and base64 encodes it for storage
50 | if (val == null || val.Text == null || val.Text.Length == 0)
51 | {
52 | return "";
53 | }
54 | else
55 | {
56 | return
57 | DataProtector.Base64Encode(DataProtector.ConvertToSecureTextString(val.Text));
58 | }
59 | }
60 | return base.ConvertTo(context, culture, value, destinationType);
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Audio/LoopStream.cs:
--------------------------------------------------------------------------------
1 | using NAudio.Wave;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace FloatingGlucose.Classes
6 | {
7 | ///
8 | /// Stream for looping playback
9 | ///
10 | public class LoopStream : WaveStream
11 | {
12 | private WaveStream sourceStream;
13 |
14 | ///
15 | /// Creates a new Loop stream
16 | ///
17 | /// The stream to read from. Note: the Read method of this stream should return 0 when it reaches the end
18 | /// or else we will not loop to the start again.
19 | public LoopStream(WaveStream sourceStream)
20 | {
21 | this.sourceStream = sourceStream;
22 | this.EnableLooping = true;
23 | }
24 |
25 | ///
26 | /// Use this to turn looping on or off
27 | ///
28 | public bool EnableLooping { get; set; }
29 |
30 | ///
31 | /// Return source stream's wave format
32 | ///
33 | public override WaveFormat WaveFormat
34 | {
35 | get { return sourceStream.WaveFormat; }
36 | }
37 |
38 | ///
39 | /// LoopStream simply returns
40 | ///
41 | public override long Length
42 | {
43 | get { return sourceStream.Length; }
44 | }
45 |
46 | ///
47 | /// LoopStream simply passes on positioning to source stream
48 | ///
49 | public override long Position
50 | {
51 | get { return sourceStream.Position; }
52 | set { sourceStream.Position = value; }
53 | }
54 |
55 | public override int Read(byte[] buffer, int offset, int count)
56 | {
57 | int totalBytesRead = 0;
58 |
59 | while (totalBytesRead < count)
60 | {
61 | int bytesRead = sourceStream.Read(buffer, offset + totalBytesRead, count - totalBytesRead);
62 | if (bytesRead == 0)
63 | {
64 | if (sourceStream.Position == 0 || !EnableLooping)
65 | {
66 | // something wrong with the source stream
67 | break;
68 | }
69 | // loop
70 | sourceStream.Position = 0;
71 | }
72 | totalBytesRead += bytesRead;
73 | }
74 | return totalBytesRead;
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/FloatingGlucose/FormWebbrowser.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace FloatingGlucose
2 | {
3 | partial class FormWebbrowser
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.webBrowser1 = new System.Windows.Forms.WebBrowser();
32 | this.SuspendLayout();
33 | //
34 | // webBrowser1
35 | //
36 | this.webBrowser1.Dock = System.Windows.Forms.DockStyle.Fill;
37 | this.webBrowser1.Location = new System.Drawing.Point(0, 0);
38 | this.webBrowser1.MinimumSize = new System.Drawing.Size(20, 20);
39 | this.webBrowser1.Name = "webBrowser1";
40 | this.webBrowser1.Size = new System.Drawing.Size(1042, 586);
41 | this.webBrowser1.TabIndex = 0;
42 | this.webBrowser1.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.webBrowser1_DocumentCompleted);
43 | this.webBrowser1.Navigated += new System.Windows.Forms.WebBrowserNavigatedEventHandler(this.webBrowser1_Navigated);
44 | //
45 | // FormWebbrowser
46 | //
47 | this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
48 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
49 | this.ClientSize = new System.Drawing.Size(1042, 586);
50 | this.Controls.Add(this.webBrowser1);
51 | this.Name = "FormWebbrowser";
52 | this.Text = "Webbrowser";
53 | this.Load += new System.EventHandler(this.FormWebbrowser_Load);
54 | this.ResumeLayout(false);
55 |
56 | }
57 |
58 | #endregion
59 |
60 | private System.Windows.Forms.WebBrowser webBrowser1;
61 | }
62 | }
--------------------------------------------------------------------------------
/FloatingGlucose/FormWebbrowser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Linq;
4 | using System.Security.Permissions;
5 | using System.Text;
6 | using System.Windows.Forms;
7 |
8 | namespace FloatingGlucose
9 | {
10 | // [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
11 | [System.Runtime.InteropServices.ComVisibleAttribute(true)]
12 | public partial class FormWebbrowser : Form
13 | {
14 | private string injectScript = "";
15 |
16 | //private string _webpageReturnValue = "/place/Norway/Sør-Trøndelag/Trondheim/Trondheim/";
17 | private string _webpageReturnValue = "";
18 |
19 | public string WebPageReturnValue => this._webpageReturnValue;
20 |
21 | private bool disableScrollBars = true;
22 |
23 | public void SetReturnValueAndClose(string value)
24 | {
25 | this._webpageReturnValue = value;
26 | this.Close();
27 | }
28 |
29 | public FormWebbrowser(string injectScript, bool disableScrollBars = true)
30 | {
31 | InitializeComponent();
32 | this.injectScript = injectScript;
33 |
34 | var wb = this.webBrowser1;
35 |
36 | wb.ObjectForScripting = this;
37 |
38 | //if (!AppShared.isDebuggingBuild)
39 | //{
40 | wb.AllowWebBrowserDrop = false;
41 | wb.IsWebBrowserContextMenuEnabled = false;
42 | wb.WebBrowserShortcutsEnabled = false;
43 | wb.ScriptErrorsSuppressed = true;
44 | //}
45 | this.disableScrollBars = disableScrollBars;
46 | }
47 |
48 | public void SetBrowserUrl(string url)
49 | {
50 | this.webBrowser1.Url = new Uri(url);
51 | }
52 |
53 | public string InvokeScript(string scriptName)
54 | {
55 | return this.webBrowser1.Document.InvokeScript(scriptName).ToString();
56 | }
57 |
58 | private void FormWebbrowser_Load(object sender, EventArgs e)
59 | {
60 | }
61 |
62 | private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
63 | {
64 | }
65 |
66 | private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
67 | {
68 | var doc = this.webBrowser1.Document;
69 |
70 | if (this.injectScript != null)
71 | {
72 | var head = doc.GetElementsByTagName("head")[0];
73 | var s = doc.CreateElement("script");
74 | s.SetAttribute("text", this.injectScript);
75 | head.AppendChild(s);
76 | Debug.WriteLine("Inserted script into web page!");
77 | }
78 |
79 | if (this.disableScrollBars)
80 | {
81 | doc.Body.Style = "overflow:hidden";
82 | }
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/PluginLoader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using static FloatingGlucose.Properties.Settings;
6 |
7 | namespace FloatingGlucose.Classes.DataSources
8 | {
9 | public class PluginLoader
10 | {
11 | private static readonly PluginLoader instance = new PluginLoader();
12 | private IDataSourcePlugin plugin;
13 | private List loadedPlugins;
14 |
15 | public List GetAllPlugins(bool forceReload = false)
16 | {
17 | if (forceReload || loadedPlugins == null)
18 | {
19 | var list = new List();
20 |
21 | // Fill the list of plugins, these are fairly static and won't change during runtime
22 | var type = typeof(IDataSourcePlugin);
23 | var allPlugins = AppDomain.CurrentDomain.GetAssemblies()
24 | .SelectMany((x) => x.GetTypes().Where((y) => type.IsAssignableFrom(y) && !y.IsInterface));
25 |
26 | foreach (Type plugin in allPlugins)
27 | {
28 | var dsinfo = new DataSourceInfo(plugin);
29 | if (!dsinfo.Instance.PluginDisabled)
30 | {
31 | list.Add(dsinfo);
32 | }
33 | }
34 | list.Sort((x, y) => x.FullName.CompareTo(y.FullName));
35 | this.loadedPlugins = list;
36 | }
37 |
38 | return this.loadedPlugins;
39 | }
40 |
41 | public IDataSourcePlugin GetActivePlugin()
42 | {
43 | if (this.plugin != null)
44 | {
45 | return this.plugin;
46 | }
47 | //use default plugin from config file
48 | // this can change between run
49 | var fullname = Default.DataSourceFullName;
50 | if (fullname.Length == 0)
51 | {
52 | throw new NoPluginChosenException("");
53 | }
54 | return this.SetActivePlugin(fullname);
55 | }
56 |
57 | public IDataSourcePlugin SetActivePlugin(string dataSourceFullName)
58 | {
59 | try
60 | {
61 | this.plugin = this.GetAllPlugins().Where((x) => x.FullName == dataSourceFullName).First().Instance;
62 | }
63 | catch (InvalidOperationException)
64 | {
65 | throw new NoSuchPluginException("Invalid plugin: " + dataSourceFullName);
66 | }
67 | return this.plugin;
68 | }
69 |
70 | // Explicit static constructor to tell C# compiler
71 | // not to mark type as beforefieldinit
72 | static PluginLoader()
73 | {
74 | }
75 |
76 | private PluginLoader()
77 | {
78 | }
79 |
80 | public static PluginLoader Instance => instance;
81 | }
82 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Plugins/NightscoutPebbleFileEndpoint.cs:
--------------------------------------------------------------------------------
1 | using FloatingGlucose.Classes.Utils;
2 | using Newtonsoft.Json;
3 | using System;
4 | using System.Collections.Specialized;
5 | using System.Globalization;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Net.Http;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 |
12 | namespace FloatingGlucose.Classes.DataSources.Plugins
13 | {
14 | internal class NightscoutPebbleFileEndpoint : NightscoutPebbleEndpoint, IDataSourcePlugin
15 | {
16 | public override bool RequiresUserNameAndPassword => false;
17 | public override bool RequiresDataSource => true;
18 | public override bool PluginDisabled => false;
19 | public override bool RequiresBrowseButton => true;
20 | public override string BrowseDialogFileFilter => "Nightscout Dumps|*.nsdata.dump;*.nsdata|Text files|*.txt";
21 | public override string DataSourceShortName => "Nightscout File Dump";
22 | public override int SortOrder => 11;
23 |
24 | public override void OnPluginSelected(FormGlucoseSettings form)
25 | {
26 | form.lblDataSourceLocation.Text = "Your File Dump location";
27 | var source = form.txtDataSourceLocation.Text.ToLower();
28 | //for this plugin, we don't handle http:// and https://
29 | //if the source does not resemble an url, it should clearly be removed.
30 | if (source.StartsWith("http://") || source.StartsWith("https://"))
31 | {
32 | form.txtDataSourceLocation.Text = "";
33 | }
34 | }
35 |
36 | public override bool VerifyConfig(Properties.Settings settings)
37 | {
38 | if (!Validators.IsReadableFile(settings.DataPathLocation))
39 | {
40 | throw new ConfigValidationException("You have entered an invalid file path for the data dump!");
41 | }
42 |
43 | return true;
44 | }
45 |
46 | public override async Task GetDataSourceDataAsync(NameValueCollection locations)
47 | {
48 | var datapath = locations["raw"];
49 | var client = new HttpClient();
50 | string fileContents;
51 |
52 | // datapath is expected to be a valid file
53 | // Exceptions will be handled by the main program
54 | using (var reader = File.OpenText(datapath))
55 | {
56 | fileContents = await reader.ReadToEndAsync();
57 | }
58 |
59 | Bg bgs = null;
60 | var parsed =
61 | this.NsData = JsonConvert.DeserializeObject(fileContents);
62 |
63 | bgs = parsed.bgs.First();
64 | this.Direction = bgs.direction;
65 | this.Glucose = Double.Parse(bgs.sgv, NumberStyles.Any, CultureInfo.InvariantCulture);
66 | this.Date = DateTimeOffset.FromUnixTimeMilliseconds(bgs.datetime).DateTime;
67 | this.Delta = Double.Parse(bgs.bgdelta, NumberStyles.Any, CultureInfo.InvariantCulture);
68 |
69 | return this;
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Utils/DataProtector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.ComponentModel;
4 | using System.Configuration;
5 | using System.Linq;
6 | using System.Security.Cryptography;
7 | using System.Text;
8 |
9 | namespace FloatingGlucose.Classes.Utils
10 | {
11 | [TypeConverter(typeof(DataProtectorConverter))]
12 | [SettingsSerializeAs(SettingsSerializeAs.String)]
13 | public class DataProtector
14 | {
15 | ///
16 | /// This class implements some of the best practices for generating a secure string that can only be read
17 | /// by the current user. However, as it is untested security wise, this can only be considered as secure as
18 | /// a two way hash, where the encrpytion key is stored on a per user setting.
19 | ///
20 | ///
21 |
22 | private static BitArray aditionalEntropy = new BitArray(new byte[7] { 64, 3, 16, 32, 28, 99, 33 });
23 | private static BitArray nr2 = new BitArray(new byte[7] { 7, 9, 3, 2, 8, 9, 3 });
24 |
25 | public string Text { get; set; }
26 |
27 | public DataProtector()
28 | {
29 | }
30 |
31 | public DataProtector(string text)
32 | {
33 | this.Text = text;
34 | }
35 |
36 | public static string Base64Encode(string plainText)
37 | {
38 | var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
39 | return System.Convert.ToBase64String(plainTextBytes);
40 | }
41 |
42 | public static string Base64Decode(string base64EncodedData)
43 | {
44 | var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
45 | return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
46 | }
47 |
48 | public static string ConvertToSecureTextString(string password)
49 | {
50 | var data = System.Text.ASCIIEncoding.Default.GetBytes(password);
51 | var converted = ConvertToBytes((aditionalEntropy.Clone() as BitArray).Xor(nr2));
52 | var protecteddata = ProtectedData.Protect(data, converted, DataProtectionScope.CurrentUser);
53 | return System.Text.Encoding.Default.GetString(protecteddata);
54 | }
55 |
56 | public static string ConvertFromSecureTextString(string securestring)
57 | {
58 | var data = System.Text.ASCIIEncoding.Default.GetBytes(securestring);
59 | var converted = ConvertToBytes((aditionalEntropy.Clone() as BitArray).Xor(nr2));
60 | var unprotecteddata = ProtectedData.Unprotect(data, converted, DataProtectionScope.CurrentUser);
61 |
62 | return System.Text.Encoding.Default.GetString(unprotecteddata);
63 | }
64 |
65 | public static byte[] ConvertToBytes(BitArray bits)
66 | {
67 | byte[] ret = new byte[(bits.Length - 1) / 8 + 1];
68 | bits.CopyTo(ret, 0);
69 | return ret;
70 | }
71 |
72 | public static string ConvertByteArrayToIntString(byte[] array)
73 | {
74 | return ((Int32)(BitConverter.ToInt16(array, 0))).ToString();
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/FloatingGlucose/buildscripts/create-zip-file.ps1:
--------------------------------------------------------------------------------
1 | # Out-Zip.msh
2 | # creates out-Zip function
3 | # /\/\o\/\/ 2006
4 | # http://mow001.blogspot.com
5 |
6 | function out-zip($zipfilename, $files) {
7 | #Load some assemblys. (No line break!)
8 | [System.Reflection.Assembly]::Load("WindowsBase,
9 | Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35") | Out-Null
10 |
11 | #Create a zip file named "MyZipFile.zip". (No line break!)
12 | $ZipPackage=[System.IO.Packaging.ZipPackage]::Open("$zipfilename",
13 | [System.IO.FileMode]"OpenOrCreate", [System.IO.FileAccess]"ReadWrite")
14 |
15 | #For each file you want to add, we must extract the bytes
16 | #and add them to a part of the zip file.
17 | ForEach ($file In $files)
18 | {
19 | $partName=New-Object System.Uri(( "/"+ $file.Name) , [System.UriKind]"Relative")
20 | #Create each part. (No line break!)
21 | $part=$ZipPackage.CreatePart($partName, "application/zip",
22 | [System.IO.Packaging.CompressionOption]"Maximum")
23 | $bytes=[System.IO.File]::ReadAllBytes($file)
24 | $stream=$part.GetStream()
25 | $stream.Write($bytes, 0, $bytes.Length)
26 | $stream.Close()
27 | }
28 |
29 | #Close the package when we're done.
30 | $ZipPackage.Close()
31 | }
32 |
33 | function getGitTagOrReleaseName() {
34 | $log = (git log --pretty=oneline --decorate | select -first 1)
35 | #$log = (git log --pretty=oneline --decorate | select -Index 3)
36 | $commit_id = "commit-" + ($log.Split(" ") | select -first 1).substring(0,8)
37 |
38 | $changes = (git status -s --porcelain)
39 | $has_uncommitted_changes = ($changes.length -ne 0)
40 |
41 | #echo "has changes?"$has_uncommitted_changes
42 | #echo "last id:"$commit_id
43 |
44 | if($log -match "tag: (?.*?)[,)]") {
45 | #basically, if there is a release associated with this commit, use that release isntead of commit
46 | $commit_id = $Matches["ver"]
47 | }
48 |
49 | if($has_uncommitted_changes) {
50 | $version = $commit_id+"+changes"
51 |
52 | } else {
53 | $version = $commit_id
54 |
55 | }
56 | return $version
57 | }
58 |
59 |
60 |
61 | function getGitReleaseZipFileName() {
62 | #$projectname = "FloatingGlucose"
63 | $projectname = $env:projectname
64 | $buildconfigname = ($env:buildconfigname).toLower()
65 | $version = getGitTagOrReleaseName
66 | return "standalone-$projectname-$version-$buildconfigname.zip"
67 |
68 | }
69 |
70 | function writeVersionFile(){
71 | $buildver = getGitTagOrReleaseName
72 | #echo $buildver > version.txt #|out-file "version.txt" -Encoding "ascii"
73 | #writealltext must be used to avoid newline
74 | [System.IO.File]::WriteAllText("version.txt", $buildver)
75 | echo "wrote $buildver to version.txt"
76 | }
77 |
78 | function createReleaseZipFile(){
79 |
80 | $zipfilename = getGitReleaseZipFileName
81 | $dir = pwd
82 | $outpath = "$dir\$zipfilename"
83 |
84 | $files = ls . | where { -not ($_.name.EndsWith(".vshost.exe") -or $_.name.EndsWith(".zip")) }
85 |
86 | echo "writing zip to $outpath"
87 | $files | out-zip -zipfilename $outpath -files $files
88 | return $zipfilename
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/FloatingGlucose/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
52 |
53 |
58 |
59 |
60 |
61 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/FloatingGlucose/installer/floatingglucose.iss:
--------------------------------------------------------------------------------
1 | ; Script generated by the Inno Setup Script Wizard.
2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
3 |
4 | #define MyAppName "FloatingGlucose"
5 |
6 | #define MyAppPublisher "code.bjorninge.no"
7 | #define MyAppURL "http://code.bjorninge.no"
8 | #define MyAppExeName "FloatingGlucose.exe"
9 | #define SourceDir ".."
10 | ;should be set via iscc
11 | ;cd installer
12 | ;"%programfiles(x86)%/Inno Setup 5/ISCC.exe" floatingglucose.iss
13 | ;#define MyAppVersion "0.9.9test"
14 | ;#define ReleaseType "Release"
15 |
16 | ;setting up signing:
17 | ; in inno setup, go to tools->configure sign tools
18 | ; add a new tool, call it "signtool" with the following value
19 | ; "C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe" sign /fd sha256 /tr http://time.certum.pl/ /sha1 0B360481F3F59C7F398FB0417AA4B341EB018906 $f
20 | [Setup]
21 | ; NOTE: The value of AppId uniquely identifies this application.
22 | ; Do not use the same AppId value in installers for other applications.
23 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
24 | SignTool=signtool /d $qFloatingGlucoseInstaller$q $f
25 | AppId={{B271DEE6-C788-4604-B392-8B8DD16C97A8}
26 | AppName={#MyAppName}
27 | AppVersion={#MyAppVersion}
28 | ;AppVerName={#MyAppName} {#MyAppVersion}
29 | AppPublisher={#MyAppPublisher}
30 | AppPublisherURL={#MyAppURL}
31 | AppSupportURL={#MyAppURL}
32 | AppUpdatesURL={#MyAppURL}
33 | DefaultDirName={pf}\{#MyAppName}
34 | DisableProgramGroupPage=yes
35 | OutputBaseFilename="{#MyAppName}Setup-{#ReleaseType}-{#MyAppVersion}"
36 | Compression=lzma
37 | SolidCompression=yes
38 | SetupIconFile="{#SourceDir}\noun_335372_cc_v2.ico"
39 |
40 | [Languages]
41 | Name: "english"; MessagesFile: "compiler:Default.isl"
42 |
43 | [Tasks]
44 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
45 | Name: "startup"; Description: "{cm:AutoStartProgram,{#MyAppName}}"; GroupDescription: "{cm:AdditionalIcons}"
46 |
47 | [Files]
48 | Source: "..\bin\{#ReleaseType}\FloatingGlucose.exe"; DestDir: "{app}"; Flags: ignoreversion
49 | Source: "..\bin\{#ReleaseType}\FloatingGlucose.exe.config"; DestDir: "{app}"; Flags: ignoreversion
50 | Source: "..\bin\{#ReleaseType}\FloatingGlucose.pdb"; DestDir: "{app}"; Flags: ignoreversion
51 | Source: "..\bin\{#ReleaseType}\Newtonsoft.Json.dll"; DestDir: "{app}"; Flags: ignoreversion
52 | Source: "..\bin\{#ReleaseType}\Newtonsoft.Json.xml"; DestDir: "{app}"; Flags: ignoreversion
53 | Source: "..\bin\{#ReleaseType}\NAudio.dll"; DestDir: "{app}"; Flags: ignoreversion
54 | Source: "..\bin\{#ReleaseType}\NAudio.xml"; DestDir: "{app}"; Flags: ignoreversion
55 | Source: "..\bin\{#ReleaseType}\README_attributions.txt"; DestDir: "{app}"; Flags: ignoreversion
56 | Source: "..\bin\{#ReleaseType}\version.txt"; DestDir: "{app}"; Flags: ignoreversion
57 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
58 | [Dirs]
59 | Name: "{app}"; Permissions: users-full
60 |
61 | [Icons]
62 | Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
63 | Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
64 | Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: startup
65 |
66 | [Run]
67 | Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
68 |
69 |
--------------------------------------------------------------------------------
/FloatingGlucose/installer/floatingglucosenosign.iss:
--------------------------------------------------------------------------------
1 | ; Script generated by the Inno Setup Script Wizard.
2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
3 |
4 | #define MyAppName "FloatingGlucose"
5 |
6 | #define MyAppPublisher "code.bjorninge.no"
7 | #define MyAppURL "http://code.bjorninge.no"
8 | #define MyAppExeName "FloatingGlucose.exe"
9 | #define SourceDir ".."
10 | ;should be set via iscc
11 | ;cd installer
12 | ;"%programfiles(x86)%/Inno Setup 5/ISCC.exe" floatingglucose.iss
13 | #define MyAppVersion "1.5.4"
14 | #define ReleaseType "Release+NoSign"
15 |
16 | ;setting up signing:
17 | ; in inno setup, go to tools->configure sign tools
18 | ; add a new tool, call it "signtool" with the following value
19 | ; "C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe" sign /fd sha256 /tr http://time.certum.pl/ /sha1 0B360481F3F59C7F398FB0417AA4B341EB018906 $f
20 | [Setup]
21 | ; NOTE: The value of AppId uniquely identifies this application.
22 | ; Do not use the same AppId value in installers for other applications.
23 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
24 | ;SignTool=signtool /d $qFloatingGlucoseInstaller$q $f
25 | AppId={{B271DEE6-C788-4604-B392-8B8DD16C97A8}
26 | AppName={#MyAppName}
27 | AppVersion={#MyAppVersion}
28 | ;AppVerName={#MyAppName} {#MyAppVersion}
29 | AppPublisher={#MyAppPublisher}
30 | AppPublisherURL={#MyAppURL}
31 | AppSupportURL={#MyAppURL}
32 | AppUpdatesURL={#MyAppURL}
33 | DefaultDirName={pf}\{#MyAppName}
34 | DisableProgramGroupPage=yes
35 | OutputBaseFilename="{#MyAppName}Setup-{#ReleaseType}-{#MyAppVersion}"
36 | Compression=lzma
37 | SolidCompression=yes
38 | SetupIconFile="{#SourceDir}\noun_335372_cc_v2.ico"
39 |
40 | [Languages]
41 | Name: "english"; MessagesFile: "compiler:Default.isl"
42 |
43 | [Tasks]
44 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
45 | Name: "startup"; Description: "{cm:AutoStartProgram,{#MyAppName}}"; GroupDescription: "{cm:AdditionalIcons}"
46 |
47 | [Files]
48 | Source: "..\bin\{#ReleaseType}\FloatingGlucose.exe"; DestDir: "{app}"; Flags: ignoreversion
49 | Source: "..\bin\{#ReleaseType}\FloatingGlucose.exe.config"; DestDir: "{app}"; Flags: ignoreversion
50 | Source: "..\bin\{#ReleaseType}\FloatingGlucose.pdb"; DestDir: "{app}"; Flags: ignoreversion
51 | Source: "..\bin\{#ReleaseType}\Newtonsoft.Json.dll"; DestDir: "{app}"; Flags: ignoreversion
52 | Source: "..\bin\{#ReleaseType}\Newtonsoft.Json.xml"; DestDir: "{app}"; Flags: ignoreversion
53 | Source: "..\bin\{#ReleaseType}\NAudio.dll"; DestDir: "{app}"; Flags: ignoreversion
54 | Source: "..\bin\{#ReleaseType}\NAudio.xml"; DestDir: "{app}"; Flags: ignoreversion
55 | Source: "..\bin\{#ReleaseType}\README_attributions.txt"; DestDir: "{app}"; Flags: ignoreversion
56 | Source: "..\bin\{#ReleaseType}\version.txt"; DestDir: "{app}"; Flags: ignoreversion
57 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
58 | [Dirs]
59 | Name: "{app}"; Permissions: users-full
60 |
61 | [Icons]
62 | Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
63 | Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
64 | Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: startup
65 |
66 | [Run]
67 | Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
68 |
69 |
--------------------------------------------------------------------------------
/FloatingGlucose/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | https://...
7 |
8 |
9 | True
10 |
11 |
12 | 60
13 |
14 |
15 | 1
16 |
17 |
18 | True
19 |
20 |
21 | 13
22 |
23 |
24 | 11
25 |
26 |
27 | 4.5
28 |
29 |
30 | 3.8
31 |
32 |
33 | False
34 |
35 |
36 | mmol
37 |
38 |
39 | 15
40 |
41 |
42 | 30
43 |
44 |
45 | True
46 |
47 |
48 | False
49 |
50 |
51 | 85
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | FF000000
64 |
65 |
66 |
67 |
68 |
69 | Stretch
70 |
71 |
72 |
--------------------------------------------------------------------------------
/FloatingGlucose/FloatingGlucose.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net5.0-windows
4 | WinExe
5 | false
6 | publish\
7 | true
8 | Disk
9 | false
10 | Foreground
11 | 7
12 | Days
13 | false
14 | false
15 | false
16 | false
17 | 0
18 | 1.0.0.%2a
19 | false
20 | true
21 | false
22 | true
23 | true
24 |
25 |
26 | noun_335372_cc_v2.ico
27 |
28 |
29 | Always
30 |
31 |
32 |
33 |
34 |
35 |
36 | false
37 |
38 |
39 | true
40 | bin\Debug+nosign\
41 | MinimumRecommendedRules.ruleset
42 | default
43 |
44 |
45 | bin\Release+nosign\
46 | true
47 | MinimumRecommendedRules.ruleset
48 |
49 |
50 | FloatingGlucose.Program
51 |
52 |
53 | true
54 |
55 |
56 |
57 | _Inlined\ShareClientDotNet\%(RecursiveDir)%(Filename)%(Extension)
58 |
59 |
60 |
61 |
62 |
63 |
64 | Always
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/FloatingGlucose.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31221.73
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FloatingGlucose", "FloatingGlucose\FloatingGlucose.csproj", "{8D044F87-547D-4E76-8A67-3225DB984C63}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShareClientDotNet", "ShareClientDotNet\ShareClientDotNet.csproj", "{2AD02968-E7EA-4989-8BD8-FAD6A31C0072}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChartWin", "winforms-datavisualization-net5\src\System.Windows.Forms.DataVisualization\ChartWin.csproj", "{CD571259-3FDE-4B5E-B0A7-F87388583326}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0A541A11-C497-425D-9DA1-E81757B2CDEF}"
13 | ProjectSection(SolutionItems) = preProject
14 | .github\workflows\FloatingGlucose.yaml = .github\workflows\FloatingGlucose.yaml
15 | EndProjectSection
16 | EndProject
17 | Global
18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
19 | Debug|Any CPU = Debug|Any CPU
20 | Debug+nosign|Any CPU = Debug+nosign|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | Release+nosign|Any CPU = Release+nosign|Any CPU
23 | EndGlobalSection
24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
25 | {8D044F87-547D-4E76-8A67-3225DB984C63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {8D044F87-547D-4E76-8A67-3225DB984C63}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {8D044F87-547D-4E76-8A67-3225DB984C63}.Debug+nosign|Any CPU.ActiveCfg = Debug+nosign|Any CPU
28 | {8D044F87-547D-4E76-8A67-3225DB984C63}.Debug+nosign|Any CPU.Build.0 = Debug+nosign|Any CPU
29 | {8D044F87-547D-4E76-8A67-3225DB984C63}.Release|Any CPU.ActiveCfg = Release|Any CPU
30 | {8D044F87-547D-4E76-8A67-3225DB984C63}.Release|Any CPU.Build.0 = Release|Any CPU
31 | {8D044F87-547D-4E76-8A67-3225DB984C63}.Release+nosign|Any CPU.ActiveCfg = Release+nosign|Any CPU
32 | {8D044F87-547D-4E76-8A67-3225DB984C63}.Release+nosign|Any CPU.Build.0 = Release+nosign|Any CPU
33 | {2AD02968-E7EA-4989-8BD8-FAD6A31C0072}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {2AD02968-E7EA-4989-8BD8-FAD6A31C0072}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {2AD02968-E7EA-4989-8BD8-FAD6A31C0072}.Debug+nosign|Any CPU.ActiveCfg = Debug|Any CPU
36 | {2AD02968-E7EA-4989-8BD8-FAD6A31C0072}.Debug+nosign|Any CPU.Build.0 = Debug|Any CPU
37 | {2AD02968-E7EA-4989-8BD8-FAD6A31C0072}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 | {2AD02968-E7EA-4989-8BD8-FAD6A31C0072}.Release|Any CPU.Build.0 = Release|Any CPU
39 | {2AD02968-E7EA-4989-8BD8-FAD6A31C0072}.Release+nosign|Any CPU.ActiveCfg = Release|Any CPU
40 | {2AD02968-E7EA-4989-8BD8-FAD6A31C0072}.Release+nosign|Any CPU.Build.0 = Release|Any CPU
41 | {CD571259-3FDE-4B5E-B0A7-F87388583326}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 | {CD571259-3FDE-4B5E-B0A7-F87388583326}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 | {CD571259-3FDE-4B5E-B0A7-F87388583326}.Debug+nosign|Any CPU.ActiveCfg = Debug|Any CPU
44 | {CD571259-3FDE-4B5E-B0A7-F87388583326}.Debug+nosign|Any CPU.Build.0 = Debug|Any CPU
45 | {CD571259-3FDE-4B5E-B0A7-F87388583326}.Release|Any CPU.ActiveCfg = Release|Any CPU
46 | {CD571259-3FDE-4B5E-B0A7-F87388583326}.Release|Any CPU.Build.0 = Release|Any CPU
47 | {CD571259-3FDE-4B5E-B0A7-F87388583326}.Release+nosign|Any CPU.ActiveCfg = Release|Any CPU
48 | {CD571259-3FDE-4B5E-B0A7-F87388583326}.Release+nosign|Any CPU.Build.0 = Release|Any CPU
49 | EndGlobalSection
50 | GlobalSection(SolutionProperties) = preSolution
51 | HideSolutionNode = FALSE
52 | EndGlobalSection
53 | GlobalSection(ExtensibilityGlobals) = postSolution
54 | SolutionGuid = {343C9185-4732-4FB7-B2B6-DA4C0640083F}
55 | EndGlobalSection
56 | EndGlobal
57 |
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Utils/GlucoseMath.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Text;
3 | using System;
4 |
5 | using static FloatingGlucose.Properties.Settings;
6 |
7 | namespace FloatingGlucose.Classes.Utils
8 | {
9 | internal class GlucoseMath
10 | {
11 | public static decimal ToMmol(decimal number)
12 | {
13 | return decimal.Round(number / 18.01559M, 1);
14 | }
15 |
16 | public static decimal ToMgdl(decimal number)
17 | {
18 | return decimal.Round(number * 18.01559M, 0);//no decimals for mgdl values
19 | }
20 |
21 | private static double calculateSlope(BgReading current, BgReading last)
22 | {
23 | if (current.Timestamp == last.Timestamp)
24 | {
25 | return 0.0;
26 | }
27 |
28 | return (last._glucose - current._glucose) / (last.Timestamp - current.Timestamp);
29 | }
30 |
31 | private static double calculateSlopeByMinute(BgReading current, BgReading last)
32 | {
33 | return calculateSlope(current, last) * 60000;
34 | }
35 |
36 | public static string GetGlucoseDirection(BgReading current, BgReading last)
37 | {
38 | //converted from:
39 | //https://github.com/NightscoutFoundation/xDrip/blob/b34e63223573a367105b31536e413d4b83b78dab/app/src/main/java/com/eveningoutpost/dexdrip/Models/BgReading.java#L563
40 |
41 | double sloapPerMinute = calculateSlopeByMinute(current, last);
42 |
43 | String arrow = "Unknown";
44 | if (sloapPerMinute <= (-3.5))
45 | {
46 | arrow = "DoubleDown";
47 | }
48 | else if (sloapPerMinute <= (-2))
49 | {
50 | arrow = "SingleDown";
51 | }
52 | else if (sloapPerMinute <= (-1))
53 | {
54 | arrow = "FortyFiveDown";
55 | }
56 | else if (sloapPerMinute <= (1))
57 | {
58 | arrow = "Flat";
59 | }
60 | else if (sloapPerMinute <= (2))
61 | {
62 | arrow = "FortyFiveUp";
63 | }
64 | else if (sloapPerMinute <= (3.5))
65 | {
66 | arrow = "SingleUp";
67 | }
68 | else if (sloapPerMinute <= (40))
69 | {
70 | arrow = "DoubleUp";
71 | }
72 | return arrow;
73 | }
74 |
75 | public static GlucoseAlarmStatusEnum GetGlucoseAlarmStatus(decimal glucose)
76 | {
77 | if (!Default.EnableAlarms)
78 | {
79 | return GlucoseAlarmStatusEnum.Normal;
80 | }
81 | decimal urgentHigh = Default.AlarmUrgentHigh;
82 | decimal high = Default.AlarmHigh;
83 | decimal low = Default.AlarmLow;
84 | decimal urgentLow = Default.AlarmUrgentLow;
85 |
86 | if (glucose <= urgentLow)
87 | {
88 | return GlucoseAlarmStatusEnum.UrgentLow;
89 | }
90 | else if (glucose <= low)
91 | {
92 | return GlucoseAlarmStatusEnum.Low;
93 | }
94 | else if (glucose <= high)
95 | {
96 | return GlucoseAlarmStatusEnum.Normal;
97 | }
98 | else if (glucose <= urgentHigh)
99 | {
100 | return GlucoseAlarmStatusEnum.High;
101 | }
102 | else if (glucose >= urgentHigh)
103 | {
104 | return GlucoseAlarmStatusEnum.UrgentHigh;
105 | }
106 |
107 | return GlucoseAlarmStatusEnum.Unknown;
108 | }
109 | }
110 | }
--------------------------------------------------------------------------------
/FloatingGlucose/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 0, 0, 0, 0
14 |
15 |
16 |
17 |
18 | https://...
19 |
20 |
21 | True
22 |
23 |
24 | 60
25 |
26 |
27 | 1
28 |
29 |
30 | True
31 |
32 |
33 | 13
34 |
35 |
36 | 11
37 |
38 |
39 | 4.5
40 |
41 |
42 | 3.8
43 |
44 |
45 | False
46 |
47 |
48 | mmol
49 |
50 |
51 | 15
52 |
53 |
54 | 30
55 |
56 |
57 | True
58 |
59 |
60 | False
61 |
62 |
63 | 85
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | FF000000
73 |
74 |
75 |
76 |
77 |
78 | Stretch
79 |
80 |
81 |
82 |
83 |
84 |
85 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/FloatingGlucose/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace FloatingGlucose.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FloatingGlucose.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Byte[].
65 | ///
66 | internal static byte[] alarm {
67 | get {
68 | object obj = ResourceManager.GetObject("alarm", resourceCulture);
69 | return ((byte[])(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized resource of type System.Byte[].
75 | ///
76 | internal static byte[] alarm2 {
77 | get {
78 | object obj = ResourceManager.GetObject("alarm2", resourceCulture);
79 | return ((byte[])(obj));
80 | }
81 | }
82 |
83 | ///
84 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
85 | ///
86 | internal static System.Drawing.Icon browse_file {
87 | get {
88 | object obj = ResourceManager.GetObject("browse_file", resourceCulture);
89 | return ((System.Drawing.Icon)(obj));
90 | }
91 | }
92 |
93 | ///
94 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
95 | ///
96 | internal static System.Drawing.Icon noun_335372_cc_v2 {
97 | get {
98 | object obj = ResourceManager.GetObject("noun_335372_cc_v2", resourceCulture);
99 | return ((System.Drawing.Icon)(obj));
100 | }
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/Audio/SoundAlarm.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Text;
3 | using NAudio.Wave;
4 | using System;
5 | using System.Diagnostics;
6 | using System.IO;
7 | using static FloatingGlucose.Properties.Settings;
8 |
9 | namespace FloatingGlucose.Classes
10 | {
11 | public class SoundAlarm : IDisposable
12 | {
13 | private static readonly SoundAlarm instance = new SoundAlarm();
14 |
15 | private bool disposed;
16 | private IWavePlayer device = new WaveOut();
17 |
18 | private readonly Mp3FileReader glucoseAlarm = new Mp3FileReader(new MemoryStream(Properties.Resources.alarm));
19 | private readonly Mp3FileReader staleAlarm = new Mp3FileReader(new MemoryStream(Properties.Resources.alarm2));
20 |
21 | private bool isCurrentlyPlaying = false;
22 |
23 | private DateTime? postponed;
24 |
25 | public DateTime? GetPostponedUntil() => this.postponed;
26 |
27 | // Explicit static constructor to tell C# compiler
28 | // not to mark type as beforefieldinit
29 | static SoundAlarm()
30 | {
31 | }
32 |
33 | private SoundAlarm()
34 | {
35 | }
36 |
37 | public static SoundAlarm Instance
38 | {
39 | get
40 | {
41 | return instance;
42 | }
43 | }
44 |
45 | public void RemovePostpone()
46 | {
47 | this.postponed = null;
48 | }
49 |
50 | public void PostponeAlarm(int minutes)
51 | {
52 | var now = DateTime.Now;
53 | var postponeUntil = now.AddMinutes(minutes);
54 |
55 | this.postponed = postponeUntil;
56 | Debug.WriteLine($"Postponed any audible alarms until {postponeUntil.ToLocalTime()}");
57 | if (this.isCurrentlyPlaying)
58 | {
59 | this.StopAlarm();
60 | }
61 | }
62 |
63 | public bool IsPostponed()
64 | {
65 | if (this.postponed == null)
66 | {
67 | return false;
68 | }
69 |
70 | var now = DateTime.Now;
71 | return this.postponed > now;
72 | }
73 |
74 | public void PlayAlarm(Mp3FileReader fileReader)
75 | {
76 | if (this.isCurrentlyPlaying || !Default.EnableAlarms || !Default.EnableSoundAlarms)
77 | {
78 | // We don't want to play if there is already other players active
79 | // even if the other players are playing other alarms..
80 | return;
81 | }
82 | //alarms should not be sound if actively snoozed or workstation is locked
83 | if (this.IsPostponed() || (AppShared.IsWorkStationLocked && Default.DisableSoundAlarmsOnWorkstationLock))
84 | {
85 | return;
86 | }
87 |
88 | var loop = new LoopStream(fileReader);
89 | device.Init(loop);
90 | device.Play();
91 | this.isCurrentlyPlaying = true;
92 | }
93 |
94 | public void PlayStaleAlarm()
95 | {
96 | this.PlayAlarm(staleAlarm);
97 | }
98 |
99 | public void StopAlarmIfPostponed()
100 | {
101 | if (this.IsPostponed())
102 | {
103 | this.StopAlarm();
104 | }
105 | }
106 |
107 | public void StopAlarm()
108 | {
109 | this.device.Stop();
110 | this.isCurrentlyPlaying = false;
111 | staleAlarm.CurrentTime =
112 | glucoseAlarm.CurrentTime = new TimeSpan(0);
113 | }
114 |
115 | public void PlayGlucoseAlarm()
116 | {
117 | this.PlayAlarm(glucoseAlarm);
118 | }
119 |
120 | ///
121 | /// The dispose method that implements IDisposable.
122 | ///
123 | public void Dispose()
124 | {
125 | this.Dispose(true);
126 | GC.SuppressFinalize(this);
127 | }
128 |
129 | ///
130 | /// The virtual dispose method that allows
131 | /// classes inherited from this one to dispose their resources.
132 | ///
133 | ///
134 | protected virtual void Dispose(bool disposing)
135 | {
136 | if (!disposed)
137 | {
138 | if (disposing)
139 | {
140 | // Dispose managed resources here.
141 | }
142 |
143 | this.device.Dispose();
144 | this.device = null;
145 | }
146 |
147 | disposed = true;
148 | }
149 |
150 | ~SoundAlarm()
151 | {
152 | this.Dispose(false);
153 | }
154 | }
155 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | # User-specific files
4 | *.suo
5 | *.user
6 | *.userosscache
7 | *.sln.docstates
8 |
9 | # User-specific files (MonoDevelop/Xamarin Studio)
10 | *.userprefs
11 |
12 | # Build results
13 | [Dd]ebug/
14 | [Dd]ebugPublic/
15 | [Rr]elease/
16 | [Rr]eleases/
17 | [Xx]64/
18 | [Xx]86/
19 | [Bb]uild/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 |
24 | # Visual Studio 2015 cache/options directory
25 | .vs/
26 | # Uncomment if you have tasks that create the project's static files in wwwroot
27 | #wwwroot/
28 |
29 | # MSTest test Results
30 | [Tt]est[Rr]esult*/
31 | [Bb]uild[Ll]og.*
32 |
33 | # NUNIT
34 | *.VisualState.xml
35 | TestResult.xml
36 |
37 | # Build Results of an ATL Project
38 | [Dd]ebugPS/
39 | [Rr]eleasePS/
40 | dlldata.c
41 |
42 | # DNX
43 | project.lock.json
44 | artifacts/
45 |
46 | *_i.c
47 | *_p.c
48 | *_i.h
49 | *.ilk
50 | *.meta
51 | *.obj
52 | *.pch
53 | *.pdb
54 | *.pgc
55 | *.pgd
56 | *.rsp
57 | *.sbr
58 | *.tlb
59 | *.tli
60 | *.tlh
61 | *.tmp
62 | *.tmp_proj
63 | *.log
64 | *.vspscc
65 | *.vssscc
66 | .builds
67 | *.pidb
68 | *.svclog
69 | *.scc
70 |
71 | # Chutzpah Test files
72 | _Chutzpah*
73 |
74 | # Visual C++ cache files
75 | ipch/
76 | *.aps
77 | *.ncb
78 | *.opendb
79 | *.opensdf
80 | *.sdf
81 | *.cachefile
82 | *.VC.db
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # TFS 2012 Local Workspace
91 | $tf/
92 |
93 | # Guidance Automation Toolkit
94 | *.gpState
95 |
96 | # ReSharper is a .NET coding add-in
97 | _ReSharper*/
98 | *.[Rr]e[Ss]harper
99 | *.DotSettings.user
100 |
101 | # JustCode is a .NET coding add-in
102 | .JustCode
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | _NCrunch_*
112 | .*crunch*.local.xml
113 | nCrunchTemp_*
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 |
142 | # TODO: Un-comment the next line if you do not want to checkin
143 | # your web deploy settings because they may include unencrypted
144 | # passwords
145 | #*.pubxml
146 | *.publishproj
147 |
148 | # NuGet Packages
149 | *.nupkg
150 | # The packages folder can be ignored because of Package Restore
151 | **/packages/*
152 | # except build/, which is used as an MSBuild target.
153 | !**/packages/build/
154 | # Uncomment if necessary however generally it will be regenerated when needed
155 | #!**/packages/repositories.config
156 | # NuGet v3's project.json files produces more ignoreable files
157 | *.nuget.props
158 | *.nuget.targets
159 |
160 | # Microsoft Azure Build Output
161 | csx/
162 | *.build.csdef
163 |
164 | # Microsoft Azure Emulator
165 | ecf/
166 | rcf/
167 |
168 | # Microsoft Azure ApplicationInsights config file
169 | ApplicationInsights.config
170 |
171 | # Windows Store app package directory
172 | AppPackages/
173 | BundleArtifacts/
174 |
175 | # Visual Studio cache files
176 | # files ending in .cache can be ignored
177 | *.[Cc]ache
178 | # but keep track of directories ending in .cache
179 | !*.[Cc]ache/
180 |
181 | # Others
182 | ClientBin/
183 | [Ss]tyle[Cc]op.*
184 | ~$*
185 | *~
186 | *.dbmdl
187 | *.dbproj.schemaview
188 | *.pfx
189 | *.publishsettings
190 | node_modules/
191 | orleans.codegen.cs
192 |
193 | # RIA/Silverlight projects
194 | Generated_Code/
195 |
196 | # Backup & report files from converting an old project file
197 | # to a newer Visual Studio version. Backup files are not needed,
198 | # because we have git ;-)
199 | _UpgradeReport_Files/
200 | Backup*/
201 | UpgradeLog*.XML
202 | UpgradeLog*.htm
203 |
204 | # SQL Server files
205 | *.mdf
206 | *.ldf
207 |
208 | # Business Intelligence projects
209 | *.rdl.data
210 | *.bim.layout
211 | *.bim_*.settings
212 |
213 | # Microsoft Fakes
214 | FakesAssemblies/
215 |
216 | # GhostDoc plugin setting file
217 | *.GhostDoc.xml
218 |
219 | # Node.js Tools for Visual Studio
220 | .ntvs_analysis.dat
221 |
222 | # Visual Studio 6 build log
223 | *.plg
224 |
225 | # Visual Studio 6 workspace options file
226 | *.opt
227 |
228 | # Visual Studio LightSwitch build output
229 | **/*.HTMLClient/GeneratedArtifacts
230 | **/*.DesktopClient/GeneratedArtifacts
231 | **/*.DesktopClient/ModelManifest.xml
232 | **/*.Server/GeneratedArtifacts
233 | **/*.Server/ModelManifest.xml
234 | _Pvt_Extensions
235 |
236 | # LightSwitch generated files
237 | GeneratedArtifacts/
238 | ModelManifest.xml
239 |
240 | # Paket dependency manager
241 | .paket/paket.exe
242 |
243 | # FAKE - F# Make
244 | .fake/
--------------------------------------------------------------------------------
/FloatingGlucose/FormWebbrowser.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/FloatingGlucose/FormGlucoseSettings.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
--------------------------------------------------------------------------------
/FloatingGlucose/.editorconfig:
--------------------------------------------------------------------------------
1 | # To learn more about .editorconfig see https://aka.ms/editorconfigdocs
2 | ###############################
3 | # Core EditorConfig Options #
4 | ###############################
5 | root = true
6 | # All files
7 | [*]
8 | indent_style = space
9 | # Code files
10 | [*.{cs,csx,vb,vbx}]
11 | indent_size = 4
12 | insert_final_newline = true
13 | charset = utf-8-bom
14 | ###############################
15 | # .NET Coding Conventions #
16 | ###############################
17 | [*.{cs,vb}]
18 | # Organize usings
19 | dotnet_sort_system_directives_first = true
20 | # this. preferences
21 | dotnet_style_qualification_for_field = false:silent
22 | dotnet_style_qualification_for_property = false:silent
23 | dotnet_style_qualification_for_method = false:silent
24 | dotnet_style_qualification_for_event = false:silent
25 | # Language keywords vs BCL types preferences
26 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent
27 | dotnet_style_predefined_type_for_member_access = true:silent
28 | # Parentheses preferences
29 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
30 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
31 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
32 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
33 | # Modifier preferences
34 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
35 | dotnet_style_readonly_field = true:suggestion
36 | # Expression-level preferences
37 | dotnet_style_object_initializer = true:suggestion
38 | dotnet_style_collection_initializer = true:suggestion
39 | dotnet_style_explicit_tuple_names = true:suggestion
40 | dotnet_style_null_propagation = true:suggestion
41 | dotnet_style_coalesce_expression = true:suggestion
42 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
43 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
44 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
45 | dotnet_style_prefer_auto_properties = true:silent
46 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
47 | dotnet_style_prefer_conditional_expression_over_return = true:silent
48 |
49 | dotnet_diagnostic.CA1416.severity = none
50 | ###############################
51 | # Naming Conventions #
52 | ###############################
53 | # Style Definitions
54 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case
55 | # Use PascalCase for constant fields
56 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
57 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
58 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
59 | dotnet_naming_symbols.constant_fields.applicable_kinds = field
60 | dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
61 | dotnet_naming_symbols.constant_fields.required_modifiers = const
62 | ###############################
63 | # C# Coding Conventions #
64 | ###############################
65 | [*.cs]
66 | # var preferences
67 | csharp_style_var_for_built_in_types = true:silent
68 | csharp_style_var_when_type_is_apparent = true:silent
69 | csharp_style_var_elsewhere = true:silent
70 | # Expression-bodied members
71 | csharp_style_expression_bodied_methods = false:silent
72 | csharp_style_expression_bodied_constructors = false:silent
73 | csharp_style_expression_bodied_operators = false:silent
74 | csharp_style_expression_bodied_properties = true:silent
75 | csharp_style_expression_bodied_indexers = true:silent
76 | csharp_style_expression_bodied_accessors = true:silent
77 | # Pattern matching preferences
78 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
79 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
80 | # Null-checking preferences
81 | csharp_style_throw_expression = true:suggestion
82 | csharp_style_conditional_delegate_call = true:suggestion
83 | # Modifier preferences
84 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
85 | # Expression-level preferences
86 | csharp_prefer_braces = true:silent
87 | csharp_style_deconstructed_variable_declaration = true:suggestion
88 | csharp_prefer_simple_default_expression = true:suggestion
89 | csharp_style_pattern_local_over_anonymous_function = true:suggestion
90 | csharp_style_inlined_variable_declaration = true:suggestion
91 | ###############################
92 | # C# Formatting Rules #
93 | ###############################
94 | # New line preferences
95 | csharp_new_line_before_open_brace = all
96 | csharp_new_line_before_else = true
97 | csharp_new_line_before_catch = true
98 | csharp_new_line_before_finally = true
99 | csharp_new_line_before_members_in_object_initializers = true
100 | csharp_new_line_before_members_in_anonymous_types = true
101 | csharp_new_line_between_query_expression_clauses = true
102 | # Indentation preferences
103 | csharp_indent_case_contents = true
104 | csharp_indent_switch_labels = true
105 | csharp_indent_labels = flush_left
106 | # Space preferences
107 | csharp_space_after_cast = false
108 | csharp_space_after_keywords_in_control_flow_statements = true
109 | csharp_space_between_method_call_parameter_list_parentheses = false
110 | csharp_space_between_method_declaration_parameter_list_parentheses = false
111 | csharp_space_between_parentheses = false
112 | csharp_space_before_colon_in_inheritance_clause = true
113 | csharp_space_after_colon_in_inheritance_clause = true
114 | csharp_space_around_binary_operators = before_and_after
115 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
116 | csharp_space_between_method_call_name_and_opening_parenthesis = false
117 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
118 | # Wrapping preferences
119 | csharp_preserve_single_line_statements = true
120 | csharp_preserve_single_line_blocks = true
121 | ###############################
122 | # VB Coding Conventions #
123 | ###############################
124 | [*.vb]
125 | # Modifier preferences
126 | visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
127 |
--------------------------------------------------------------------------------
/FloatingGlucose/FloatingGlucose.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
124 | 150, 17
125 |
126 |
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Plugins/DexcomShare/DexcomShareUS.cs:
--------------------------------------------------------------------------------
1 | using FloatingGlucose.Classes.Extensions;
2 | using ShareClientDotNet;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Collections.Specialized;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace FloatingGlucose.Classes.DataSources.Plugins
11 | {
12 | internal class DexcomShareEndpoint : IDataSourcePlugin
13 | {
14 | public string Acknowledgment => "Dexcom share plugin by Bjørn inge Vikhammermo";
15 |
16 | public string AcknowledgmentUrl => "";
17 | public string Author => "Bjørn inge Vikhammermo";
18 |
19 | public bool PluginDisabled => false;
20 | public bool RequiresUserNameAndPassword => true;
21 | public bool RequiresDataSource => false;
22 |
23 | public List HandleFormatting() => null;
24 |
25 | public bool RequiresBrowseButton => false;
26 | public bool PluginHandlesFormatting => false;
27 | public string BrowseDialogFileFilter => "";
28 | public virtual string DataSourceShortName => "Dexcom Share (US)";
29 | public virtual int SortOrder => 25;
30 |
31 | //private List csv = new List();
32 |
33 | public DateTime Date
34 | {
35 | get
36 | {
37 | var reading = this.shareGlucose.First();
38 | return reading.LocalTime;
39 | }
40 | }
41 |
42 | public double Delta => this.Glucose - this.PreviousGlucose;
43 |
44 | //
45 | // Raw glucose is not supported for this plugin
46 | //
47 | public double RawDelta => 0.0;
48 |
49 | public double RoundedRawDelta() => 0.0;
50 |
51 | public double RawGlucose => 0.0;
52 | public double PreviousRawGlucose => 0.0;
53 |
54 | public DateTime LocalDate => this.Date;
55 |
56 | public double RoundedDelta() => Math.Round(this.Delta, 1);
57 |
58 | public double Glucose
59 | {
60 | get
61 | {
62 | var reading = this.shareGlucose?.First();
63 | if (reading == null)
64 | {
65 | return 0;
66 | }
67 | return Convert.ToDouble(this.UserWantsMmolUnits() ? reading.ValueMmol : reading.ValueMgdl);
68 | }
69 | }
70 |
71 | protected ShareClient shareClient = new DebuggableShareClient();
72 |
73 | protected List shareGlucose = new List();
74 |
75 | public double PreviousGlucose
76 | {
77 | get
78 | {
79 | var reading = this.shareGlucose?.Skip(1)?.First();
80 | if (reading == null)
81 | {
82 | return 0;
83 | }
84 | return Convert.ToDouble(this.UserWantsMmolUnits() ? reading.ValueMmol : reading.ValueMgdl);
85 | }
86 | }
87 |
88 | public string Direction
89 | {
90 | get
91 | {
92 | var first = this.shareGlucose?.First();
93 |
94 | //
95 | // Converts between share glucose Direction ordinals to nightscout glucose directions
96 | // which is the expected format
97 | //
98 | switch (first.Trend)
99 | {
100 | case ShareGlucoseSlopeOrdinals.DOUBLE_UP:
101 | return "DoubleUp";
102 |
103 | case ShareGlucoseSlopeOrdinals.SINGLE_UP:
104 | return "SingleUp";
105 |
106 | case ShareGlucoseSlopeOrdinals.UP_45:
107 | return "FortyFiveUp";
108 |
109 | case ShareGlucoseSlopeOrdinals.FLAT:
110 | return "Flat";
111 |
112 | case ShareGlucoseSlopeOrdinals.DOWN_45:
113 | return "FortyFiveDown";
114 |
115 | case ShareGlucoseSlopeOrdinals.SINGLE_DOWN:
116 | return "SingleDown";
117 |
118 | case ShareGlucoseSlopeOrdinals.DOUBLE_DOWN:
119 | return "DoubleDown";
120 |
121 | case ShareGlucoseSlopeOrdinals.NOT_COMPUTABLE:
122 | return "NOT COMPUTABLE";
123 |
124 | case ShareGlucoseSlopeOrdinals.OUT_OF_RANGE:
125 | return "OUT OF RANGE";
126 |
127 | case ShareGlucoseSlopeOrdinals.NONE:
128 | return "None";
129 | }
130 |
131 | return "";
132 | }
133 | }
134 |
135 | public virtual void OnPluginSelected(FormGlucoseSettings form)
136 | {
137 | form.lblDataSourceLocation.Text = "Dexcom share server";
138 | form.txtDataSourceLocation.Text = this.shareClient.CurrentDexcomServer;
139 |
140 | form.lblUsername.Text = "User Name";
141 | form.lblPassword.Enabled = true;
142 | form.txtPassword.Enabled = true;
143 | }
144 |
145 | public virtual bool VerifyConfig(Properties.Settings settings)
146 | {
147 | var username = settings.UserName;
148 | var password = settings.HashedPassword?.Text ?? "";
149 |
150 | if (username.Length < 2)
151 | {
152 | throw new ConfigValidationException("UserName field was not correctly filled!");
153 | }
154 |
155 | if (password.Length < 1)
156 | {
157 | throw new ConfigValidationException("Password field was not correctly filled!");
158 | }
159 |
160 | shareClient.Username = username;
161 | shareClient.Password = password;
162 |
163 | return true;
164 | }
165 |
166 | public virtual async Task GetDataSourceDataAsync(NameValueCollection locations)
167 | {
168 | try
169 | {
170 | //this can return null if the internet connection is broken
171 | Console.WriteLine($"Will attempt {this.shareClient.CurrentDexcomServer}, user: {shareClient.Username}");
172 | this.shareGlucose = await shareClient.FetchLast(3);
173 | }
174 | catch (SpecificShareError err)
175 | {
176 | if (err.code == ShareKnownRemoteErrorCodes.AuthenticateAccountNotFound || err.code == ShareKnownRemoteErrorCodes.AuthenticatePasswordInvalid)
177 | {
178 | this.shareGlucose = null;
179 | throw new ConfigValidationException($"Dexcom share client uknown username or password. Entered username: {shareClient.Username}");
180 | }
181 | }
182 |
183 | return this;
184 | }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Plugins/GlimpFileEndpoint.cs:
--------------------------------------------------------------------------------
1 | using FloatingGlucose.Classes.Extensions;
2 | using FloatingGlucose.Classes.Utils;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Collections.Specialized;
6 | using System.Diagnostics;
7 | using System.Globalization;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Net.Http;
11 | using System.Text;
12 | using System.Threading.Tasks;
13 |
14 | namespace FloatingGlucose.Classes.DataSources.Plugins
15 | {
16 | internal class RawGlimpData : BgReading
17 | {
18 | public string FileVersion;
19 | public string RawGlucose;
20 | public string SensorId;
21 | }
22 |
23 | internal class GlimpFileEndpoint : IDataSourcePlugin
24 | {
25 | public string Acknowledgment => "Glimp plugin by Bjørn inge Vikhammermo";
26 |
27 | public string AcknowledgmentUrl => "";
28 | public string Author => "Bjørn inge Vikhammermo";
29 |
30 | public bool PluginDisabled => false;
31 | public bool RequiresUserNameAndPassword => false;
32 | public bool RequiresDataSource => true;
33 |
34 | public List HandleFormatting() => null;
35 |
36 | public bool RequiresBrowseButton => true;
37 | public bool PluginHandlesFormatting => false;
38 | public string BrowseDialogFileFilter => "Glimp Glucose file |GlicemiaMisurazioni.csv";
39 | public string DataSourceShortName => "Glimp Dropbox File";
40 | public virtual int SortOrder => 20;
41 |
42 | private List csv = new List();
43 |
44 | public DateTime Date => this.csv.First().DateReading;
45 |
46 | public double Delta => this.Glucose - this.PreviousGlucose;
47 |
48 | //
49 | // Raw glucose is not supported for this plugin
50 | //
51 | public double RawDelta => 0.0;
52 |
53 | public double RoundedRawDelta() => 0.0;
54 |
55 | public double RawGlucose => 0.0;
56 | public double PreviousRawGlucose => 0.0;
57 |
58 | public DateTime LocalDate => this.Date;
59 |
60 | public double RoundedDelta() => Math.Round(this.Delta, 1);
61 |
62 | public double Glucose => this.UserWantsMmolUnits() ? this.csv.First().GlucoseMmol : this.csv.First().GlucoseMgdl;
63 |
64 | private DateTime GlimpDateStringToDateTime(string reading) => DateTime.ParseExact(reading, "dd/MM/yyyy HH.mm.ss", CultureInfo.InvariantCulture);
65 |
66 | public double PreviousGlucose
67 | {
68 | get
69 | {
70 | RawGlimpData reading;
71 |
72 | try
73 | {
74 | reading = this.csv.Skip(1).First();
75 | }
76 | catch (InvalidOperationException)
77 | {
78 | //this is factually incorrect, but will provide a delta of 0 in the GUI,
79 | //which is what we want
80 | //reading the first entry might also fail, but we presume the file has content when it's created
81 | reading = this.csv.First();
82 | }
83 |
84 | return this.UserWantsMmolUnits() ? reading.GlucoseMmol : reading.GlucoseMgdl;
85 | }
86 | }
87 |
88 | public string Direction
89 | {
90 | get
91 | {
92 | //Sligthly more advanced implementation of direction
93 | //Calculates a slope per minute
94 | //(how much glucose has changed every minute between two readings)
95 |
96 | var first = this.csv.First();
97 | RawGlimpData last;
98 | try
99 | {
100 | last = this.csv.Skip(1).First();
101 | }
102 | catch (InvalidOperationException)
103 | {
104 | last = first;
105 | }
106 |
107 | var dir = first.GetRelativeGlucoseDirection(last);
108 | Debug.WriteLine($"glimpfile got glucose direction:{dir}");
109 | return dir;
110 | }
111 | }
112 |
113 | public void OnPluginSelected(FormGlucoseSettings form)
114 | {
115 | form.lblDataSourceLocation.Text = "Your File Dump location";
116 | var source = form.txtDataSourceLocation.Text.ToLower();
117 | //for this plugin, we don't handle http:// and https://
118 | //if the source does not resemble an url, it should clearly be removed.
119 | if (source.StartsWith("http://") || source.StartsWith("https://"))
120 | {
121 | form.txtDataSourceLocation.Text = "";
122 | }
123 | }
124 |
125 | public bool VerifyConfig(Properties.Settings settings)
126 | {
127 | if (!Validators.IsReadableFile(settings.DataPathLocation))
128 | {
129 | throw new ConfigValidationException("You have entered an invalid file path for the data dump!");
130 | }
131 |
132 | return true;
133 | }
134 |
135 | public async Task GetDataSourceDataAsync(NameValueCollection locations)
136 | {
137 | var datapath = locations["raw"];
138 | var client = new HttpClient();
139 |
140 | this.csv.Clear();
141 | // datapath is expected to be a valid file
142 | // Exceptions will be handled by the main program
143 | using (var reader = new StreamReader(datapath, System.Text.Encoding.Unicode))
144 | {
145 | int i = 0;
146 |
147 | while (true)
148 | {
149 | //something wrong here, it is read wrongly..
150 | string line = await reader.ReadLineAsync();
151 | if (line == null || i++ > 100)
152 | {
153 | break;
154 | }
155 | if (line.Trim().Length == 0)
156 | {
157 | continue;
158 | }
159 | string[] items = line.Split(';');
160 | var data = new RawGlimpData();
161 |
162 | //Measurement type (0=manual measurement, 1=Freestyle Libre)
163 | //if (items[6] == "1")
164 | //{
165 | data.FileVersion = items[0];
166 | data.DateReading = GlimpDateStringToDateTime(items[1]);
167 | data.RawGlucose = items[4];
168 | data._glucose = Double.Parse(items[5], NumberStyles.Any, CultureInfo.InvariantCulture);
169 | data.SensorId = items[7];
170 | //}
171 |
172 | this.csv.Add(data);
173 | }
174 | }
175 |
176 | return this;
177 | }
178 | }
179 | }
--------------------------------------------------------------------------------
/FloatingGlucose/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\alarm.mp3;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
123 |
124 |
125 | ..\Resources\alarm2.mp3;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
126 |
127 |
128 | ..\Resources\browse-file.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
129 |
130 |
131 | ..\Resources\noun_335372_cc_v2.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
132 |
133 |
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Plugins/NightscoutPebbleEndpoint/NightscoutPebbleEndpoint.cs:
--------------------------------------------------------------------------------
1 | using FloatingGlucose.Classes.Extensions;
2 | using FloatingGlucose.Classes.Utils;
3 | using Newtonsoft.Json;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Collections.Specialized;
7 | using System.Globalization;
8 | using System.Linq;
9 | using System.Net.Http;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace FloatingGlucose.Classes.DataSources.Plugins
14 | {
15 | internal class NightscoutPebbleEndpoint : IDataSourcePlugin
16 | {
17 | public string Acknowledgment => "Nightscout plugin by Bjørn inge Vikhammermo";
18 |
19 | public string AcknowledgmentUrl => "";
20 | public string Author => "Bjørn inge Vikhammermo";
21 |
22 | public bool PluginHandlesFormatting => false;
23 | public virtual bool RequiresDataSource => true;
24 |
25 | public List HandleFormatting() => null;
26 |
27 | public virtual bool RequiresUserNameAndPassword => true;
28 | public virtual bool PluginDisabled => false;
29 | public virtual bool RequiresBrowseButton => false;
30 | public virtual string BrowseDialogFileFilter => "";
31 | public GeneratedNsData NsData;
32 | public DateTime Date { get; set; }
33 |
34 | public double Glucose { get; set; }
35 | public double Delta { get; set; }
36 | public string Direction { get; set; }
37 |
38 | public virtual int SortOrder => 10;
39 |
40 | public double RawDelta => this.RawGlucose - this.PreviousRawGlucose;
41 |
42 | public double RoundedDelta() => Math.Round(this.Delta, 1);
43 |
44 | public double RoundedRawDelta() => Math.Round(this.RawDelta, 1);
45 |
46 | //public static CultureInfo Culture = new CultureInfo("en-US");
47 |
48 | public double CalculateRawGlucose(Cal cal, Bg bg, double actualGlucose)
49 | {
50 | double number;
51 | double curBG = actualGlucose;
52 | int specialValue = 0;
53 |
54 | if (this.UserWantsMmolUnits())
55 | {
56 | if ((actualGlucose < 2.2) || (actualGlucose > 22.2))
57 | {
58 | specialValue = 1;
59 | }
60 |
61 | curBG = curBG * 18.01559;
62 | }
63 | else
64 | {
65 | if ((actualGlucose < 40) || (actualGlucose > 400))
66 | {
67 | specialValue = 1;
68 | }
69 | }
70 |
71 | //this special value is only triggered when the Dexcom upload is brand new
72 | //from a brand new sensor?
73 | if (specialValue == 1)
74 | {
75 | number = cal.scale * (bg.unfiltered - cal.intercept) / cal.slope;
76 | }
77 | else
78 | {
79 | number = cal.scale * (bg.filtered - cal.intercept) / cal.slope / curBG;
80 | number = cal.scale * (bg.unfiltered - cal.intercept) / cal.slope / number;
81 | }
82 |
83 | if (this.UserWantsMmolUnits())
84 | {
85 | number = number / 18.01559;
86 | }
87 |
88 | return number;
89 | }
90 |
91 | public double PreviousGlucose
92 | {
93 | get
94 | {
95 | var bgs = this.NsData.bgs.Skip(1).First();
96 | return Double.Parse(bgs.sgv, NumberStyles.Any, CultureInfo.InvariantCulture);
97 | }
98 | }
99 |
100 | public double PreviousRawGlucose
101 | {
102 | get
103 | {
104 | try
105 | {
106 | var cal = this.NsData.cals.Skip(1).First();
107 | var bg = this.NsData.bgs.Skip(1).First();
108 | return this.CalculateRawGlucose(cal, bg, this.PreviousGlucose);
109 | }
110 | catch (InvalidOperationException)
111 | {
112 | throw new InvalidJsonDataException("The raw data are not available, enable RAWBG in your azure settings");
113 | }
114 | }
115 | }
116 |
117 | public double RawGlucose
118 | {
119 | get
120 | {
121 | try
122 | {
123 | var cal = this.NsData.cals.First();
124 | var bg = this.NsData.bgs.First();
125 | return this.CalculateRawGlucose(cal, bg, this.Glucose);
126 | }
127 | catch (InvalidOperationException)
128 | {
129 | throw new InvalidJsonDataException("The raw data are not available, you may have enable RAWBG in your azure settings");
130 | }
131 | }
132 | }
133 |
134 | public DateTime LocalDate => this.Date.ToLocalTime();
135 |
136 | public virtual string DataSourceShortName => "Nightscout URL";
137 |
138 | public virtual void OnPluginSelected(FormGlucoseSettings form)
139 | {
140 | form.lblDataSourceLocation.Text = "Your Nightscout installation URL";
141 | form.lblUsername.Text = "Access Token";
142 | form.lblPassword.Enabled = false;
143 | form.txtPassword.Enabled = false;
144 |
145 | var source = form.txtDataSourceLocation.Text.ToLower();
146 | //for this plugin, we handle only http:// and https://
147 | //if the source does not resemble an url, it should clearly be removed.
148 | if (!(source.StartsWith("http://") || source.StartsWith("https://")))
149 | {
150 | form.txtDataSourceLocation.Text = "";
151 | }
152 | }
153 |
154 | public virtual bool VerifyConfig(Properties.Settings settings)
155 | {
156 | if (!Validators.IsUrl(settings.DataPathLocation) || settings.DataPathLocation == "https://mysite.azurewebsites.net")
157 | {
158 | throw new ConfigValidationException("You have entered an invalid Nightscout site URL");
159 | }
160 |
161 | return true;
162 | }
163 |
164 | public virtual async Task GetDataSourceDataAsync(NameValueCollection locations)
165 | {
166 | var datapath = locations["location"];
167 | var client = new HttpClient();
168 | Bg bgs = null;
169 |
170 | string urlContents = await client.GetStringAsync(datapath);
171 |
172 | //urlContents = "{ \"status\":[{\"now\":1471947452808}],\"bgs\":[],\"cals\":[]";
173 | //urlContents = "{}"
174 | var parsed =
175 | this.NsData = JsonConvert.DeserializeObject(urlContents);
176 | try
177 | {
178 | bgs = parsed.bgs.First();
179 | this.Direction = bgs.direction;
180 | this.Glucose = Double.Parse(bgs.sgv, NumberStyles.Any, CultureInfo.InvariantCulture);
181 | this.Date = DateTimeOffset.FromUnixTimeMilliseconds(bgs.datetime).DateTime;
182 | this.Delta = Double.Parse(bgs.bgdelta, NumberStyles.Any, CultureInfo.InvariantCulture);
183 | }
184 | catch (InvalidOperationException)
185 | {
186 | //this exception might be hit when the Nightscout installation is brand new or contains no recent data;
187 | throw new MissingDataException("No data");
188 | }
189 |
190 | return this;
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Plugins/YrWeatherService/YrWeatherServiceEndpoint.cs:
--------------------------------------------------------------------------------
1 | using FloatingGlucose.Classes.Extensions;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Collections.Specialized;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Net.Http;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 | using System.Xml.Serialization;
11 | using Xml2CSharp;
12 |
13 | namespace FloatingGlucose.Classes.DataSources.Plugins
14 | {
15 | internal class YrWeatherServiceEndpoint : IDataSourcePlugin
16 | {
17 | public string Acknowledgment => "Weather forecast from Yr, delivered by the Norwegian Meteorological Institute and the NRK";
18 |
19 | public string AcknowledgmentUrl => "";
20 | public string Author => "Bjørn inge Vikhammermo";
21 |
22 | public bool PluginHandlesFormatting => true;
23 | public bool RequiresUserNameAndPassword => false;
24 | public bool RequiresDataSource => true;
25 | public bool PluginDisabled => true;
26 | public bool RequiresBrowseButton => false;
27 | public string BrowseDialogFileFilter => "";
28 | public string DataSourceShortName => "Yr.no Weather";
29 | public virtual int SortOrder => 20;
30 |
31 | public DateTime Date
32 | {
33 | get
34 | {
35 | var from = this.weatherData?.Forecast?.Tabular?.Time[0]?.From;
36 | return from == null || from.Length == 0 ? DateTime.MinValue : DateTime.Parse(from);
37 | }
38 | }
39 |
40 | public double Delta => this.Glucose - this.PreviousGlucose;
41 |
42 | //glucose,lastupdate,delta,notification
43 | public List HandleFormatting()
44 | {
45 | var texts = new List();
46 |
47 | if (this.weatherData != null)
48 | {
49 | var temp = this.weatherData.Forecast?.Tabular?.Time?[0]?.Temperature;
50 | var val = temp?.Value ?? "";
51 | var unit = temp.Unit?[0].ToString()?.ToUpper() ?? "";
52 | var temperature = $"{val} °{unit}";
53 | texts.Add(temperature);
54 | texts.Add(this.Date.ToLocalTime().ToShortTimeString());
55 | texts.Add(this.weatherData.Location?.Name ?? "");
56 |
57 | texts.Add("Weatherdata plugin active");
58 |
59 | return texts;
60 | }
61 | texts.Add("Error");
62 | texts.Add("Cannot");
63 | texts.Add("get weatherdata");
64 | texts.Add("error");
65 |
66 | return texts;
67 | }
68 |
69 | //
70 | // Raw glucose is not supported for this plugin
71 | //
72 | public double RawDelta => 0.0;
73 |
74 | public double RoundedRawDelta() => 0.0;
75 |
76 | public double RawGlucose => 0.0;
77 | public double PreviousRawGlucose => 0.0;
78 |
79 | public DateTime LocalDate => this.Date;
80 |
81 | public double RoundedDelta() => Math.Round(this.Delta, 1);
82 |
83 | public double Glucose => 0.0;
84 |
85 | public double PreviousGlucose => 0.0;
86 |
87 | public string Direction => "Flat";
88 |
89 | private string yrSearchUrl = "https://www.yr.no/soek/soek.aspx?&land=&spr=eng®ion1=&sok=Search&sted=";
90 | private string yrForeCast = "http://www.yr.no/{0}/forecast_hour_by_hour.xml";
91 |
92 | private Weatherdata weatherData;
93 |
94 | private string getYrForeCastURL(string pathname) => String.Format(this.yrForeCast, pathname);
95 |
96 | //override specific for this plugin as it doesn't really handle deltas
97 |
98 | public FormWebbrowser createYrSearchBrowser(string city)
99 | {
100 | var js = @"
101 |
102 | (function(){
103 | if (!String.prototype.startsWith) {
104 | String.prototype.startsWith = function(searchString, position){
105 | position = position || 0;
106 | return this.substr(position, searchString.length) === searchString;
107 | };
108 | }
109 |
110 | function handler(ev) {
111 | var event = window.event || event;
112 | var target = event.srcElement || event.target;
113 |
114 | if( target.tagName != 'A') {
115 | return false;
116 | }
117 |
118 | var pathname = target.pathname;
119 | if(pathname.substring(0, 1) != '/') {
120 | pathname = '/'+ pathname;
121 | }
122 | //alert('will now try to close, pathname: ' + pathname);
123 | if(pathname.startsWith('/place/') ||
124 | pathname.startsWith('/sted/') ||
125 | pathname.startsWith('/stad/') ||
126 | pathname.startsWith('/sadji/')
127 | ) {
128 | //alert('will now close with reval:' + pathname)
129 | //console.log('set return value and close:' + pathname);
130 | window.external.SetReturnValueAndClose(pathname);
131 | }
132 |
133 | ev.preventDefault && ev.preventDefault();
134 |
135 | return false;
136 | }
137 | var aEL=window.addEventListener;
138 | alert('Please choose the most appropriate location in the following web page')
139 |
140 | document.body[ aEL ? 'addEventListener' : 'attachEvent' ]( aEL ? 'click' : 'onclick', handler )
141 | })();
142 | ";
143 | var form = new FormWebbrowser(js);
144 | form.SetBrowserUrl(this.yrSearchUrl + city);
145 | form.ShowDialog();
146 | return form;
147 | }
148 |
149 | private Weatherdata deserializeWeatherData(string xmlsource)
150 | {
151 | Weatherdata weather;
152 |
153 | using (var reader = new StringReader(xmlsource))
154 | {
155 | var serializer = new XmlSerializer(typeof(Weatherdata));
156 | weather = (Weatherdata)serializer.Deserialize(reader);
157 |
158 | return weather;
159 | }
160 | }
161 |
162 | private bool verifyYrPathString(string path)
163 | {
164 | if (path.StartsWith("/place/") || path.StartsWith("/sted/") ||
165 | path.StartsWith("/stad/") ||
166 | path.StartsWith("/sadji/"))
167 | {
168 | return path.Count(x => x == '/') > 4;
169 | }
170 |
171 | return false;
172 | }
173 |
174 | public void OnPluginSelected(FormGlucoseSettings form)
175 | {
176 | form.lblDataSourceLocation.Text = "Your closest location";
177 |
178 | var source = form.txtDataSourceLocation.Text.ToLower();
179 | //for this plugin, we handle only city names and yr.no path names
180 | //if the source resembles an url, it should clearly be removed.
181 | if (source.StartsWith("http://") || source.StartsWith("https://"))
182 | {
183 | form.txtDataSourceLocation.Text = "";
184 | }
185 | }
186 |
187 | public bool VerifyConfig(Properties.Settings settings)
188 | {
189 | var pathname = settings.DataPathLocation;
190 | settings.DataPathLocation = pathname;
191 | if (this.verifyYrPathString(pathname))
192 | {
193 | return true;
194 | }
195 | //
196 | // pathname entered by user is most probably in the form of "Stockholm"
197 | // We need the a more specific path "/place/Sweden/Stockholm/Stockholm"
198 | //
199 |
200 | var client = this.createYrSearchBrowser(pathname);
201 |
202 | settings.DataPathLocation = client.WebPageReturnValue;
203 |
204 | var url = this.getYrForeCastURL(settings.DataPathLocation);
205 |
206 | if (!this.verifyYrPathString(settings.DataPathLocation))
207 | {
208 | throw new ConfigValidationException("The entered weather location was not correctly formed!");
209 | }
210 |
211 | return true;
212 | }
213 |
214 | public async Task GetDataSourceDataAsync(NameValueCollection locations)
215 | {
216 | this.WriteDebug("Will now try refresh using YR.no");
217 | var pathname = locations["raw"];
218 | var forecastUrl = this.getYrForeCastURL(pathname);
219 |
220 | try
221 | {
222 | var client = new HttpClient();
223 | string urlContents = await client.GetStringAsync(forecastUrl);
224 |
225 | this.weatherData = this.deserializeWeatherData(urlContents);
226 | }
227 | catch (Exception err)
228 | {
229 | this.WriteDebug($"got error in fetching xmldata from {forecastUrl}: {err.Message}");
230 | throw;
231 | }
232 |
233 | return this;
234 | }
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/FloatingGlucose/Classes/DataSources/Plugins/YrWeatherService/YrHourByHour.cs:
--------------------------------------------------------------------------------
1 | /*
2 | Licensed under the Apache License, Version 2.0
3 |
4 | http://www.apache.org/licenses/LICENSE-2.0
5 |
6 | Adapted from http://xmltocsharp.azurewebsites.net/ by bjorninges.spam@bjorninge.no
7 | */
8 |
9 | using System.Collections.Generic;
10 | using System.Xml.Serialization;
11 |
12 | namespace Xml2CSharp
13 | {
14 | [XmlRoot(ElementName = "timezone")]
15 | public class Timezone
16 | {
17 | [XmlAttribute(AttributeName = "id")]
18 | public string Id { get; set; }
19 |
20 | [XmlAttribute(AttributeName = "utcoffsetMinutes")]
21 | public string UtcoffsetMinutes { get; set; }
22 | }
23 |
24 | [XmlRoot(ElementName = "location")]
25 | public class Location2
26 | {
27 | [XmlAttribute(AttributeName = "altitude")]
28 | public string Altitude { get; set; }
29 |
30 | [XmlAttribute(AttributeName = "latitude")]
31 | public string Latitude { get; set; }
32 |
33 | [XmlAttribute(AttributeName = "longitude")]
34 | public string Longitude { get; set; }
35 |
36 | [XmlAttribute(AttributeName = "geobase")]
37 | public string Geobase { get; set; }
38 |
39 | [XmlAttribute(AttributeName = "geobaseid")]
40 | public string Geobaseid { get; set; }
41 | }
42 |
43 | [XmlRoot(ElementName = "location")]
44 | public class Location
45 | {
46 | [XmlElement(ElementName = "name")]
47 | public string Name { get; set; }
48 |
49 | [XmlElement(ElementName = "type")]
50 | public string Type { get; set; }
51 |
52 | [XmlElement(ElementName = "country")]
53 | public string Country { get; set; }
54 |
55 | [XmlElement(ElementName = "timezone")]
56 | public Timezone Timezone { get; set; }
57 |
58 | [XmlElement(ElementName = "location2")]
59 | public Location2 Location2 { get; set; }
60 | }
61 |
62 | [XmlRoot(ElementName = "link")]
63 | public class Link
64 | {
65 | [XmlAttribute(AttributeName = "text")]
66 | public string Text { get; set; }
67 |
68 | [XmlAttribute(AttributeName = "url")]
69 | public string Url { get; set; }
70 |
71 | [XmlAttribute(AttributeName = "id")]
72 | public string Id { get; set; }
73 | }
74 |
75 | [XmlRoot(ElementName = "credit")]
76 | public class Credit
77 | {
78 | [XmlElement(ElementName = "link")]
79 | public Link Link { get; set; }
80 | }
81 |
82 | [XmlRoot(ElementName = "links")]
83 | public class Links
84 | {
85 | [XmlElement(ElementName = "link")]
86 | public List Link { get; set; }
87 | }
88 |
89 | [XmlRoot(ElementName = "meta")]
90 | public class Meta
91 | {
92 | [XmlElement(ElementName = "lastupdate")]
93 | public string Lastupdate { get; set; }
94 |
95 | [XmlElement(ElementName = "nextupdate")]
96 | public string Nextupdate { get; set; }
97 | }
98 |
99 | [XmlRoot(ElementName = "sun")]
100 | public class Sun
101 | {
102 | [XmlAttribute(AttributeName = "rise")]
103 | public string Rise { get; set; }
104 |
105 | [XmlAttribute(AttributeName = "set")]
106 | public string Set { get; set; }
107 | }
108 |
109 | [XmlRoot(ElementName = "time")]
110 | public class Time
111 | {
112 | [XmlElement(ElementName = "title")]
113 | public string Title { get; set; }
114 |
115 | [XmlElement(ElementName = "body")]
116 | public string Body { get; set; }
117 |
118 | [XmlAttribute(AttributeName = "from")]
119 | public string From { get; set; }
120 |
121 | [XmlAttribute(AttributeName = "to")]
122 | public string To { get; set; }
123 |
124 | [XmlElement(ElementName = "symbol")]
125 | public Symbol Symbol { get; set; }
126 |
127 | [XmlElement(ElementName = "precipitation")]
128 | public Precipitation Precipitation { get; set; }
129 |
130 | [XmlElement(ElementName = "windDirection")]
131 | public WindDirection WindDirection { get; set; }
132 |
133 | [XmlElement(ElementName = "windSpeed")]
134 | public WindSpeed WindSpeed { get; set; }
135 |
136 | [XmlElement(ElementName = "temperature")]
137 | public Temperature Temperature { get; set; }
138 |
139 | [XmlElement(ElementName = "pressure")]
140 | public Pressure Pressure { get; set; }
141 | }
142 |
143 | [XmlRoot(ElementName = "location")]
144 | public class Location3
145 | {
146 | [XmlElement(ElementName = "time")]
147 | public List