diff --git a/SharpCaster.sln b/SharpCaster.sln
index e912ef5..5014bf6 100644
--- a/SharpCaster.sln
+++ b/SharpCaster.sln
@@ -13,8 +13,8 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {7B2AED87-CDE7-4B71-A5B6-19512202FF60}.Debug|Any CPU.ActiveCfg = Release|Any CPU
- {7B2AED87-CDE7-4B71-A5B6-19512202FF60}.Debug|Any CPU.Build.0 = Release|Any CPU
+ {7B2AED87-CDE7-4B71-A5B6-19512202FF60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7B2AED87-CDE7-4B71-A5B6-19512202FF60}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B2AED87-CDE7-4B71-A5B6-19512202FF60}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B2AED87-CDE7-4B71-A5B6-19512202FF60}.Release|Any CPU.Build.0 = Release|Any CPU
{C8C0A3A8-C6AC-4E1B-8D3B-E6764C45C35B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
diff --git a/Sharpcaster.Test/ChromecastConnectionTester.cs b/Sharpcaster.Test/ChromecastConnectionTester.cs
index 05f39d2..3e1b4cc 100644
--- a/Sharpcaster.Test/ChromecastConnectionTester.cs
+++ b/Sharpcaster.Test/ChromecastConnectionTester.cs
@@ -1,11 +1,21 @@
-using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestPlatform.Utilities;
+using System.Threading;
+using System.Threading.Tasks;
using Xunit;
+using Xunit.Abstractions;
namespace Sharpcaster.Test
{
[Collection("SingleCollection")]
public class ChromecastConnectionTester
{
+
+ private ITestOutputHelper output;
+ public ChromecastConnectionTester(ITestOutputHelper outputHelper)
+ {
+ output = outputHelper;
+ }
+
[Fact]
public async Task SearchChromecastsAndConnectToIt()
{
@@ -15,5 +25,26 @@ public async Task SearchChromecastsAndConnectToIt()
Assert.NotNull(status);
}
+ [Fact]
+ public async Task SearchChromecastsAndConnectToItThenWaitForItToShutdown()
+ {
+ var client = await TestHelper.CreateConnectAndLoadAppClient(output);
+
+ Assert.NotNull(client.GetChromecastStatus());
+
+ AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
+
+ client.Disconnected += (sender, args) =>
+ {
+ output.WriteLine("Chromecast did shutdown");
+ _autoResetEvent.Set();
+ };
+
+ //This checks that within 30 seconds we have noticed that device was turned off
+ //This need manual intervention to turn off the device
+ output.WriteLine("Waiting for Chromecast to shutdown");
+ Assert.True(_autoResetEvent.WaitOne(30000));
+ }
+
}
}
diff --git a/Sharpcaster/Channels/HeartbeatChannel.cs b/Sharpcaster/Channels/HeartbeatChannel.cs
index a4cf0b4..392a4a0 100644
--- a/Sharpcaster/Channels/HeartbeatChannel.cs
+++ b/Sharpcaster/Channels/HeartbeatChannel.cs
@@ -1,7 +1,10 @@
-using Sharpcaster.Interfaces;
+using Microsoft.Extensions.Logging;
+using Sharpcaster.Interfaces;
using Sharpcaster.Messages.Heartbeat;
+using System;
using System.Diagnostics;
using System.Threading.Tasks;
+using System.Timers;
namespace Sharpcaster.Channels
{
@@ -10,28 +13,52 @@ namespace Sharpcaster.Channels
///
public class HeartbeatChannel : ChromecastChannel, IHeartbeatChannel
{
+ private ILogger _logger = null;
+ private Timer _timer;
+
///
/// Initializes a new instance of HeartbeatChannel class
///
- public HeartbeatChannel() : base("tp.heartbeat")
+ public HeartbeatChannel(ILogger logger = null) : base("tp.heartbeat")
{
+ _logger = logger;
+ _timer = new Timer(10000); // timeout is 10 seconds.
+ // Because Chromecast only waits for 8 seconds for response
+ _timer.Elapsed += TimerElapsed;
+ _timer.AutoReset = false;
}
+ public event EventHandler StatusChanged;
+
///
/// Called when a message for this channel is received
///
/// message to process
public override async Task OnMessageReceivedAsync(IMessage message)
{
- if (message is PingMessage)
- {
- await SendAsync(new PongMessage());
- }
- else
- {
- //TODO: Remove this if we don't hit this
- Debugger.Break();
- }
+ _logger.LogDebug("Received ping message on Heartbeat channel");
+ await SendAsync(new PongMessage());
+ _logger.LogDebug("Sent pong message on Heartbeat channel");
+ _timer.Stop();
+ _timer.Start();
+ }
+
+ public void StartTimeoutTimer()
+ {
+ _timer.Start();
+ _logger.LogDebug("Started heartbeat timeout timer");
+ }
+
+ public void StopTimeoutTimer()
+ {
+ _timer.Stop();
+ _logger.LogDebug("Stopped heartbeat timeout timer");
+ }
+
+ private void TimerElapsed(object sender, ElapsedEventArgs e)
+ {
+ _logger.LogDebug("Heartbeat timeout");
+ StatusChanged?.Invoke(this, e);
}
}
}
diff --git a/Sharpcaster/ChromeCastClient.cs b/Sharpcaster/ChromeCastClient.cs
index 80f623d..bddd034 100644
--- a/Sharpcaster/ChromeCastClient.cs
+++ b/Sharpcaster/ChromeCastClient.cs
@@ -114,17 +114,26 @@ public async Task ConnectChromecast(ChromecastReceiver chromec
_client = new TcpClient();
await _client.ConnectAsync(chromecastReceiver.DeviceUri.Host, chromecastReceiver.Port);
+
//Open SSL stream to Chromecast and bypass all SSL validation
var secureStream = new SslStream(_client.GetStream(), true, (sender, certificate, chain, sslPolicyErrors) => true);
await secureStream.AuthenticateAsClientAsync(chromecastReceiver.DeviceUri.Host);
_stream = secureStream;
+
ReceiveTcs = new TaskCompletionSource();
Receive();
+ GetChannel().StartTimeoutTimer();
+ GetChannel().StatusChanged += HeartBeatTimedOut;
await GetChannel().ConnectAsync();
return await GetChannel().GetChromecastStatusAsync();
}
+ private async void HeartBeatTimedOut(object sender, EventArgs e)
+ {
+ await DisconnectAsync();
+ }
+
private void Receive()
{
Task.Run(async () =>
@@ -237,6 +246,8 @@ public async Task DisconnectAsync()
{
channel.GetType().GetProperty("Status").SetValue(channel, null);
}
+ GetChannel().StopTimeoutTimer();
+ GetChannel().StatusChanged -= HeartBeatTimedOut;
await Dispose();
}
diff --git a/Sharpcaster/Interfaces/IHeartbeatChannel.cs b/Sharpcaster/Interfaces/IHeartbeatChannel.cs
index 9dfaf73..347d832 100644
--- a/Sharpcaster/Interfaces/IHeartbeatChannel.cs
+++ b/Sharpcaster/Interfaces/IHeartbeatChannel.cs
@@ -1,9 +1,20 @@
-namespace Sharpcaster.Interfaces
+using System.Net.NetworkInformation;
+using System;
+using System.Threading.Tasks;
+using System.Timers;
+
+namespace Sharpcaster.Interfaces
{
///
/// Interface for the heartbeat channel
///
interface IHeartbeatChannel : IChromecastChannel
{
+ void StartTimeoutTimer();
+ void StopTimeoutTimer();
+ ///
+ /// Raised when the status has changed
+ ///
+ event EventHandler StatusChanged;
}
}