예제 #1
0
        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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
        }
예제 #4
0
        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!");
        }
예제 #5
0
        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;
            }
        }