Skip to content

Commit

Permalink
Merge 88150ac into e2cc408
Browse files Browse the repository at this point in the history
  • Loading branch information
helto4real committed Apr 25, 2020
2 parents e2cc408 + 88150ac commit 2cd34f1
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 123 deletions.
2 changes: 1 addition & 1 deletion exampleapps/apps/test1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public override async Task InitializeAsync()
Log("Logging from global app");
LogError("OMG SOMETING IS WRONG {error}", "The error!");

Entity("light.my_light").TurnOn();
// Entity("light.my_light").TurnOn();
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/App/NetDaemon.App/NetDaemon.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="JoySoftware.HassClient" Version="0.1.2-alpha" />
<PackageReference Include="JoySoftware.HassClient" Version="0.1.3-alpha" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.3" />
</ItemGroup>

Expand Down
143 changes: 57 additions & 86 deletions src/Daemon/NetDaemon.Daemon/Daemon/NetDaemonHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,8 @@

namespace JoySoftware.HomeAssistant.NetDaemon.Daemon
{
public class NetDaemonHost : INetDaemonHost
public class NetDaemonHost : INetDaemonHost, IAsyncDisposable
{
/// <summary>
/// The intervall used when disconnected
/// </summary>
private const int _reconnectIntervall = 40000;

internal readonly Channel<(string, string)> _ttsMessageQueue =
Channel.CreateBounded<(string, string)>(20);

Expand Down Expand Up @@ -225,95 +220,66 @@ public async Task Run(string host, short port, bool ssl, string token, Cancellat
throw new NullReferenceException("HassClient cant be null when running daemon, check constructor!");
}

while (!cancellationToken.IsCancellationRequested)
try
{
try
{
bool connectResult;

if (hassioToken != null)
{
// We are running as hassio add-on
connectResult = await _hassClient.ConnectAsync(new Uri("ws://supervisor/core/websocket"),
hassioToken, true).ConfigureAwait(false);
}
else
{
connectResult = await _hassClient.ConnectAsync(host, port, ssl, token, true).ConfigureAwait(false);
}

if (!connectResult)
{
Connected = false;
Logger.LogWarning($"Home assistant is unavailable, retrying in {_reconnectIntervall / 1000} seconds...");
await _hassClient.CloseAsync().ConfigureAwait(false);
await Task.Delay(_reconnectIntervall, cancellationToken).ConfigureAwait(false);

continue;
}

// Setup TTS
Task handleTextToSpeechMessagesTask = HandleTextToSpeechMessages(cancellationToken);
bool connectResult;

await _hassClient.SubscribeToEvents().ConfigureAwait(false);

Connected = true;
InternalState = _hassClient.States.Values.Select(n => n.ToDaemonEntityState())
.ToDictionary(n => n.EntityId);

Logger.LogInformation(
hassioToken != null
? "Successfully connected to Home Assistant Core in Home Assistant Add-on"
: "Successfully connected to Home Assistant Core on host {host}:{port}", host, port);

while (!cancellationToken.IsCancellationRequested)
{
HassEvent changedEvent = await _hassClient.ReadEventAsync().ConfigureAwait(false);
if (changedEvent != null)
{
// Remove all completed Tasks
_eventHandlerTasks.RemoveAll(x => x.IsCompleted);
_eventHandlerTasks.Add(HandleNewEvent(changedEvent, cancellationToken));
}
else
{
// Will only happen when doing unit tests
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
}
}
if (hassioToken != null)
{
// We are running as hassio add-on
connectResult = await _hassClient.ConnectAsync(new Uri("ws://supervisor/core/websocket"),
hassioToken, true).ConfigureAwait(false);
}
catch (OperationCanceledException)
else
{
if (cancellationToken.IsCancellationRequested)
{
// Normal behaviour do nothing
await _scheduler.Stop().ConfigureAwait(false);
}
connectResult = await _hassClient.ConnectAsync(host, port, ssl, token, true).ConfigureAwait(false);
}
catch (Exception e)

if (!connectResult)
{
Connected = false;
Logger.LogError(e, "Error, during operation");
return;
}
finally

// Setup TTS
Task handleTextToSpeechMessagesTask = HandleTextToSpeechMessages(cancellationToken);

await _hassClient.SubscribeToEvents().ConfigureAwait(false);

Connected = true;
InternalState = _hassClient.States.Values.Select(n => n.ToDaemonEntityState())
.ToDictionary(n => n.EntityId);

Logger.LogInformation(
hassioToken != null
? "Successfully connected to Home Assistant Core in Home Assistant Add-on"
: "Successfully connected to Home Assistant Core on host {host}:{port}", host, port);

while (!cancellationToken.IsCancellationRequested)
{
try
HassEvent changedEvent = await _hassClient.ReadEventAsync(cancellationToken).ConfigureAwait(false);
if (changedEvent != null)
{
await _hassClient.CloseAsync().ConfigureAwait(false);
// Remove all completed Tasks
_eventHandlerTasks.RemoveAll(x => x.IsCompleted);
_eventHandlerTasks.Add(HandleNewEvent(changedEvent, cancellationToken));
}
catch
{
}

Connected = false;
if (!cancellationToken.IsCancellationRequested)
else
{
await Task.Delay(_reconnectIntervall, cancellationToken).ConfigureAwait(false);
// Will only happen when doing unit tests
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
}
}
}

await _scheduler.Stop().ConfigureAwait(false);
catch (OperationCanceledException)
{
// Normal
}
catch (Exception e)
{
Connected = false;
Logger.LogError(e, "Error, during operation");
}
}

public IScript RunScript(INetDaemonApp app, params string[] entityId) => new EntityManager(entityId, this, app);
Expand Down Expand Up @@ -350,19 +316,19 @@ public async Task Run(string host, short port, bool ssl, string token, Cancellat

public async Task Stop()
{
if (_hassClient == null)
{
throw new NullReferenceException("HassClient cant be null when running daemon, check constructor!");
}

if (_stopped)
{
return;
}

await _hassClient.CloseAsync().ConfigureAwait(false);
_eventActions.Clear();
_eventFunctionList.Clear();
_stateActions.Clear();
_serviceCallFunctionList.Clear();
await _scheduler.Stop().ConfigureAwait(false);

await _hassClient.CloseAsync().ConfigureAwait(false);

_stopped = true;
}

Expand Down Expand Up @@ -695,6 +661,11 @@ public void ClearAppInstances()
{
_daemonAppInstances.Clear();
}

public async ValueTask DisposeAsync()
{
await Stop();
}
}
public class DelayResult : IDelayResult
{
Expand Down
2 changes: 1 addition & 1 deletion src/Daemon/NetDaemon.Daemon/NetDaemon.Daemon.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

</PropertyGroup>
<ItemGroup>
<PackageReference Include="JoySoftware.HassClient" Version="0.1.2-alpha" />
<PackageReference Include="JoySoftware.HassClient" Version="0.1.3-alpha" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.3" />
</ItemGroup>
<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/DaemonRunner/DaemonRunner/DaemonRunner.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

</PropertyGroup>
<ItemGroup>
<PackageReference Include="JoySoftware.HassClient" Version="0.1.2-alpha" />
<PackageReference Include="JoySoftware.HassClient" Version="0.1.3-alpha" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.5.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.3" />
<PackageReference Include="YamlDotNet" Version="8.1.1" />
Expand Down
66 changes: 44 additions & 22 deletions src/DaemonRunner/DaemonRunner/Service/RunnerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@ public class RunnerService : BackgroundService
{
const string _version = "dev";

private NetDaemonHost? _daemonHost;
// private NetDaemonHost? _daemonHost;
private readonly ILogger<RunnerService> _logger;
private readonly ILoggerFactory _loggerFactory;

/// <summary>
/// The intervall used when disconnected
/// </summary>
private const int _reconnectIntervall = 40000;

public RunnerService(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<RunnerService>();
Expand All @@ -33,13 +38,8 @@ public RunnerService(ILoggerFactory loggerFactory)

public override async Task StopAsync(CancellationToken cancellationToken)
{
if (_daemonHost == null)
return;

_logger.LogInformation("Stopping NetDaemon...");
await _daemonHost.Stop().ConfigureAwait(false);

await Task.WhenAny(_daemonHost.Stop(), Task.Delay(1000, cancellationToken)).ConfigureAwait(false);
await base.StopAsync(cancellationToken);
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
Expand All @@ -61,28 +61,33 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)

var sourceFolder = config.SourceFolder;
var storageFolder = Path.Combine(config.SourceFolder!, ".storage");
_daemonHost = new NetDaemonHost(new HassClient(_loggerFactory), new DataRepository(storageFolder), _loggerFactory);

sourceFolder = Path.Combine(config.SourceFolder!, "apps");

// Automatically create source directories
if (!System.IO.Directory.Exists(sourceFolder))
System.IO.Directory.CreateDirectory(sourceFolder);

bool hasConnectedBefore = false;
bool generatedEntities = false;
while (!stoppingToken.IsCancellationRequested)
{
try
{
if (hasConnectedBefore)
{
// This is due to re-connect, it must be a re-connect
// so delay before retry connect again
await Task.Delay(_reconnectIntervall, stoppingToken).ConfigureAwait(false); // Wait x seconds
}

await using var _daemonHost = new NetDaemonHost(new HassClient(_loggerFactory), new DataRepository(storageFolder), _loggerFactory);

var daemonHostTask = _daemonHost.Run(config.Host, config.Port, config.Ssl, config.Token,
stoppingToken);

var nrOfTimesCheckForConnectedState = 0;
while (!_daemonHost.Connected && !stoppingToken.IsCancellationRequested)
{
await Task.Delay(1000, stoppingToken).ConfigureAwait(false);
if (nrOfTimesCheckForConnectedState++ > 3)
break;
}
await WaitForDaemonToConnect(_daemonHost, stoppingToken);

if (!stoppingToken.IsCancellationRequested)
{
if (_daemonHost.Connected)
Expand All @@ -93,8 +98,9 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
var envGenEntities = Environment.GetEnvironmentVariable("HASS_GEN_ENTITIES") ?? config.GenerateEntitiesOnStartup?.ToString();
if (envGenEntities is object)
{
if (envGenEntities == "True")
if (envGenEntities == "True" && !generatedEntities)
{
generatedEntities = true;
var codeGen = new CodeGenerator();
var source = codeGen.GenerateCode("Netdaemon.Generated.Extensions",
_daemonHost.State.Select(n => n.EntityId).Distinct());
Expand All @@ -108,6 +114,11 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)

// Wait until daemon stops
await daemonHostTask.ConfigureAwait(false);
if (!stoppingToken.IsCancellationRequested)
{
// It is disconnet, wait
_logger.LogWarning($"Home assistant is unavailable, retrying in {_reconnectIntervall / 1000} seconds...");
}
}
}
catch (TaskCanceledException)
Expand All @@ -121,21 +132,20 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
}
else
{
_logger.LogWarning("Home Assistant Core still unavailable, retrying in 40 seconds...");
_logger.LogWarning($"Home Assistant Core still unavailable, retrying in {_reconnectIntervall / 1000} seconds...");
}
}
}
catch (OperationCanceledException)
{
if (!stoppingToken.IsCancellationRequested)
{
_logger.LogWarning("Home Assistant Core disconnected!, retrying in 40 seconds...");

_logger.LogWarning($"Home assistant is disconnected, retrying in {_reconnectIntervall / 1000} seconds...");
}
}

if (!stoppingToken.IsCancellationRequested)
// The service is still running, we have error in connection to hass
await Task.Delay(40000, stoppingToken).ConfigureAwait(false); // Wait 5 seconds
// If we reached here it could be a re-connect
hasConnectedBefore = true;
}
}
catch (OperationCanceledException)
Expand All @@ -149,6 +159,18 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
_logger.LogInformation("Netdaemon exited!");
}

private async Task WaitForDaemonToConnect(NetDaemonHost daemonHost, CancellationToken stoppingToken)
{
var nrOfTimesCheckForConnectedState = 0;

while (!daemonHost.Connected && !stoppingToken.IsCancellationRequested)
{
await Task.Delay(1000, stoppingToken).ConfigureAwait(false);
if (nrOfTimesCheckForConnectedState++ > 5)
break;
}
}

private async Task<HostConfig?> ReadConfigAsync()
{
try
Expand Down
11 changes: 0 additions & 11 deletions tests/NetDaemon.Daemon.Tests/Demon/NetDaemonHostTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,17 +155,6 @@ public void RunNullReferenceToHassClientShouldThrowException()
{ var DefaultDaemonHost = new NetDaemonHost(null, null); });
}

[Fact]
public async Task RunWhenCanceledShouldCompleteWithCanceledException()
{
// ARRANGE

// ACT and ASSERT
await RunDefauldDaemonUntilCanceled(20);

Assert.False(DefaultDaemonHost.Connected);
}

[Fact]
public async Task SendEventShouldCallCorrectMethod()
{
Expand Down
Loading

0 comments on commit 2cd34f1

Please sign in to comment.