From 8ef7bd10b0caab071cbb1b58f096f3a009f7b63e Mon Sep 17 00:00:00 2001 From: Frank Bakker Date: Wed, 12 Oct 2022 20:46:05 +0200 Subject: [PATCH 1/8] First try to get ND running in Webapps --- NetDaemon.sln | 15 +++++++ TestWeb/Program.cs | 14 +++++++ TestWeb/Properties/launchSettings.json | 37 +++++++++++++++++ TestWeb/TestWeb.csproj | 17 ++++++++ TestWeb/appsettings.json | 16 ++++++++ .../Integration/TestRuntime.cs | 6 +-- .../Internal/NetDaemonRuntimeTests.cs | 8 ++-- .../Extensions/HostBuilderExtensions.cs | 6 ++- .../NetDaemon.Runtime/Common/IRuntime.cs | 1 + .../Internal/NetDaemonRuntime.cs | 41 +++++++++++-------- .../Internal/RuntimeService.cs | 24 ++++++----- 11 files changed, 150 insertions(+), 35 deletions(-) create mode 100644 TestWeb/Program.cs create mode 100644 TestWeb/Properties/launchSettings.json create mode 100644 TestWeb/TestWeb.csproj create mode 100644 TestWeb/appsettings.json diff --git a/NetDaemon.sln b/NetDaemon.sln index 5475ad59e..30bb0b8d5 100644 --- a/NetDaemon.sln +++ b/NetDaemon.sln @@ -57,6 +57,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetDaemon.Extensions.Tts", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetDaemon.Extensions.MqttEntityManager", "src\Extensions\NetDaemon.Extensions.MqttEntityManager\NetDaemon.Extensions.MqttEntityManager.csproj", "{3EB8C461-C91E-4900-BFBD-0986CBBE87A6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestWeb", "TestWeb\TestWeb.csproj", "{AEBC7828-7C19-4A86-B6E2-58B5171347B1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -271,6 +273,18 @@ Global {F4B29B77-9B92-4037-A884-288CA5EF0B78}.Release|x64.Build.0 = Release|Any CPU {F4B29B77-9B92-4037-A884-288CA5EF0B78}.Release|x86.ActiveCfg = Release|Any CPU {F4B29B77-9B92-4037-A884-288CA5EF0B78}.Release|x86.Build.0 = Release|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Debug|x64.ActiveCfg = Debug|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Debug|x64.Build.0 = Debug|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Debug|x86.ActiveCfg = Debug|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Debug|x86.Build.0 = Debug|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Release|Any CPU.Build.0 = Release|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Release|x64.ActiveCfg = Release|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Release|x64.Build.0 = Release|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Release|x86.ActiveCfg = Release|Any CPU + {AEBC7828-7C19-4A86-B6E2-58B5171347B1}.Release|x86.Build.0 = Release|Any CPU {3EB8C461-C91E-4900-BFBD-0986CBBE87A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3EB8C461-C91E-4900-BFBD-0986CBBE87A6}.Debug|Any CPU.Build.0 = Debug|Any CPU {3EB8C461-C91E-4900-BFBD-0986CBBE87A6}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -307,6 +321,7 @@ Global {00333EBA-DB52-4D56-ADF7-940FB533E530} = {DFF3E7AA-7A50-4A1E-B3F8-EC01531FB83D} {F4B29B77-9B92-4037-A884-288CA5EF0B78} = {DFF3E7AA-7A50-4A1E-B3F8-EC01531FB83D} {3EB8C461-C91E-4900-BFBD-0986CBBE87A6} = {DFF3E7AA-7A50-4A1E-B3F8-EC01531FB83D} + {AEBC7828-7C19-4A86-B6E2-58B5171347B1} = {E15D4280-7FFC-4F8B-9B8C-CF9AF2BF838C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7C5FBB7F-654C-4CAC-964F-6D71AF3D62F8} diff --git a/TestWeb/Program.cs b/TestWeb/Program.cs new file mode 100644 index 000000000..22ca99175 --- /dev/null +++ b/TestWeb/Program.cs @@ -0,0 +1,14 @@ +using NetDaemon.HassModel.Common; +using NetDaemon.Runtime; + +var builder = WebApplication.CreateBuilder(args); +builder.Host.UseNetDaemonRuntime(); + +var app = builder.Build(); + +app.MapGet("/", (IHaContext ha) => ha.GetAllEntities().Where(e=>e.EntityId.StartsWith("light")).Select(e => e.EntityId)); +app.MapGet("/Off", (IHaContext ha) => ha.Entity("light.spots_woonkamer_rechts").CallService("turn_off")); +app.MapGet("/On", (IHaContext ha) => ha.Entity("light.spots_woonkamer_rechts").CallService("turn_on")); +app.MapGet("/State/{id}", (IHaContext ha, string id) => ha.Entity(id).EntityState); + +app.Run(); \ No newline at end of file diff --git a/TestWeb/Properties/launchSettings.json b/TestWeb/Properties/launchSettings.json new file mode 100644 index 000000000..581114cdf --- /dev/null +++ b/TestWeb/Properties/launchSettings.json @@ -0,0 +1,37 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:59620", + "sslPort": 44389 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5272", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7272;http://localhost:5272", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/TestWeb/TestWeb.csproj b/TestWeb/TestWeb.csproj new file mode 100644 index 000000000..c35082204 --- /dev/null +++ b/TestWeb/TestWeb.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/TestWeb/appsettings.json b/TestWeb/appsettings.json new file mode 100644 index 000000000..dde3bbeb3 --- /dev/null +++ b/TestWeb/appsettings.json @@ -0,0 +1,16 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "HomeAssistant": { + "Host": "localhost", + "Port": 8123, + "Ssl": false, + "Token": "", + "InsecureBypassCertificateErrors": false + } +} diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs b/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs index dc9923db4..6c47269ff 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs @@ -25,7 +25,7 @@ public async Task TestApplicationIsLoaded() ).Build(); - var runnerTask = host.RunAsync(); + var runnerTask = host.RunAsync(timedCancellationSource.Token); haRunner.ConnectMock.OnNext(haRunner.ClientMock.ConnectionMock.Object); var service = (NetDaemonRuntime) host.Services.GetService()!; @@ -50,7 +50,7 @@ public async Task TestApplicationReactToNewEvents() .AddTransient>(_ => haRunner.ClientMock.ConnectionMock.HomeAssistantEventMock) ).Build(); - var runnerTask = host.RunAsync(); + var runnerTask = host.RunAsync(timedCancellationSource.Token); while (!haRunner.ConnectMock.HasObservers && !runnerTask.IsCompleted) await Task.Delay(10); haRunner.ConnectMock.OnNext(haRunner.ClientMock.ConnectionMock.Object); _ = (NetDaemonRuntime) host.Services.GetService()!; @@ -87,7 +87,7 @@ public async Task TestApplicationReactToNewEventsAndThrowException() .AddNetDaemonApp() ).Build(); - var runnerTask = host.RunAsync(); + var runnerTask = host.RunAsync(timedCancellationSource.Token); while (!haRunner.ConnectMock.HasObservers && !runnerTask.IsCompleted) await Task.Delay(10); haRunner.ConnectMock.OnNext(haRunner.ClientMock.ConnectionMock.Object); _ = (NetDaemonRuntime) host.Services.GetService()!; diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs b/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs index d1e08ea63..5a833fd58 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs @@ -13,7 +13,6 @@ public class NetDaemonRuntimeTests public async Task TestExecuteAsync() { var homeAssistantRunnerMock = new Mock(); - var appModelMock = new Mock(); var serviceProviderMock = new Mock(); var loggerMock = new Mock>(); @@ -25,7 +24,6 @@ public async Task TestExecuteAsync() homeAssistantRunnerMock.Object, new FakeHassSettingsOptions(), new FakeApplicationLocationSettingsOptions(), - appModelMock.Object, serviceProviderMock.Object, loggerMock.Object, Mock.Of() @@ -68,13 +66,13 @@ public async Task TestOnConnect() serviceCollection.AddTransient>(_ => hassEventSubject); serviceCollection.AddSingleton(_ => homeAssistantRunnerMock.Object); serviceCollection.AddScoped(_ => scopedContext); + serviceCollection.AddSingleton(appModelMock.Object); var serviceProvider = serviceCollection.BuildServiceProvider(); await using var runtime = new NetDaemonRuntime( homeAssistantRunnerMock.Object, new FakeHassSettingsOptions(), new FakeApplicationLocationSettingsOptions(), - appModelMock.Object, serviceProvider, loggerMock.Object, Mock.Of() @@ -85,6 +83,8 @@ public async Task TestOnConnect() homeAssistantConnectionMock.Object ); + await runtime.WhenStarted; + appModelMock.Verify(n => n.InitializeAsync(It.IsAny())); } @@ -119,13 +119,13 @@ public async Task TestOnDisconnect() serviceCollection.AddTransient>(_ => hassEventSubject); serviceCollection.AddSingleton(_ => homeAssistantRunnerMock.Object); serviceCollection.AddScoped(_ => scopedContext); + serviceCollection.AddSingleton(appModelMock.Object); var serviceProvider = serviceCollection.BuildServiceProvider(); await using var runtime = new NetDaemonRuntime( homeAssistantRunnerMock.Object, new FakeHassSettingsOptions(), new FakeApplicationLocationSettingsOptions(), - appModelMock.Object, serviceProvider, loggerMock.Object, Mock.Of() diff --git a/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs b/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs index 3b9f6f437..4a0e8b410 100644 --- a/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs +++ b/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs @@ -12,6 +12,8 @@ public static IHostBuilder UseNetDaemonAppSettings(this IHostBuilder hostBuilder return hostBuilder .ConfigureServices((context, services) => services.ConfigureNetDaemonServices(context.Configuration) + // services.Configure(context.Configuration.GetSection("NetDaemon")); + ) .ConfigureAppConfiguration((ctx, config) => { @@ -38,8 +40,8 @@ public static IHostBuilder UseNetDaemonRuntime(this IHostBuilder hostBuilder) services.AddLogging(); services.AddHostedService(); services.AddHomeAssistantClient(); - + services.Configure(context.Configuration.GetSection("HomeAssistant")); services.AddSingleton(); }); } -} +} \ No newline at end of file diff --git a/src/Runtime/NetDaemon.Runtime/Common/IRuntime.cs b/src/Runtime/NetDaemon.Runtime/Common/IRuntime.cs index 644010067..6e2250b80 100644 --- a/src/Runtime/NetDaemon.Runtime/Common/IRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime/Common/IRuntime.cs @@ -3,4 +3,5 @@ namespace NetDaemon.Runtime; public interface IRuntime : IAsyncDisposable { Task ExecuteAsync(CancellationToken stoppingToken); + Task WhenStarted { get; } } \ No newline at end of file diff --git a/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs b/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs index a9fabf80c..be4c928f8 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs @@ -8,7 +8,7 @@ internal class NetDaemonRuntime : IRuntime { private const string Version = "custom_compiled"; private const int TimeoutInSeconds = 30; - private readonly IAppModel _appModel; + private readonly IAppModel? _appModel; private readonly ICacheManager _cacheManager; private readonly HomeAssistantSettings _haSettings; @@ -25,7 +25,6 @@ public NetDaemonRuntime( IHomeAssistantRunner homeAssistantRunner, IOptions settings, IOptions locationSettings, - IAppModel appModel, IServiceProvider serviceProvider, ILogger logger, ICacheManager cacheManager) @@ -33,7 +32,7 @@ public NetDaemonRuntime( _haSettings = settings.Value; _homeAssistantRunner = homeAssistantRunner; _locationSettings = locationSettings; - _appModel = appModel; + _appModel = serviceProvider.GetService(); _serviceProvider = serviceProvider; _logger = logger; _cacheManager = cacheManager; @@ -43,6 +42,9 @@ public NetDaemonRuntime( internal IReadOnlyCollection ApplicationInstances => _applicationModelContext?.Applications ?? Array.Empty(); + public Task WhenStarted => connected.Task; + private TaskCompletionSource connected = new(); + public async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation($"Starting NetDaemon runtime version {Version}."); @@ -71,7 +73,7 @@ await _homeAssistantRunner.RunAsync( // Ignore and just stop } - _logger.LogInformation("Exiting NetDaemon runtime."); + _logger.LogInformation("Exiting NetDaemon runtime"); } public async ValueTask DisposeAsync() @@ -89,20 +91,27 @@ CancellationToken cancelToken InternalConnection = haConnection; _logger.LogInformation("Successfully connected to Home Assistant"); - if (!string.IsNullOrEmpty(_locationSettings.Value.ApplicationConfigurationFolder)) - _logger.LogDebug("Loading applications from folder {Path}", - Path.GetFullPath(_locationSettings.Value.ApplicationConfigurationFolder)); - else - _logger.LogDebug("Loading applications with no configuration folder"); - await _cacheManager.InitializeAsync(cancelToken).ConfigureAwait(false); - _applicationModelContext = - await _appModel.InitializeAsync(CancellationToken.None).ConfigureAwait(false); + if (_appModel is not null) + { + if (!string.IsNullOrEmpty(_locationSettings.Value.ApplicationConfigurationFolder)) + _logger.LogDebug("Loading applications from folder {Path}", + Path.GetFullPath(_locationSettings.Value.ApplicationConfigurationFolder)); + else + _logger.LogDebug("Loading applications with no configuration folder"); + + _applicationModelContext = + await _appModel.InitializeAsync(CancellationToken.None).ConfigureAwait(false); + + + // Handle state change for apps if registered + var appStateHandler = _serviceProvider.GetService(); + if (appStateHandler != null) + await appStateHandler.InitializeAsync(haConnection, _applicationModelContext); + } + connected.SetResult(); - // Handle state change for apps if registered - var appStateHandler = _serviceProvider.GetRequiredService(); - await appStateHandler.InitializeAsync(haConnection, _applicationModelContext); } catch (Exception e) { @@ -144,4 +153,4 @@ private async Task DisposeApplicationsAsync() _applicationModelContext = null; } } -} +} \ No newline at end of file diff --git a/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs b/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs index d267f6a83..17edea34b 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs @@ -2,23 +2,27 @@ namespace NetDaemon.Runtime.Internal; internal class RuntimeService : BackgroundService { - private readonly IHostApplicationLifetime _hostLifetime; - private readonly IRuntime _runtime; + + private Task? _executingTask; - public RuntimeService( - IHostApplicationLifetime hostLifetime, - IRuntime runtime - ) + public RuntimeService(IRuntime runtime) { - _hostLifetime = hostLifetime; _runtime = runtime; } + public override async Task StartAsync(CancellationToken cancellationToken) + { + _executingTask = _runtime.ExecuteAsync(cancellationToken); + await _runtime.WhenStarted; + await base.StartAsync(cancellationToken); + } + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - await _runtime.ExecuteAsync(stoppingToken).ConfigureAwait(false); - // Stop application if this is exited - _hostLifetime.StopApplication(); + if (_executingTask != null) + { + await _executingTask.ConfigureAwait(false); + } } } \ No newline at end of file From 722faa7ac4c851ca28646b90d1459426c919c50b Mon Sep 17 00:00:00 2001 From: Frank Bakker Date: Fri, 14 Oct 2022 23:26:36 +0200 Subject: [PATCH 2/8] Rafactor --- TestWeb/Program.cs | 7 +- dev/DebugHost/apps/HelloApp/HelloApp.cs | 21 +++-- .../NetDaemon.AppModel.Tests/AppModelTests.cs | 10 +- .../Config/ConfigTests.cs | 2 +- .../Helpers/TestHelpers.cs | 4 +- .../NetDaemon.AppModel/Common/IAppModel.cs | 4 +- .../Common/IAppModelContext.cs | 2 +- .../NetDaemon.AppModel/Internal/AppModel.cs | 21 ++--- .../Internal/AppModelContext.cs | 3 +- .../Internal/HomeAssistantRunner.cs | 2 +- .../Integration/TestRuntime.cs | 18 +++- .../Internal/NetDaemonRuntimeTests.cs | 18 ++-- .../Extensions/HostBuilderExtensions.cs | 4 +- .../NetDaemon.Runtime/Common/IRuntime.cs | 3 +- .../Internal/NetDaemonRuntime.cs | 94 +++++++++++-------- .../Internal/RuntimeService.cs | 17 ++-- 16 files changed, 128 insertions(+), 102 deletions(-) diff --git a/TestWeb/Program.cs b/TestWeb/Program.cs index 22ca99175..ff04a6c0c 100644 --- a/TestWeb/Program.cs +++ b/TestWeb/Program.cs @@ -1,4 +1,4 @@ -using NetDaemon.HassModel.Common; +using NetDaemon.HassModel; using NetDaemon.Runtime; var builder = WebApplication.CreateBuilder(args); @@ -11,4 +11,7 @@ app.MapGet("/On", (IHaContext ha) => ha.Entity("light.spots_woonkamer_rechts").CallService("turn_on")); app.MapGet("/State/{id}", (IHaContext ha, string id) => ha.Entity(id).EntityState); -app.Run(); \ No newline at end of file +app.MapGet("/Stop", () => app.StopAsync().Wait()); + +app.Run(); + diff --git a/dev/DebugHost/apps/HelloApp/HelloApp.cs b/dev/DebugHost/apps/HelloApp/HelloApp.cs index ccef477dc..2daa511cf 100644 --- a/dev/DebugHost/apps/HelloApp/HelloApp.cs +++ b/dev/DebugHost/apps/HelloApp/HelloApp.cs @@ -1,5 +1,6 @@ using System; using System.Reactive.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using NetDaemon.AppModel; using NetDaemon.HassModel; @@ -8,15 +9,21 @@ namespace Apps; [NetDaemonApp] // [Focus] -public class HelloApp +public class HelloApp : IAsyncDisposable { public HelloApp(IHaContext ha, ILogger logger) { - // .Where(n => n.EventType == "test_event") - ha?.Events.Where(n => n.EventType == "test_event").Subscribe( n => - { - logger.LogInformation("Hello testevent"); - }); - ha?.CallService("notify", "persistent_notification", data: new { message = "Notify me", title = "Hello world!" }); + // // .Where(n => n.EventType == "test_event") + // ha?.Events.Where(n => n.EventType == "test_event").Subscribe( n => + // { + // logger.LogInformation("Hello testevent"); + // }); + // ha?.CallService("notify", "persistent_notification", data: new { message = "Notify me", title = "Hello world!" }); + } + + public ValueTask DisposeAsync() + { + Console.WriteLine("disposed app"); + return ValueTask.CompletedTask; } } \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs index ac2724139..ad30aaa8d 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs @@ -66,7 +66,7 @@ public async Task TestGetApplicationsLocalWithDisabled() var fakeStateManager = (FakeAppStateManager?) builder.Services.GetService(); var appModel = builder.Services.GetService(); - var appModelContext = await appModel!.InitializeAsync(CancellationToken.None).ConfigureAwait(false); + var appModelContext = await appModel!.LoadNewApplicationContext(CancellationToken.None).ConfigureAwait(false); // ACT var apps = appModelContext.Applications; @@ -105,7 +105,7 @@ public async Task TestGetApplicationsLocalWithEnabled() .Build(); var appModel = builder.Services.GetService(); - var appModelContext = await appModel!.InitializeAsync(CancellationToken.None).ConfigureAwait(false); + var appModelContext = await appModel!.LoadNewApplicationContext(CancellationToken.None).ConfigureAwait(false); // ACT var apps = appModelContext.Applications; @@ -176,7 +176,7 @@ public async Task TestGetApplicationsShouldReturnNonErrorOnes() // ACT var loadApps = (await appModel! - .InitializeAsync(CancellationToken.None)).Applications; + .LoadNewApplicationContext(CancellationToken.None)).Applications; // CHECK @@ -266,7 +266,7 @@ public async Task TestFocusShouldAlwaysLoadAppIfIndependentOfStateManager(Applic .Build(); var appModel = builder.Services.GetService(); - var appModelContext = await appModel!.InitializeAsync(CancellationToken.None).ConfigureAwait(false); + var appModelContext = await appModel!.LoadNewApplicationContext(CancellationToken.None).ConfigureAwait(false); // ACT var apps = appModelContext.Applications; @@ -300,7 +300,7 @@ public async Task TestInjectedClassShouldHaveCorrectValue() .Build(); var appModel = builder.Services.GetService(); - var appModelContext = await appModel!.InitializeAsync(CancellationToken.None).ConfigureAwait(false); + var appModelContext = await appModel!.LoadNewApplicationContext(CancellationToken.None).ConfigureAwait(false); // ACT var apps = appModelContext.Applications; diff --git a/src/AppModel/NetDaemon.AppModel.Tests/Config/ConfigTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/Config/ConfigTests.cs index d64f0be5c..ebee0a097 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/Config/ConfigTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/Config/ConfigTests.cs @@ -131,7 +131,7 @@ public async Task TestAddYamlConfigWithTypeConverterGetsSettingsCorrectly2() var appModel = scope.ServiceProvider.GetService(); // ACT - var loadApps = (await appModel!.InitializeAsync(CancellationToken.None)).Applications; + var loadApps = (await appModel!.LoadNewApplicationContext(CancellationToken.None)).Applications; var application = (Application)loadApps.First(n => n.Id == "LocalApps.MyAppLocalApp"); var app = (MyAppLocalApp?)application?.ApplicationContext?.Instance; // CHECK diff --git a/src/AppModel/NetDaemon.AppModel.Tests/Helpers/TestHelpers.cs b/src/AppModel/NetDaemon.AppModel.Tests/Helpers/TestHelpers.cs index 96f305e78..f384c5007 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/Helpers/TestHelpers.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/Helpers/TestHelpers.cs @@ -21,7 +21,7 @@ internal static async Task> GetLocalApplicatio }) .Build(); var appModel = builder.Services.GetService(); - var appModelContext = await appModel!.InitializeAsync(CancellationToken.None).ConfigureAwait(false); + var appModelContext = await appModel!.LoadNewApplicationContext(CancellationToken.None).ConfigureAwait(false); return appModelContext.Applications; } @@ -43,7 +43,7 @@ internal static async Task> GetDynamicApplicat }) .Build(); var appModel = builder.Services.GetService(); - var appModelContext = await appModel!.InitializeAsync(CancellationToken.None).ConfigureAwait(false); + var appModelContext = await appModel!.LoadNewApplicationContext(CancellationToken.None).ConfigureAwait(false); return appModelContext.Applications; } } \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Common/IAppModel.cs b/src/AppModel/NetDaemon.AppModel/Common/IAppModel.cs index 3b7f9d375..251f6cc35 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/IAppModel.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/IAppModel.cs @@ -3,7 +3,7 @@ namespace NetDaemon.AppModel; /// /// Application model /// -public interface IAppModel : IAsyncDisposable +public interface IAppModel { /// /// Instance and configure all applications. @@ -15,5 +15,5 @@ public interface IAppModel : IAsyncDisposable /// Depending on the selected compilation type it will be local or dynamically compiled apps /// /// - Task InitializeAsync(CancellationToken cancellationToken); + Task LoadNewApplicationContext(CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Common/IAppModelContext.cs b/src/AppModel/NetDaemon.AppModel/Common/IAppModelContext.cs index 1ea312375..da7b25007 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/IAppModelContext.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/IAppModelContext.cs @@ -3,7 +3,7 @@ namespace NetDaemon.AppModel; /// /// Manage AppModel state and lifecycle /// -public interface IAppModelContext : IAsyncDisposable +public interface IAppModelContext : IAsyncInitializable, IAsyncDisposable { /// /// Current instantiated and running applications diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppModel.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppModel.cs index b1852adf7..7a5433b9d 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppModel.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppModel.cs @@ -1,29 +1,22 @@ namespace NetDaemon.AppModel.Internal; +/// +/// This class serves as a factory for creating and initializing new ApplicationContexts +/// internal class AppModelImpl : IAppModel { private readonly IServiceProvider _provider; - public AppModelImpl( - IServiceProvider provider - ) + public AppModelImpl(IServiceProvider provider) { _provider = provider; } - internal IAppModelContext? CurrentContext { get; set; } - - public async ValueTask DisposeAsync() - { - if (CurrentContext is not null) - await CurrentContext.DisposeAsync().ConfigureAwait(false); - } - - public async Task InitializeAsync(CancellationToken cancellationToken) + public async Task LoadNewApplicationContext(CancellationToken cancellationToken) { + // Create a new AppModelContext var appModelContext = _provider.GetRequiredService(); - var initContext = (IAsyncInitializable)appModelContext; - await initContext.InitializeAsync(CancellationToken.None); + await appModelContext.InitializeAsync(cancellationToken); return appModelContext; } } \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs index f6fe4938d..de4d62cec 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs @@ -1,9 +1,8 @@ -using System.Reflection; using NetDaemon.AppModel.Internal.AppFactoryProviders; namespace NetDaemon.AppModel.Internal; -internal class AppModelContext : IAppModelContext, IAsyncInitializable +internal class AppModelContext : IAppModelContext { private readonly List _applications = new(); diff --git a/src/Client/NetDaemon.HassClient/Internal/HomeAssistantRunner.cs b/src/Client/NetDaemon.HassClient/Internal/HomeAssistantRunner.cs index 54264e694..854b1f02f 100644 --- a/src/Client/NetDaemon.HassClient/Internal/HomeAssistantRunner.cs +++ b/src/Client/NetDaemon.HassClient/Internal/HomeAssistantRunner.cs @@ -74,7 +74,7 @@ private async Task InternalRunAsync(string host, int port, bool ssl, string toke { if (isRetry) { - _logger.LogDebug("Client disconnected, retrying in {seconds} seconds...", timeout.TotalSeconds); + _logger.LogDebug("Client disconnected, retrying in {Seconds} seconds...", timeout.TotalSeconds); // This is a retry await Task.Delay(timeout, combinedToken.Token).ConfigureAwait(false); } diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs b/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs index 6c47269ff..2b25de4f7 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs @@ -33,7 +33,11 @@ public async Task TestApplicationIsLoaded() instances.Where(n => n.Id == "LocalApps.LocalApp").Should().NotBeEmpty(); timedCancellationSource.Cancel(); - await runnerTask.ConfigureAwait(false); + try + { + await runnerTask.ConfigureAwait(false); + } + catch (OperationCanceledException) { } } [Fact] @@ -71,7 +75,11 @@ public async Task TestApplicationReactToNewEvents() n => n.SendCommandAsync(It.IsAny(), It.IsAny()), Times.Once); timedCancellationSource.Cancel(); - await runnerTask.ConfigureAwait(false); + try + { + await runnerTask.ConfigureAwait(false); + } + catch (OperationCanceledException) { } } [Fact] @@ -105,7 +113,11 @@ public async Task TestApplicationReactToNewEventsAndThrowException() }); timedCancellationSource.Cancel(); - await runnerTask.ConfigureAwait(false); + try + { + await runnerTask.ConfigureAwait(false); + } + catch (OperationCanceledException) { } } private static IHostBuilder GetDefaultHostBuilder(string path) diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs b/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs index 5a833fd58..0e8ca72b1 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs @@ -29,10 +29,12 @@ public async Task TestExecuteAsync() Mock.Of() ); var cancelSource = new CancellationTokenSource(5000); - await runtime.ExecuteAsync(cancelSource.Token).ConfigureAwait(false); + var startingTask = runtime.StartAsync(cancelSource.Token); connectSubject.HasObservers.Should().BeTrue(); disconnectSubject.HasObservers.Should().BeTrue(); + + startingTask.IsCompleted.Should().BeFalse(); } [Fact] @@ -77,15 +79,16 @@ public async Task TestOnConnect() loggerMock.Object, Mock.Of() ); - await runtime.ExecuteAsync(cancelSource.Token).ConfigureAwait(false); + var startingTask = runtime.StartAsync(cancelSource.Token); + startingTask.IsCompleted.Should().BeFalse(); connectSubject.OnNext( homeAssistantConnectionMock.Object ); + await startingTask.ConfigureAwait(false); - await runtime.WhenStarted; - appModelMock.Verify(n => n.InitializeAsync(It.IsAny())); + appModelMock.Verify(n => n.LoadNewApplicationContext(It.IsAny())); } [Fact] @@ -130,21 +133,22 @@ public async Task TestOnDisconnect() loggerMock.Object, Mock.Of() ); - await runtime.ExecuteAsync(cancelSource.Token).ConfigureAwait(false); + var startingTask = runtime.StartAsync(cancelSource.Token); // First make sure we add an connection connectSubject.OnNext( homeAssistantConnectionMock.Object ); - Assert.NotNull(runtime.InternalConnection); + await startingTask.ConfigureAwait(false); + runtime.IsConnected.Should().BeTrue(); // Then fake a disconnect disconnectSubject.OnNext( DisconnectReason.Client ); - Assert.Null(runtime.InternalConnection); + runtime.IsConnected.Should().BeFalse(); } private class FakeHassSettingsOptions : IOptions diff --git a/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs b/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs index 4a0e8b410..8b3df68b7 100644 --- a/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs +++ b/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs @@ -12,8 +12,6 @@ public static IHostBuilder UseNetDaemonAppSettings(this IHostBuilder hostBuilder return hostBuilder .ConfigureServices((context, services) => services.ConfigureNetDaemonServices(context.Configuration) - // services.Configure(context.Configuration.GetSection("NetDaemon")); - ) .ConfigureAppConfiguration((ctx, config) => { @@ -35,7 +33,7 @@ public static IHostBuilder UseNetDaemonRuntime(this IHostBuilder hostBuilder) { return hostBuilder .UseAppScopedHaContext() - .ConfigureServices((_, services) => + .ConfigureServices((context, services) => { services.AddLogging(); services.AddHostedService(); diff --git a/src/Runtime/NetDaemon.Runtime/Common/IRuntime.cs b/src/Runtime/NetDaemon.Runtime/Common/IRuntime.cs index 6e2250b80..023eb35c9 100644 --- a/src/Runtime/NetDaemon.Runtime/Common/IRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime/Common/IRuntime.cs @@ -2,6 +2,5 @@ namespace NetDaemon.Runtime; public interface IRuntime : IAsyncDisposable { - Task ExecuteAsync(CancellationToken stoppingToken); - Task WhenStarted { get; } + Task StartAsync(CancellationToken stoppingToken); } \ No newline at end of file diff --git a/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs b/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs index be4c928f8..7d4c6a79e 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs @@ -8,7 +8,6 @@ internal class NetDaemonRuntime : IRuntime { private const string Version = "custom_compiled"; private const int TimeoutInSeconds = 30; - private readonly IAppModel? _appModel; private readonly ICacheManager _cacheManager; private readonly HomeAssistantSettings _haSettings; @@ -19,7 +18,9 @@ internal class NetDaemonRuntime : IRuntime private readonly IServiceProvider _serviceProvider; private IAppModelContext? _applicationModelContext; private CancellationToken? _stoppingToken; - internal IHomeAssistantConnection? InternalConnection; + private CancellationTokenSource? _runnerCancelationSource; + + public bool IsConnected; public NetDaemonRuntime( IHomeAssistantRunner homeAssistantRunner, @@ -32,7 +33,6 @@ public NetDaemonRuntime( _haSettings = settings.Value; _homeAssistantRunner = homeAssistantRunner; _locationSettings = locationSettings; - _appModel = serviceProvider.GetService(); _serviceProvider = serviceProvider; _logger = logger; _cacheManager = cacheManager; @@ -42,10 +42,11 @@ public NetDaemonRuntime( internal IReadOnlyCollection ApplicationInstances => _applicationModelContext?.Applications ?? Array.Empty(); - public Task WhenStarted => connected.Task; - private TaskCompletionSource connected = new(); - - public async Task ExecuteAsync(CancellationToken stoppingToken) + private readonly TaskCompletionSource _startedAndConnected = new(); + + private Task _runnerTask = Task.CompletedTask; + + public async Task StartAsync(CancellationToken stoppingToken) { _logger.LogInformation($"Starting NetDaemon runtime version {Version}."); @@ -59,59 +60,60 @@ public async Task ExecuteAsync(CancellationToken stoppingToken) .Subscribe(); try { - await _homeAssistantRunner.RunAsync( + _runnerCancelationSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); + + _runnerTask = _homeAssistantRunner.RunAsync( _haSettings.Host, _haSettings.Port, _haSettings.Ssl, _haSettings.Token, _haSettings.WebsocketPath, TimeSpan.FromSeconds(TimeoutInSeconds), - stoppingToken).ConfigureAwait(false); + _runnerCancelationSource.Token); + + await _startedAndConnected.Task; } catch (OperationCanceledException) { // Ignore and just stop } - - _logger.LogInformation("Exiting NetDaemon runtime"); - } - - public async ValueTask DisposeAsync() - { - await DisposeApplicationsAsync().ConfigureAwait(false); } private async Task OnHomeAssistantClientConnected( IHomeAssistantConnection haConnection, - CancellationToken cancelToken - ) + CancellationToken cancelToken) { - try - { - InternalConnection = haConnection; + _logger.LogInformation("Successfully connected to Home Assistant"); + IsConnected = true; - _logger.LogInformation("Successfully connected to Home Assistant"); - await _cacheManager.InitializeAsync(cancelToken).ConfigureAwait(false); + await _cacheManager.InitializeAsync(cancelToken).ConfigureAwait(false); - if (_appModel is not null) - { - if (!string.IsNullOrEmpty(_locationSettings.Value.ApplicationConfigurationFolder)) - _logger.LogDebug("Loading applications from folder {Path}", - Path.GetFullPath(_locationSettings.Value.ApplicationConfigurationFolder)); - else - _logger.LogDebug("Loading applications with no configuration folder"); - - _applicationModelContext = - await _appModel.InitializeAsync(CancellationToken.None).ConfigureAwait(false); + await LoadNewAppContextAsync(haConnection, cancelToken); + _startedAndConnected.SetResult(); + } - // Handle state change for apps if registered - var appStateHandler = _serviceProvider.GetService(); - if (appStateHandler != null) - await appStateHandler.InitializeAsync(haConnection, _applicationModelContext); - } - connected.SetResult(); + private async Task LoadNewAppContextAsync(IHomeAssistantConnection haConnection, CancellationToken cancelToken) + { + var appModel = _serviceProvider.GetService(); + if (appModel == null) return; + try + { + // this logging is a bit weird in this class + if (!string.IsNullOrEmpty(_locationSettings.Value.ApplicationConfigurationFolder)) + _logger.LogDebug("Loading applications from folder {Path}", + Path.GetFullPath(_locationSettings.Value.ApplicationConfigurationFolder)); + else + _logger.LogDebug("Loading applications with no configuration folder"); + + _applicationModelContext = await appModel.LoadNewApplicationContext(CancellationToken.None).ConfigureAwait(false); + + // Handle state change for apps if registered + var appStateHandler = _serviceProvider.GetService(); + if (appStateHandler == null) return; + + await appStateHandler.InitializeAsync(haConnection, _applicationModelContext); } catch (Exception e) { @@ -140,8 +142,8 @@ private async Task OnHomeAssistantClientDisconnected(DisconnectReason reason) reasonString, TimeoutInSeconds); } - if (InternalConnection is not null) InternalConnection = null; await DisposeApplicationsAsync().ConfigureAwait(false); + IsConnected = false; } private async Task DisposeApplicationsAsync() @@ -153,4 +155,16 @@ private async Task DisposeApplicationsAsync() _applicationModelContext = null; } } + + public async ValueTask DisposeAsync() + { + await DisposeApplicationsAsync().ConfigureAwait(false); + try + { + _runnerCancelationSource?.Cancel(); + } + catch (OperationCanceledException) { } + + await _runnerTask.ConfigureAwait(false); + } } \ No newline at end of file diff --git a/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs b/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs index 17edea34b..023ee6f08 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs @@ -10,19 +10,16 @@ public RuntimeService(IRuntime runtime) { _runtime = runtime; } - + public override async Task StartAsync(CancellationToken cancellationToken) { - _executingTask = _runtime.ExecuteAsync(cancellationToken); - await _runtime.WhenStarted; - await base.StartAsync(cancellationToken); - } - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - if (_executingTask != null) + try { - await _executingTask.ConfigureAwait(false); + await _runtime.StartAsync(cancellationToken); + await base.StartAsync(cancellationToken); } + catch (OperationCanceledException) { } } + + protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.CompletedTask; } \ No newline at end of file From 98c188b6f6f80846f34489053b6c0b1d44fb6c2f Mon Sep 17 00:00:00 2001 From: Frank Bakker Date: Sat, 15 Oct 2022 18:42:54 +0200 Subject: [PATCH 3/8] Refactor some + small fixes --- dev/DebugHost/Program.cs | 2 +- .../AssuredMqttConnection.cs | 2 +- .../Integration/TestRuntime.cs | 19 +++---------------- .../Extensions/HostBuilderExtensions.cs | 11 +++++++---- .../Extensions/ServiceCollectionExtensions.cs | 1 + .../Internal/NetDaemonRuntime.cs | 8 ++++---- 6 files changed, 17 insertions(+), 26 deletions(-) diff --git a/dev/DebugHost/Program.cs b/dev/DebugHost/Program.cs index e850a10e1..c038785e7 100644 --- a/dev/DebugHost/Program.cs +++ b/dev/DebugHost/Program.cs @@ -13,7 +13,7 @@ try { await Host.CreateDefaultBuilder(args) - .UseNetDaemonAppSettings() + //.UseNetDaemonAppSettings() .UseNetDaemonDefaultLogging() .UseNetDaemonRuntime() .UseNetDaemonTextToSpeech() diff --git a/src/Extensions/NetDaemon.Extensions.MqttEntityManager/AssuredMqttConnection.cs b/src/Extensions/NetDaemon.Extensions.MqttEntityManager/AssuredMqttConnection.cs index 7f914f1bc..66be73ea6 100644 --- a/src/Extensions/NetDaemon.Extensions.MqttEntityManager/AssuredMqttConnection.cs +++ b/src/Extensions/NetDaemon.Extensions.MqttEntityManager/AssuredMqttConnection.cs @@ -71,7 +71,7 @@ private async Task ConnectAsync(MqttConfiguration mqttConfig, IMqttFactoryWrappe private Task MqttClientOnDisconnectedAsync(MqttClientDisconnectedEventArgs arg) { - _logger.LogDebug("MQTT disconnected: {Reason}", arg.ConnectResult.ReasonString); + _logger.LogDebug("MQTT disconnected: {Reason}", arg.ConnectResult?.ReasonString); return Task.CompletedTask; } diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs b/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs index 2b25de4f7..c049a24b0 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs @@ -24,7 +24,6 @@ public async Task TestApplicationIsLoaded() .AddAppsFromAssembly(Assembly.GetExecutingAssembly()) ).Build(); - var runnerTask = host.RunAsync(timedCancellationSource.Token); haRunner.ConnectMock.OnNext(haRunner.ClientMock.ConnectionMock.Object); @@ -33,11 +32,7 @@ public async Task TestApplicationIsLoaded() instances.Where(n => n.Id == "LocalApps.LocalApp").Should().NotBeEmpty(); timedCancellationSource.Cancel(); - try - { - await runnerTask.ConfigureAwait(false); - } - catch (OperationCanceledException) { } + await runnerTask.ConfigureAwait(false); } [Fact] @@ -75,11 +70,7 @@ public async Task TestApplicationReactToNewEvents() n => n.SendCommandAsync(It.IsAny(), It.IsAny()), Times.Once); timedCancellationSource.Cancel(); - try - { - await runnerTask.ConfigureAwait(false); - } - catch (OperationCanceledException) { } + await runnerTask.ConfigureAwait(false); } [Fact] @@ -113,11 +104,7 @@ public async Task TestApplicationReactToNewEventsAndThrowException() }); timedCancellationSource.Cancel(); - try - { - await runnerTask.ConfigureAwait(false); - } - catch (OperationCanceledException) { } + await runnerTask.ConfigureAwait(false); } private static IHostBuilder GetDefaultHostBuilder(string path) diff --git a/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs b/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs index 8b3df68b7..0895c6bcc 100644 --- a/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs +++ b/src/Runtime/NetDaemon.Runtime/Common/Extensions/HostBuilderExtensions.cs @@ -15,17 +15,20 @@ public static IHostBuilder UseNetDaemonAppSettings(this IHostBuilder hostBuilder ) .ConfigureAppConfiguration((ctx, config) => { + // TODO: Most of this seems to be what Host.CreateDefaultBuilder already does config.SetBasePath(Directory.GetCurrentDirectory()); config.AddJsonFile("appsettings.json"); config.AddJsonFile($"appsettings.{ctx.HostingEnvironment.EnvironmentName}.json", true); config.AddEnvironmentVariables(); + var c = config.Build(); var locationSetting = c.GetSection("NetDaemon").Get(); + if (locationSetting?.ApplicationConfigurationFolder is not null) + { + var fullPath = Path.GetFullPath(locationSetting.ApplicationConfigurationFolder); + config.AddYamlAppConfig(fullPath); + } - var fullPath = Path.GetFullPath(locationSetting.ApplicationConfigurationFolder); - - config.AddYamlAppConfig( - fullPath); }); } diff --git a/src/Runtime/NetDaemon.Runtime/Common/Extensions/ServiceCollectionExtensions.cs b/src/Runtime/NetDaemon.Runtime/Common/Extensions/ServiceCollectionExtensions.cs index 9753bed2c..fc4bdb6d1 100644 --- a/src/Runtime/NetDaemon.Runtime/Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/Runtime/NetDaemon.Runtime/Common/Extensions/ServiceCollectionExtensions.cs @@ -8,5 +8,6 @@ public static IServiceCollection ConfigureNetDaemonServices(this IServiceCollect { return services.Configure(config.GetSection("NetDaemon")) .Configure(config.GetSection("HomeAssistant")); + // todo: maybe remove 'HomeAssistant' section this here, is this method really needed? If we remove this we can inline the rest } } \ No newline at end of file diff --git a/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs b/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs index 7d4c6a79e..3cca42312 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs @@ -124,7 +124,7 @@ private async Task LoadNewAppContextAsync(IHomeAssistantConnection haConnection, private async Task OnHomeAssistantClientDisconnected(DisconnectReason reason) { - if (_stoppingToken?.IsCancellationRequested ?? false) + if (_stoppingToken?.IsCancellationRequested == true || reason == DisconnectReason.Client) { _logger.LogInformation("HassClient disconnected cause of user stopping"); } @@ -152,6 +152,7 @@ private async Task DisposeApplicationsAsync() { foreach (var applicationInstance in _applicationModelContext.Applications) await applicationInstance.DisposeAsync().ConfigureAwait(false); + _applicationModelContext = null; } } @@ -159,12 +160,11 @@ private async Task DisposeApplicationsAsync() public async ValueTask DisposeAsync() { await DisposeApplicationsAsync().ConfigureAwait(false); + _runnerCancelationSource?.Cancel(); try { - _runnerCancelationSource?.Cancel(); + await _runnerTask.ConfigureAwait(false); } catch (OperationCanceledException) { } - - await _runnerTask.ConfigureAwait(false); } } \ No newline at end of file From 930c4c8c3cc3e91fdf1d351326fd825b6eecbe9a Mon Sep 17 00:00:00 2001 From: Frank Bakker Date: Sun, 16 Oct 2022 21:20:44 +0200 Subject: [PATCH 4/8] fix shutdown + added some tests --- .../Helpers/HomeAssistantRunnerMock.cs | 8 +- .../Integration/TestRuntime.cs | 84 ++++++++++--------- .../Internal/NetDaemonRuntime.cs | 6 ++ .../Internal/RuntimeService.cs | 13 ++- 4 files changed, 66 insertions(+), 45 deletions(-) diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Helpers/HomeAssistantRunnerMock.cs b/src/Runtime/NetDaemon.Runtime.Tests/Helpers/HomeAssistantRunnerMock.cs index c37bc4084..9bb9f6644 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Helpers/HomeAssistantRunnerMock.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Helpers/HomeAssistantRunnerMock.cs @@ -8,7 +8,7 @@ internal class HomeAssistantRunnerMock : Mock public HomeAssistantClientMock ClientMock { get; } public Subject ConnectMock { get; } public Subject DisconnectMock { get; } - public HomeAssistantRunnerMock(CancellationToken cancelToken) + public HomeAssistantRunnerMock() { ConnectMock = new(); DisconnectMock = new(); @@ -25,12 +25,14 @@ public HomeAssistantRunnerMock(CancellationToken cancelToken) It.IsAny(), It.IsAny(), It.IsAny())).Returns( - async () => + async (string _, int _, bool _, string _, string _, TimeSpan _, CancellationToken ct) => { - await Task.Delay(-1, cancelToken); + await Task.Delay(-1, ct); } ); } + + public void MockConnect() => ConnectMock.OnNext(ClientMock.ConnectionMock.Object); } internal class HomeAssistantClientMock : Mock diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs b/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs index c049a24b0..c68c10ebb 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using NetDaemon.AppModel; -using NetDaemon.Infrastructure.ObservableHelpers; using NetDaemon.Runtime.Internal; using NetDaemon.Runtime.Tests.Helpers; @@ -15,9 +14,9 @@ public class TestRuntime public async Task TestApplicationIsLoaded() { var timedCancellationSource = new CancellationTokenSource(5000); - var haRunner = new HomeAssistantRunnerMock(timedCancellationSource.Token); + var haRunner = new HomeAssistantRunnerMock(); - var hostBuilder = GetDefaultHostBuilder("Fixtures"); + var hostBuilder = GetDefaultHostBuilder(); var host = hostBuilder.ConfigureServices((_, services) => services .AddSingleton(haRunner.Object) @@ -26,7 +25,7 @@ public async Task TestApplicationIsLoaded() var runnerTask = host.RunAsync(timedCancellationSource.Token); - haRunner.ConnectMock.OnNext(haRunner.ClientMock.ConnectionMock.Object); + haRunner.MockConnect(); var service = (NetDaemonRuntime) host.Services.GetService()!; var instances = service.ApplicationInstances; @@ -39,9 +38,9 @@ public async Task TestApplicationIsLoaded() public async Task TestApplicationReactToNewEvents() { var timedCancellationSource = new CancellationTokenSource(-1); - var haRunner = new HomeAssistantRunnerMock(timedCancellationSource.Token); + var haRunner = new HomeAssistantRunnerMock(); - var hostBuilder = GetDefaultHostBuilder("Fixtures"); + var hostBuilder = GetDefaultHostBuilder(); var host = hostBuilder.ConfigureServices((_, services) => services .AddSingleton(haRunner.Object) @@ -49,10 +48,10 @@ public async Task TestApplicationReactToNewEvents() .AddTransient>(_ => haRunner.ClientMock.ConnectionMock.HomeAssistantEventMock) ).Build(); - var runnerTask = host.RunAsync(timedCancellationSource.Token); - while (!haRunner.ConnectMock.HasObservers && !runnerTask.IsCompleted) await Task.Delay(10); - haRunner.ConnectMock.OnNext(haRunner.ClientMock.ConnectionMock.Object); + var runnerTask = host.StartAsync(timedCancellationSource.Token); + haRunner.MockConnect(); _ = (NetDaemonRuntime) host.Services.GetService()!; + await runnerTask.ConfigureAwait(false); haRunner.ClientMock.ConnectionMock.AddStateChangeEvent( new HassState @@ -65,30 +64,29 @@ public async Task TestApplicationReactToNewEvents() EntityId = "binary_sensor.mypir", State = "on" }); - + // stopping the host will also flush any event queues + await host.StopAsync(timedCancellationSource.Token).ConfigureAwait(false); + haRunner.ClientMock.ConnectionMock.Verify( - n => n.SendCommandAsync(It.IsAny(), + n => n.SendCommandAsync(It.IsAny(), It.IsAny()), Times.Once); - timedCancellationSource.Cancel(); - await runnerTask.ConfigureAwait(false); } [Fact] public async Task TestApplicationReactToNewEventsAndThrowException() { var timedCancellationSource = new CancellationTokenSource(5000); - var haRunner = new HomeAssistantRunnerMock(timedCancellationSource.Token); + var haRunner = new HomeAssistantRunnerMock(); - var hostBuilder = GetDefaultHostBuilder("Fixtures"); + var hostBuilder = GetDefaultHostBuilder(); var host = hostBuilder.ConfigureServices((_, services) => services .AddSingleton(haRunner.Object) .AddNetDaemonApp() ).Build(); - var runnerTask = host.RunAsync(timedCancellationSource.Token); - while (!haRunner.ConnectMock.HasObservers && !runnerTask.IsCompleted) await Task.Delay(10); - haRunner.ConnectMock.OnNext(haRunner.ClientMock.ConnectionMock.Object); + var runnerTask = host.StartAsync(timedCancellationSource.Token); + haRunner.MockConnect(); _ = (NetDaemonRuntime) host.Services.GetService()!; haRunner.ClientMock.ConnectionMock.AddStateChangeEvent( @@ -106,29 +104,37 @@ public async Task TestApplicationReactToNewEventsAndThrowException() timedCancellationSource.Cancel(); await runnerTask.ConfigureAwait(false); } - - private static IHostBuilder GetDefaultHostBuilder(string path) + + + [Fact] + public async Task TestShutdownHostShutDownApps() { - return Host.CreateDefaultBuilder() - .UseNetDaemonAppSettings() + var timedCancellationSource = new CancellationTokenSource(); + var haRunner = new HomeAssistantRunnerMock(); + + var disposableApp = new Mock(); + + var host = Host.CreateDefaultBuilder() .UseNetDaemonRuntime() .ConfigureServices((_, services) => - { - services.Configure(hostOptions => - { - hostOptions.BackgroundServiceExceptionBehavior = BackgroundServiceExceptionBehavior.Ignore; - }); - services.AddTransient>( - _ => new FakeOptions(Path.Combine(AppContext.BaseDirectory, path))); - services.AddScoped>(); - services.AddScoped>(s => - s.GetRequiredService>()); - }) - .ConfigureAppConfiguration((_, config) => - { - config.AddYamlAppConfig( - Path.Combine(AppContext.BaseDirectory, - path)); - }); + services + .AddSingleton(haRunner.Object) + .AddNetDaemonApp(_ => disposableApp.Object)) + .Build(); + var runnerTask = host.StartAsync(timedCancellationSource.Token); + + haRunner.MockConnect(); + await runnerTask.WaitAsync(timedCancellationSource.Token).ConfigureAwait(false);; + + await host.StopAsync(timedCancellationSource.Token).WaitAsync(timedCancellationSource.Token).ConfigureAwait(false); + host.Dispose(); + + disposableApp.Verify(m=>m.DisposeAsync(), Times.Once); + } + + private static IHostBuilder GetDefaultHostBuilder() + { + return Host.CreateDefaultBuilder() + .UseNetDaemonRuntime(); } } diff --git a/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs b/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs index 3cca42312..9c79cee4f 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs @@ -71,6 +71,7 @@ public async Task StartAsync(CancellationToken stoppingToken) TimeSpan.FromSeconds(TimeoutInSeconds), _runnerCancelationSource.Token); + // Make sure we only return after the connection is made and initialization is ready await _startedAndConnected.Task; } catch (OperationCanceledException) @@ -90,6 +91,7 @@ private async Task OnHomeAssistantClientConnected( await LoadNewAppContextAsync(haConnection, cancelToken); + // Now signal that StartAsync may return _startedAndConnected.SetResult(); } @@ -157,8 +159,12 @@ private async Task DisposeApplicationsAsync() } } + private bool _isDisposed; public async ValueTask DisposeAsync() { + if (_isDisposed) return; + _isDisposed = true; + await DisposeApplicationsAsync().ConfigureAwait(false); _runnerCancelationSource?.Cancel(); try diff --git a/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs b/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs index 023ee6f08..789cc17bf 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/RuntimeService.cs @@ -3,12 +3,12 @@ namespace NetDaemon.Runtime.Internal; internal class RuntimeService : BackgroundService { private readonly IRuntime _runtime; - - private Task? _executingTask; + private readonly ILogger _logger; - public RuntimeService(IRuntime runtime) + public RuntimeService(IRuntime runtime, ILogger logger) { _runtime = runtime; + _logger = logger; } public override async Task StartAsync(CancellationToken cancellationToken) @@ -22,4 +22,11 @@ public override async Task StartAsync(CancellationToken cancellationToken) } protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.CompletedTask; + + public override async Task StopAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("NetDaemon RuntimeService is stopping"); + await _runtime.DisposeAsync(); + await base.StopAsync(cancellationToken); + } } \ No newline at end of file From 79c91b45eca662a6bdb997c7036a99c39bd54f3e Mon Sep 17 00:00:00 2001 From: Frank Bakker Date: Sun, 16 Oct 2022 21:31:39 +0200 Subject: [PATCH 5/8] Fix warnings --- dev/DebugHost/apps/HelloApp/HelloApp.cs | 16 +++++++++------- .../Internal/SerilogConfiguratior.cs | 1 + .../Internal/NetDaemonRuntimeTests.cs | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/dev/DebugHost/apps/HelloApp/HelloApp.cs b/dev/DebugHost/apps/HelloApp/HelloApp.cs index 2daa511cf..7030d2bfb 100644 --- a/dev/DebugHost/apps/HelloApp/HelloApp.cs +++ b/dev/DebugHost/apps/HelloApp/HelloApp.cs @@ -9,21 +9,23 @@ namespace Apps; [NetDaemonApp] // [Focus] -public class HelloApp : IAsyncDisposable +public sealed class HelloApp : IAsyncDisposable { + private readonly ILogger _logger; + public HelloApp(IHaContext ha, ILogger logger) { - // // .Where(n => n.EventType == "test_event") - // ha?.Events.Where(n => n.EventType == "test_event").Subscribe( n => - // { - // logger.LogInformation("Hello testevent"); - // }); + _logger = logger; + ha?.Events.Where(n => n.EventType == "test_event").Subscribe( n => + { + logger.LogInformation("Hello testevent"); + }); // ha?.CallService("notify", "persistent_notification", data: new { message = "Notify me", title = "Hello world!" }); } public ValueTask DisposeAsync() { - Console.WriteLine("disposed app"); + _logger.LogInformation("disposed app"); return ValueTask.CompletedTask; } } \ No newline at end of file diff --git a/src/Extensions/NetDaemon.Extensions.Logging/Internal/SerilogConfiguratior.cs b/src/Extensions/NetDaemon.Extensions.Logging/Internal/SerilogConfiguratior.cs index e19840661..f8aa4e239 100644 --- a/src/Extensions/NetDaemon.Extensions.Logging/Internal/SerilogConfiguratior.cs +++ b/src/Extensions/NetDaemon.Extensions.Logging/Internal/SerilogConfiguratior.cs @@ -28,6 +28,7 @@ public static LoggerConfiguration Configure(LoggerConfiguration loggerConfigurat .MinimumLevel.Override("System.Net.Http.HttpClient", LogEventLevel.Warning) .Enrich.FromLogContext() .WriteTo.Console( + formatProvider: CultureInfo.InvariantCulture, theme: NetDaemonLoggingThemes.NetDaemonConsoleThemes.GetThemeByType(loggingConfiguration .ConsoleThemeType), applyThemeToRedirectedOutput: true, diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs b/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs index 0e8ca72b1..60b937d26 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Internal/NetDaemonRuntimeTests.cs @@ -10,7 +10,7 @@ namespace NetDaemon.Runtime.Tests.Internal; public class NetDaemonRuntimeTests { [Fact] - public async Task TestExecuteAsync() + public void TestExecuteAsync() { var homeAssistantRunnerMock = new Mock(); var serviceProviderMock = new Mock(); From 39c3dcae7eb2ab3c86e00f77d6a60f08f4f6d5d8 Mon Sep 17 00:00:00 2001 From: Frank Bakker Date: Sun, 16 Oct 2022 21:54:13 +0200 Subject: [PATCH 6/8] Fix Duplicate disposal of apps --- .../NetDaemon.AppModel/Internal/AppModelContext.cs | 11 +++++++---- .../NetDaemon.Runtime/Internal/NetDaemonRuntime.cs | 3 +-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs index de4d62cec..567723c7f 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs @@ -36,11 +36,14 @@ public async Task InitializeAsync(CancellationToken cancellationToken) public async ValueTask DisposeAsync() { - if (_isDisposed) - return; + if (_isDisposed) return; + _isDisposed = true; - foreach (var appInstance in _applications) await appInstance.DisposeAsync().ConfigureAwait(false); + foreach (var appInstance in _applications) + { + await appInstance.DisposeAsync().ConfigureAwait(false); + } + _applications.Clear(); - _isDisposed = true; } } \ No newline at end of file diff --git a/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs b/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs index 9c79cee4f..66dfcc1b3 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/NetDaemonRuntime.cs @@ -152,8 +152,7 @@ private async Task DisposeApplicationsAsync() { if (_applicationModelContext is not null) { - foreach (var applicationInstance in _applicationModelContext.Applications) - await applicationInstance.DisposeAsync().ConfigureAwait(false); + await _applicationModelContext.DisposeAsync(); _applicationModelContext = null; } From dd2c43f1a462b3d6210f479426bd373d54d72fce Mon Sep 17 00:00:00 2001 From: Frank Bakker Date: Sun, 16 Oct 2022 22:21:33 +0200 Subject: [PATCH 7/8] Move debug projects to correct folders --- NetDaemon.sln | 4 ++-- {dev => src/debug}/DebugHost/DebugHost.csproj | 0 {dev => src/debug}/DebugHost/Program.cs | 0 .../DebugHost/Properties/launchSettings.json | 0 .../DebugHost/_appsettings.Development.json | 0 .../DebugHost/apps/ConcurrencyTestApp.cs | 0 .../debug}/DebugHost/apps/Config/Config.yaml | 0 .../debug}/DebugHost/apps/Config/ConfigApp.cs | 0 .../apps/Extensions/MqttEntityManagerApp.cs | 0 .../Extensions/MttEntitySubscriptionApp.cs | 0 .../DebugHost/apps/HelloApp/HelloApp.cs | 0 .../debug}/DebugHost/apps/YamlApp/YamlApp.cs | 0 .../DebugHost/apps/YamlApp/YamlApp.yaml | 0 {dev => src/debug}/DebugHost/appsettings.json | 0 .../debug/DebugWebHost/DebugWebHost.csproj | 3 +++ .../debug/DebugWebHost}/Program.cs | 2 +- .../Properties/launchSettings.json | 0 .../_appsettings.Development.json | 20 +++++++++++++++++++ .../debug/DebugWebHost}/appsettings.json | 0 19 files changed, 26 insertions(+), 3 deletions(-) rename {dev => src/debug}/DebugHost/DebugHost.csproj (100%) rename {dev => src/debug}/DebugHost/Program.cs (100%) rename {dev => src/debug}/DebugHost/Properties/launchSettings.json (100%) rename {dev => src/debug}/DebugHost/_appsettings.Development.json (100%) rename {dev => src/debug}/DebugHost/apps/ConcurrencyTestApp.cs (100%) rename {dev => src/debug}/DebugHost/apps/Config/Config.yaml (100%) rename {dev => src/debug}/DebugHost/apps/Config/ConfigApp.cs (100%) rename {dev => src/debug}/DebugHost/apps/Extensions/MqttEntityManagerApp.cs (100%) rename {dev => src/debug}/DebugHost/apps/Extensions/MttEntitySubscriptionApp.cs (100%) rename {dev => src/debug}/DebugHost/apps/HelloApp/HelloApp.cs (100%) rename {dev => src/debug}/DebugHost/apps/YamlApp/YamlApp.cs (100%) rename {dev => src/debug}/DebugHost/apps/YamlApp/YamlApp.yaml (100%) rename {dev => src/debug}/DebugHost/appsettings.json (100%) rename TestWeb/TestWeb.csproj => src/debug/DebugWebHost/DebugWebHost.csproj (90%) rename {TestWeb => src/debug/DebugWebHost}/Program.cs (92%) rename {TestWeb => src/debug/DebugWebHost}/Properties/launchSettings.json (100%) create mode 100644 src/debug/DebugWebHost/_appsettings.Development.json rename {TestWeb => src/debug/DebugWebHost}/appsettings.json (100%) diff --git a/NetDaemon.sln b/NetDaemon.sln index 30bb0b8d5..65c5e2dad 100644 --- a/NetDaemon.sln +++ b/NetDaemon.sln @@ -49,7 +49,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetDaemon.Runtime.Tests", " EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Debug", "Debug", "{E15D4280-7FFC-4F8B-9B8C-CF9AF2BF838C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DebugHost", "dev\DebugHost\DebugHost.csproj", "{898966EA-F814-4B7B-9A3D-5E78C38174B2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DebugHost", "src\debug\DebugHost\DebugHost.csproj", "{898966EA-F814-4B7B-9A3D-5E78C38174B2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetDaemon.Extensions.Logging", "src\Extensions\NetDaemon.Extensions.Logging\NetDaemon.Extensions.Logging.csproj", "{00333EBA-DB52-4D56-ADF7-940FB533E530}" EndProject @@ -57,7 +57,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetDaemon.Extensions.Tts", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetDaemon.Extensions.MqttEntityManager", "src\Extensions\NetDaemon.Extensions.MqttEntityManager\NetDaemon.Extensions.MqttEntityManager.csproj", "{3EB8C461-C91E-4900-BFBD-0986CBBE87A6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestWeb", "TestWeb\TestWeb.csproj", "{AEBC7828-7C19-4A86-B6E2-58B5171347B1}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DebugWebHost", "src\debug\DebugWebHost\DebugWebHost.csproj", "{AEBC7828-7C19-4A86-B6E2-58B5171347B1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/dev/DebugHost/DebugHost.csproj b/src/debug/DebugHost/DebugHost.csproj similarity index 100% rename from dev/DebugHost/DebugHost.csproj rename to src/debug/DebugHost/DebugHost.csproj diff --git a/dev/DebugHost/Program.cs b/src/debug/DebugHost/Program.cs similarity index 100% rename from dev/DebugHost/Program.cs rename to src/debug/DebugHost/Program.cs diff --git a/dev/DebugHost/Properties/launchSettings.json b/src/debug/DebugHost/Properties/launchSettings.json similarity index 100% rename from dev/DebugHost/Properties/launchSettings.json rename to src/debug/DebugHost/Properties/launchSettings.json diff --git a/dev/DebugHost/_appsettings.Development.json b/src/debug/DebugHost/_appsettings.Development.json similarity index 100% rename from dev/DebugHost/_appsettings.Development.json rename to src/debug/DebugHost/_appsettings.Development.json diff --git a/dev/DebugHost/apps/ConcurrencyTestApp.cs b/src/debug/DebugHost/apps/ConcurrencyTestApp.cs similarity index 100% rename from dev/DebugHost/apps/ConcurrencyTestApp.cs rename to src/debug/DebugHost/apps/ConcurrencyTestApp.cs diff --git a/dev/DebugHost/apps/Config/Config.yaml b/src/debug/DebugHost/apps/Config/Config.yaml similarity index 100% rename from dev/DebugHost/apps/Config/Config.yaml rename to src/debug/DebugHost/apps/Config/Config.yaml diff --git a/dev/DebugHost/apps/Config/ConfigApp.cs b/src/debug/DebugHost/apps/Config/ConfigApp.cs similarity index 100% rename from dev/DebugHost/apps/Config/ConfigApp.cs rename to src/debug/DebugHost/apps/Config/ConfigApp.cs diff --git a/dev/DebugHost/apps/Extensions/MqttEntityManagerApp.cs b/src/debug/DebugHost/apps/Extensions/MqttEntityManagerApp.cs similarity index 100% rename from dev/DebugHost/apps/Extensions/MqttEntityManagerApp.cs rename to src/debug/DebugHost/apps/Extensions/MqttEntityManagerApp.cs diff --git a/dev/DebugHost/apps/Extensions/MttEntitySubscriptionApp.cs b/src/debug/DebugHost/apps/Extensions/MttEntitySubscriptionApp.cs similarity index 100% rename from dev/DebugHost/apps/Extensions/MttEntitySubscriptionApp.cs rename to src/debug/DebugHost/apps/Extensions/MttEntitySubscriptionApp.cs diff --git a/dev/DebugHost/apps/HelloApp/HelloApp.cs b/src/debug/DebugHost/apps/HelloApp/HelloApp.cs similarity index 100% rename from dev/DebugHost/apps/HelloApp/HelloApp.cs rename to src/debug/DebugHost/apps/HelloApp/HelloApp.cs diff --git a/dev/DebugHost/apps/YamlApp/YamlApp.cs b/src/debug/DebugHost/apps/YamlApp/YamlApp.cs similarity index 100% rename from dev/DebugHost/apps/YamlApp/YamlApp.cs rename to src/debug/DebugHost/apps/YamlApp/YamlApp.cs diff --git a/dev/DebugHost/apps/YamlApp/YamlApp.yaml b/src/debug/DebugHost/apps/YamlApp/YamlApp.yaml similarity index 100% rename from dev/DebugHost/apps/YamlApp/YamlApp.yaml rename to src/debug/DebugHost/apps/YamlApp/YamlApp.yaml diff --git a/dev/DebugHost/appsettings.json b/src/debug/DebugHost/appsettings.json similarity index 100% rename from dev/DebugHost/appsettings.json rename to src/debug/DebugHost/appsettings.json diff --git a/TestWeb/TestWeb.csproj b/src/debug/DebugWebHost/DebugWebHost.csproj similarity index 90% rename from TestWeb/TestWeb.csproj rename to src/debug/DebugWebHost/DebugWebHost.csproj index c35082204..e2b5c0120 100644 --- a/TestWeb/TestWeb.csproj +++ b/src/debug/DebugWebHost/DebugWebHost.csproj @@ -13,5 +13,8 @@ + + + diff --git a/TestWeb/Program.cs b/src/debug/DebugWebHost/Program.cs similarity index 92% rename from TestWeb/Program.cs rename to src/debug/DebugWebHost/Program.cs index ff04a6c0c..1945e5a39 100644 --- a/TestWeb/Program.cs +++ b/src/debug/DebugWebHost/Program.cs @@ -11,7 +11,7 @@ app.MapGet("/On", (IHaContext ha) => ha.Entity("light.spots_woonkamer_rechts").CallService("turn_on")); app.MapGet("/State/{id}", (IHaContext ha, string id) => ha.Entity(id).EntityState); -app.MapGet("/Stop", () => app.StopAsync().Wait()); +app.MapGet("/Stop", () => app.StopAsync()); app.Run(); diff --git a/TestWeb/Properties/launchSettings.json b/src/debug/DebugWebHost/Properties/launchSettings.json similarity index 100% rename from TestWeb/Properties/launchSettings.json rename to src/debug/DebugWebHost/Properties/launchSettings.json diff --git a/src/debug/DebugWebHost/_appsettings.Development.json b/src/debug/DebugWebHost/_appsettings.Development.json new file mode 100644 index 000000000..10a6b2a87 --- /dev/null +++ b/src/debug/DebugWebHost/_appsettings.Development.json @@ -0,0 +1,20 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "Microsoft": "Warning" + }, + "ConsoleThemeType": "System" + }, + "HomeAssistant": { + "Host": "ENTER YOUR IP TO Development Home Assistant here", + "Port": 8124, + "Ssl": false, + "Token": "ENTER YOUR TOKEN", + "InsecureBypassCertificateErrors": false + }, + "NetDaemon": { + "Admin": false, + "ApplicationConfigurationFolder": "./apps" + } +} \ No newline at end of file diff --git a/TestWeb/appsettings.json b/src/debug/DebugWebHost/appsettings.json similarity index 100% rename from TestWeb/appsettings.json rename to src/debug/DebugWebHost/appsettings.json From d7a91876d2205a9eb9ec92ef43a111034cf34ba6 Mon Sep 17 00:00:00 2001 From: Frank Bakker Date: Sun, 16 Oct 2022 22:27:25 +0200 Subject: [PATCH 8/8] fix projectrefrences --- src/debug/DebugHost/DebugHost.csproj | 16 ++++++++-------- src/debug/DebugWebHost/DebugWebHost.csproj | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/debug/DebugHost/DebugHost.csproj b/src/debug/DebugHost/DebugHost.csproj index 1b6028c4e..45016b7f9 100644 --- a/src/debug/DebugHost/DebugHost.csproj +++ b/src/debug/DebugHost/DebugHost.csproj @@ -27,17 +27,17 @@ - - - - - - - + + + + + + + - ..\..\.linting\roslynator.ruleset + ..\..\..\.linting\roslynator.ruleset true AllEnabledByDefault diff --git a/src/debug/DebugWebHost/DebugWebHost.csproj b/src/debug/DebugWebHost/DebugWebHost.csproj index e2b5c0120..3fef87e76 100644 --- a/src/debug/DebugWebHost/DebugWebHost.csproj +++ b/src/debug/DebugWebHost/DebugWebHost.csproj @@ -6,11 +6,11 @@ enable - - - - - + + + + +