public async Task ACustomEventShouldUsingSelectorUsingDataNotExisstFuncNotCall() { // ARRANGE var hcMock = HassClientMock.DefaultMock; var daemonHost = new NetDaemonHost(hcMock.Object, new Mock <IDataRepository>().Object); dynamic dynObject = new ExpandoObject(); dynObject.Test = "Hello World!"; hcMock.AddCustomEvent("CUSTOM_EVENT", dynObject); var cancelSource = hcMock.GetSourceWithTimeout(); var isCalled = false; string?message = ""; daemonHost .Events(n => n.EventId == "CUSTOM_EVENT" && n?.Data?.NotExist == "Hello Test!") .Call((ev, data) => { isCalled = true; message = data?.Test; return(Task.CompletedTask); }).Execute(); try { await daemonHost.Run("host", 8123, false, "token", cancelSource.Token).ConfigureAwait(false); } catch (TaskCanceledException) { // Expected behaviour } Assert.False(isCalled); }
public (Task, CancellationTokenSource) ReturnRunningNotConnectedDaemonHostTask(short milliSeconds = 100, bool overrideDebugNotCancel = false) { var cancelSource = Debugger.IsAttached && !overrideDebugNotCancel ? new CancellationTokenSource() : new CancellationTokenSource(milliSeconds); return(_notConnectedDaemonHost.Run("host", 8123, false, "token", cancelSource.Token), cancelSource); }
public async Task ACustomEventShouldDoCorrectCall() { // ARRANGE var hcMock = HassClientMock.DefaultMock; await using var daemonHost = new NetDaemonHost(hcMock.Object, new Mock <IDataRepository>().Object); var app = new FluentTestApp(); app.Id = "id"; daemonHost.InternalRunningAppInstances[app.Id] = app; await app.StartUpAsync(daemonHost); dynamic dynObject = new ExpandoObject(); dynObject.Test = "Hello World!"; hcMock.AddCustomEvent("CUSTOM_EVENT", dynObject); var cancelSource = hcMock.GetSourceWithTimeout(); var isCalled = false; string?message = ""; app .Event("CUSTOM_EVENT") .Call((ev, data) => { isCalled = true; message = data?.Test; return(Task.CompletedTask); }).Execute(); try { await daemonHost.Run("host", 8123, false, "token", cancelSource.Token).ConfigureAwait(false); } catch (TaskCanceledException) { // Expected behaviour } Assert.True(isCalled); Assert.Equal("Hello World!", message); }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { _logger.LogInformation($"Starting NeDaemon (version {_version})..."); var config = await ReadConfigAsync().ConfigureAwait(false); if (config == null) { _logger.LogError("No config specified, file or environment variables! Exiting..."); return; } EnsureApplicationDirectoryExists(config); var sourceFolder = config.SourceFolder; var storageFolder = Path.Combine(config.SourceFolder !, ".storage"); 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); await WaitForDaemonToConnect(_daemonHost, stoppingToken).ConfigureAwait(false); if (!stoppingToken.IsCancellationRequested) { if (_daemonHost.Connected) { try { // Generate code if requested var envGenEntities = Environment.GetEnvironmentVariable("HASS_GEN_ENTITIES") ?? config.GenerateEntitiesOnStartup?.ToString(); if (envGenEntities is object) { if (envGenEntities == "True" && !generatedEntities) { generatedEntities = true; var codeGen = new CodeGenerator(); var source = codeGen.GenerateCode("Netdaemon.Generated.Extensions", _daemonHost.State.Select(n => n.EntityId).Distinct()); System.IO.File.WriteAllText(System.IO.Path.Combine(sourceFolder !, "_EntityExtensions.cs"), source); } } using (var codeManager = new CodeManager(sourceFolder !, _daemonHost.Logger)) { await codeManager.EnableApplicationDiscoveryServiceAsync(_daemonHost, discoverServicesOnStartup : true).ConfigureAwait(false); // 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) { _logger.LogInformation("Canceling NetDaemon service..."); } catch (Exception e) { _logger.LogError(e, "Failed to load applications"); } } else { _logger.LogWarning($"Home Assistant Core still unavailable, retrying in {_reconnectIntervall / 1000} seconds..."); } } } catch (OperationCanceledException) { if (!stoppingToken.IsCancellationRequested) { _logger.LogWarning($"Home assistant is disconnected, retrying in {_reconnectIntervall / 1000} seconds..."); } } // If we reached here it could be a re-connect hasConnectedBefore = true; } } catch (OperationCanceledException) { } // Normal exit catch (Exception e) { _logger.LogError(e, "NetDaemon had unhandled exception, closing..."); } _logger.LogInformation("Netdaemon exited!"); }
private async Task Run(NetDaemonHost daemonHost, CancellationToken stoppingToken) { 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(ReconnectInterval, stoppingToken).ConfigureAwait(false); // Wait x seconds _logger.LogInformation($"Restarting NeDaemon service (version {Version})..."); } var daemonHostTask = daemonHost.Run( _homeAssistantSettings.Host, _homeAssistantSettings.Port, _homeAssistantSettings.Ssl, _homeAssistantSettings.Token, stoppingToken ); if (!await WaitForDaemonToConnect(daemonHost, stoppingToken).ConfigureAwait(false)) { continue; } if (!stoppingToken.IsCancellationRequested) { if (daemonHost.IsConnected) { try { // Generate code if requested if (_sourcePath is string) { await GenerateEntities(daemonHost, _sourcePath).ConfigureAwait(false); } if (_loadedDaemonApps is null) { _loadedDaemonApps = _daemonAppCompiler.GetApps(); } if (_loadedDaemonApps?.Any() != true) { _logger.LogError("No NetDaemon apps could be found, exiting..."); return; } IInstanceDaemonApp?codeManager = new CodeManager(_loadedDaemonApps, _logger, _yamlConfig); await daemonHost.Initialize(codeManager).ConfigureAwait(false); // Wait until daemon stops await daemonHostTask.ConfigureAwait(false); } catch (TaskCanceledException) { _logger.LogTrace("Canceling NetDaemon service..."); } catch (Exception e) { _logger.LogError(e, "Failed to load applications"); } } else { _logger.LogWarning($"Home Assistant Core still unavailable, retrying in {ReconnectInterval / 1000} seconds..."); } } } catch (OperationCanceledException) { if (!stoppingToken.IsCancellationRequested) { _logger.LogWarning($"Home assistant is disconnected, retrying in {ReconnectInterval / 1000} seconds..."); } } catch (Exception e) { _logger.LogError("Error in NetDaemon service!, set trace log level to see details."); _logger.LogTrace(e, "Error in NetDaemon service!"); } finally { try { await daemonHost.Stop().ConfigureAwait(false); } catch (Exception e) { _logger.LogError("Error stopping NetDaemonInstance, enable trace level logging for details"); _logger.LogTrace(e, "Error stopping NetDaemonInstance"); } } // If we reached here it could be a re-connect _hasConnectedBefore = true; } }