diff --git a/App.config b/App.config
index 3d87d88..b18ad13 100644
--- a/App.config
+++ b/App.config
@@ -1,12 +1,15 @@
-
+
-
-
+
+
+
+
+
-
+
@@ -16,9 +19,25 @@
-
- http://chip
-
-
\ No newline at end of file
+
+
+
+ 00:00:30
+
+
+ 00:01:00
+
+
+ http://{0}:{1}/api/hub/device-status
+
+
+ 172.23.10.3
+
+
+ 80
+
+
+
+
diff --git a/HomeStatusWindow.csproj b/HomeStatusWindow.csproj
index 7b6dfb4..e31ab29 100644
--- a/HomeStatusWindow.csproj
+++ b/HomeStatusWindow.csproj
@@ -9,11 +9,12 @@
Properties
HomeStatusWindow
HomeStatusWindow
- v4.6
+ v4.8
512
{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
4
true
+
AnyCPU
@@ -53,6 +54,7 @@
Designer
+
App.xaml
@@ -98,8 +100,11 @@
1.0.0.9
-
- 1.0.5
+
+ 6.0.8
+
+
+ 13.0.1
diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs
index 8e2b58e..571bbac 100644
--- a/Properties/Resources.Designer.cs
+++ b/Properties/Resources.Designer.cs
@@ -19,7 +19,7 @@ namespace HomeStatusWindow.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs
index eaf1078..5dd43a4 100644
--- a/Properties/Settings.Designer.cs
+++ b/Properties/Settings.Designer.cs
@@ -12,7 +12,7 @@ namespace HomeStatusWindow.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.3.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -47,15 +47,48 @@ namespace HomeStatusWindow.Properties {
}
}
- [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Configuration.DefaultSettingValueAttribute("http://chip")]
- public string ServerAddress {
+ [global::System.Configuration.DefaultSettingValueAttribute("00:00:30")]
+ public global::System.TimeSpan ReconnectTimerInterval {
get {
- return ((string)(this["ServerAddress"]));
+ return ((global::System.TimeSpan)(this["ReconnectTimerInterval"]));
}
- set {
- this["ServerAddress"] = value;
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("00:01:00")]
+ public global::System.TimeSpan ReconnectTimeout {
+ get {
+ return ((global::System.TimeSpan)(this["ReconnectTimeout"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("http://{0}:{1}/api/hub/device-status")]
+ public string ServerUri {
+ get {
+ return ((string)(this["ServerUri"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("172.23.10.3")]
+ public string ServerName {
+ get {
+ return ((string)(this["ServerName"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("80")]
+ public int ServerPort {
+ get {
+ return ((int)(this["ServerPort"]));
}
}
}
diff --git a/Properties/Settings.settings b/Properties/Settings.settings
index c0c83cc..b0ee502 100644
--- a/Properties/Settings.settings
+++ b/Properties/Settings.settings
@@ -8,8 +8,20 @@
-
- http://chip
+
+ 00:00:30
+
+
+ 00:01:00
+
+
+ http://{0}:{1}/api/hub/device-status
+
+
+ 172.23.10.3
+
+
+ 80
\ No newline at end of file
diff --git a/Status.cs b/Status.cs
index 6d4999a..35699dc 100644
--- a/Status.cs
+++ b/Status.cs
@@ -4,9 +4,9 @@
public class Status
{
// ReSharper disable once UnusedAutoPropertyAccessor.Global
- public bool? Washer { get; set; }
+ public bool Washer { get; set; }
// ReSharper disable once UnusedAutoPropertyAccessor.Global
- public bool? Dryer { get; set; }
+ public bool Dryer { get; set; }
}
}
diff --git a/StatusMessage.cs b/StatusMessage.cs
new file mode 100644
index 0000000..a0e8f25
--- /dev/null
+++ b/StatusMessage.cs
@@ -0,0 +1,8 @@
+namespace HomeStatusWindow
+{
+ public class StatusMessage
+ {
+ public string Name { get; set; }
+ public bool Status { get; set; }
+ }
+}
diff --git a/WindowSource.cs b/WindowSource.cs
index 16e4a41..5dac9fe 100644
--- a/WindowSource.cs
+++ b/WindowSource.cs
@@ -1,10 +1,11 @@
using FloatingStatusWindowLibrary;
using HomeStatusWindow.Properties;
+using Microsoft.AspNetCore.SignalR.Client;
using Newtonsoft.Json;
-using Quobject.SocketIoClientDotNet.Client;
using System;
using System.Text;
using System.Threading.Tasks;
+using System.Timers;
using System.Windows.Threading;
namespace HomeStatusWindow
@@ -14,21 +15,30 @@ namespace HomeStatusWindow
private readonly FloatingStatusWindow _floatingStatusWindow;
private readonly Dispatcher _dispatcher;
- private Socket _socket;
+ private HubConnection _hubConnection;
+ private DateTime _lastUpdate;
+ private Timer _reconnectTimer;
+
+ private readonly Status _lastStatus = new Status();
internal WindowSource()
{
_dispatcher = Dispatcher.CurrentDispatcher;
_floatingStatusWindow = new FloatingStatusWindow(this);
- _floatingStatusWindow.SetText(Resources.Loading);
+ UpdateText(Resources.Loading);
- Task.Factory.StartNew(Initialize);
+ // Initialize the connection
+ Task.Factory.StartNew(InitializeConnection);
}
- public void Dispose()
+ public async void Dispose()
{
- Terminate();
+ // Stop the reconnection timer (if any)
+ StopReconnectionTimer();
+
+ // Terminate the connection
+ await TerminateConnection();
_floatingStatusWindow.Save();
_floatingStatusWindow.Dispose();
@@ -48,12 +58,14 @@ namespace HomeStatusWindow
public string Name => Resources.Name;
- public bool HasSettingsMenu => false;
- public bool HasRefreshMenu => false;
- public bool HasAboutMenu => false;
-
public System.Drawing.Icon Icon => Resources.ApplicationIcon;
+ public bool HasSettingsMenu => false;
+
+ public bool HasRefreshMenu => false;
+
+ public bool HasAboutMenu => false;
+
public string WindowSettings
{
get => Settings.Default.WindowSettings;
@@ -64,64 +76,121 @@ namespace HomeStatusWindow
}
}
- private readonly Status _fullStatus = new Status();
-
- private void Initialize()
+ private void StartReconnectionTimer()
{
- // Create the socket
- _socket = IO.Socket(Settings.Default.ServerAddress);
+ // Stop the current timer (if any)
+ StopReconnectionTimer();
- // Setup for status events
- _socket.On("status", UpdateText);
-
- _socket.On(Socket.EVENT_CONNECT, () => _socket.Emit("getStatus"));
- _socket.On(Socket.EVENT_DISCONNECT, () => SetText(Resources.Disconnected));
+ // Create and start the reconnection timer
+ _reconnectTimer = new Timer(Settings.Default.ReconnectTimerInterval.TotalMilliseconds);
+ _reconnectTimer.Elapsed += HandleReconnectTimerElapsed;
+ _reconnectTimer.Start();
}
- private void Terminate()
+ private void StopReconnectionTimer()
{
- _socket?.Disconnect();
+ // Get rid of the reconnection timer
+ _reconnectTimer?.Dispose();
+ _reconnectTimer = null;
}
- private void UpdateText(object data)
+ private void HandleReconnectTimerElapsed(object sender, ElapsedEventArgs e)
{
- var json = (string)data;
-
- var status = JsonConvert.DeserializeObject(json);
-
- if (status.Dryer.HasValue)
- _fullStatus.Dryer = status.Dryer;
-
- if (status.Washer.HasValue)
- _fullStatus.Washer = status.Washer;
-
- var text = GetText(_fullStatus);
-
- SetText(text);
+ // See if we haven't heard from the server within the timeout and reconnect if needed
+ if (DateTime.Now - _lastUpdate >= Settings.Default.ReconnectTimeout)
+ InitializeConnection();
}
- private void SetText(string text)
- {
- // Update the window on the main thread
- _dispatcher.Invoke(() => _floatingStatusWindow.SetText(text));
- }
-
- private static string GetText(Status status)
+ private void InitializeConnection()
{
try
{
- var output = new StringBuilder();
+ // Stop the reconnection timer (if any)
+ StopReconnectionTimer();
- output.AppendFormat(Resources.DryerStatus, status.Dryer.GetValueOrDefault() ? Resources.On : Resources.Off);
- output.AppendLine();
- output.AppendFormat(Resources.WasherStatus, status.Washer.GetValueOrDefault() ? Resources.On : Resources.Off);
+ // Create the URI for the server
+ var serverUri = string.Format(Settings.Default.ServerUri, Settings.Default.ServerName, Settings.Default.ServerPort);
- return output.ToString();
+ _hubConnection = new HubConnectionBuilder()
+ .WithUrl(serverUri)
+ .Build();
+
+ _hubConnection.Closed += error =>
+ {
+ StartReconnectionTimer();
+
+ return Task.CompletedTask;
+ };
+
+ _hubConnection.On("LatestStatus", (message) =>
+ {
+ try
+ {
+ var statusMessage = JsonConvert.DeserializeObject(message);
+
+ switch (statusMessage?.Name)
+ {
+ case "washer":
+ _lastStatus.Washer = statusMessage.Status;
+ break;
+ case "dryer":
+ _lastStatus.Dryer = statusMessage.Status;
+ break;
+ }
+
+ UpdateDisplay(_lastStatus);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ }
+ });
+
+ // Open the connection
+ _hubConnection.StartAsync().Wait();
+
+ _hubConnection.InvokeAsync("RequestLatestStatus").Wait();
}
- catch (Exception ex)
+ catch (Exception exception)
{
- return ex.Message;
+ UpdateText($"Connection error: {exception.Message}");
+ }
+ finally
+ {
+ // Start the reconnection check timer
+ StartReconnectionTimer();
}
}
+
+ private async Task TerminateConnection()
+ {
+ // If the client doesn't exist or isn't open then there's nothing to do
+ if (_hubConnection == null || _hubConnection.State != HubConnectionState.Connected)
+ return;
+
+ // Close the connection
+ await _hubConnection.DisposeAsync();
+ }
+
+ private void UpdateDisplay(Status status)
+ {
+ // Last update was now
+ _lastUpdate = DateTime.Now;
+
+ // Create a string builder
+ var text = new StringBuilder();
+
+ text.AppendFormat(Resources.DryerStatus, status.Dryer ? Resources.On : Resources.Off);
+ text.AppendLine();
+ text.AppendFormat(Resources.WasherStatus, status.Washer ? Resources.On : Resources.Off);
+
+ // Set the text
+ UpdateText(text.ToString());
+ }
+
+ private void UpdateText(string text)
+ {
+ _dispatcher.InvokeAsync(() => _floatingStatusWindow.SetText(text));
+ }
}
-}
+}
\ No newline at end of file