diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateManagerTests.cs b/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateManagerTests.cs index d7dc82ee8..20a07844d 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateManagerTests.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateManagerTests.cs @@ -1,6 +1,7 @@ using System.Net; using System.Reactive.Subjects; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using NetDaemon.AppModel; using NetDaemon.Client.Internal.Exceptions; using NetDaemon.HassModel; @@ -21,6 +22,7 @@ public async Task TestGetStateAsyncReturnsCorrectStateEnabled() var provider = new ServiceCollection() .AddSingleton(haRunnerMock.Object) + .AddSingleton(new Mock().Object) .AddNetDaemonStateManager() .BuildServiceProvider(); using var scopedProvider = provider.CreateScope(); @@ -44,17 +46,9 @@ public async Task TestGetStateAsyncReturnsCorrectStateEnabled() public async Task TestGetStateAsyncReturnsCorrectStateDisabled() { // ARRANGE - var haConnectionMock = new Mock(); - var haRunnerMock = new Mock(); - haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); - - var provider = new ServiceCollection() - .AddSingleton(haRunnerMock.Object) - .AddNetDaemonStateManager() - .BuildServiceProvider(); - using var scopedProvider = provider.CreateScope(); + var (haConnectionMock, provider) = SetupProviderAndMocks(); - var appStateManager = scopedProvider.ServiceProvider.GetRequiredService(); + var appStateManager = provider.GetRequiredService(); // ACT // ASSERT @@ -73,16 +67,9 @@ public async Task TestGetStateAsyncReturnsCorrectStateDisabled() public async Task TestSaveStateAsyncReturnsCorrectStateDisabled() { // ARRANGE - var haConnectionMock = new Mock(); - var haRunnerMock = new Mock(); - haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); - - var provider = new ServiceCollection() - .AddSingleton(haRunnerMock.Object) - .AddNetDaemonStateManager() - .BuildServiceProvider(); + var (haConnectionMock, scopedProvider) = SetupProviderAndMocks(); - var appStateManager = provider.GetRequiredService(); + var appStateManager = scopedProvider.GetRequiredService(); haConnectionMock.Setup(n => n.GetApiCallAsync(It.IsAny(), It.IsAny())) .ReturnsAsync( new HassState @@ -106,14 +93,7 @@ public async Task TestSaveStateAsyncReturnsCorrectStateDisabled() public async Task TestGetStateAsyncNotExistReturnsCorrectStateEnabled() { // ARRANGE - var haConnectionMock = new Mock(); - var haRunnerMock = new Mock(); - haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); - - var provider = new ServiceCollection() - .AddSingleton(haRunnerMock.Object) - .AddNetDaemonStateManager() - .BuildServiceProvider(); + var (haConnectionMock, provider) = SetupProviderAndMocks(); var appStateManager = provider.GetRequiredService(); haConnectionMock.Setup(n => n.GetApiCallAsync(It.IsAny(), It.IsAny())) @@ -134,17 +114,34 @@ public async Task TestGetStateAsyncNotExistReturnsCorrectStateEnabled() } [Fact] - public async Task TestSetStateAsyncEnabled() + public async Task TestGetStateAsyncNotExistReturnsCorrectStateEnabledInDevelopment() { // ARRANGE - var haConnectionMock = new Mock(); - var haRunnerMock = new Mock(); - haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); + var (haConnectionMock, provider) = SetupProviderAndMocksDevelopment(); - var provider = new ServiceCollection() - .AddSingleton(haRunnerMock.Object) - .AddNetDaemonStateManager() - .BuildServiceProvider(); + var appStateManager = provider.GetRequiredService(); + haConnectionMock.Setup(n => n.GetApiCallAsync(It.IsAny(), It.IsAny())) + .ThrowsAsync( + new HomeAssistantApiCallException("ohh no", HttpStatusCode.NotFound)); + // ACT + _ = await appStateManager.GetStateAsync("helloapp"); + // ASSERT + haConnectionMock.Verify(n => + n.GetApiCallAsync("states/input_boolean.dev_netdaemon_helloapp", It.IsAny())); + // It exists so it should turn it on + haConnectionMock.Verify(n => + n.SendCommandAndReturnResponseAsync( + It.IsAny(), It.IsAny())); + haConnectionMock.Verify(n => + n.SendCommandAndReturnResponseAsync(It.IsAny(), + It.IsAny())); + } + + [Fact] + public async Task TestSetStateAsyncEnabled() + { + // ARRANGE + var (haConnectionMock, provider) = SetupProviderAndMocks(); var appStateManager = provider.GetRequiredService(); haConnectionMock.Setup(n => n.GetApiCallAsync(It.IsAny(), It.IsAny())) @@ -170,14 +167,7 @@ public async Task TestSetStateAsyncEnabled() public async Task TestSetStateAsyncRunning() { // ARRANGE - var haConnectionMock = new Mock(); - var haRunnerMock = new Mock(); - haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); - - var provider = new ServiceCollection() - .AddSingleton(haRunnerMock.Object) - .AddNetDaemonStateManager() - .BuildServiceProvider(); + var (haConnectionMock, provider) = SetupProviderAndMocks(); var appStateManager = provider.GetRequiredService(); haConnectionMock.Setup(n => n.GetApiCallAsync(It.IsAny(), It.IsAny())) @@ -200,17 +190,9 @@ public async Task TestSetStateAsyncRunning() public async Task TestSetStateAsyncError() { // ARRANGE - var haConnectionMock = new Mock(); - var haRunnerMock = new Mock(); - haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); + var (haConnectionMock, provider) = SetupProviderAndMocks(); - var provider = new ServiceCollection() - .AddSingleton(haRunnerMock.Object) - .AddNetDaemonStateManager() - .BuildServiceProvider(); - using var scopedProvider = provider.CreateScope(); - - var appStateManager = scopedProvider.ServiceProvider.GetRequiredService(); + var appStateManager = provider.GetRequiredService(); haConnectionMock.Setup(n => n.GetApiCallAsync(It.IsAny(), It.IsAny())) .ReturnsAsync( @@ -233,15 +215,22 @@ public async Task TestInitialize() // ARRANGE var haContextMock = new Mock(); var appModelContextMock = new Mock(); - appModelContextMock.SetupGet(n => n.Applications).Returns(new List()); + appModelContextMock.SetupGet(n => n.Applications) + .Returns(new List() {new Mock().Object}); + var haConnectionMock = new Mock(); + var haRunnerMock = new Mock(); haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); + var repositoryMock = new Mock(); + var provider = new ServiceCollection() .AddSingleton(haRunnerMock.Object) .AddScoped(_ => haContextMock.Object) + .AddSingleton(new Mock().Object) .AddNetDaemonStateManager() + .AddSingleton(repositoryMock.Object) .BuildServiceProvider(); using var scopedProvider = provider.CreateScope(); @@ -255,29 +244,58 @@ await homeAssistantStateUpdater.InitializeAsync(haConnectionMock.Object, appMode .ConfigureAwait(false); // ASSERT hassEvent.HasObservers.Should().BeTrue(); + repositoryMock.Verify(n => n.RemoveNotUsedStatesAsync(It.IsAny>(), It.IsAny()), Times.Once); } [Fact] - public async Task TestAppDisabledShouldCallSetStateAsyncEnabled() + public async Task InitializeShouldNeverTryDeleteUnusedInputBooleanHelpersWhenInDevelopmentEnvironment() { // ARRANGE var haContextMock = new Mock(); var appModelContextMock = new Mock(); - appModelContextMock.SetupGet(n => n.Applications).Returns(new List()); - var appMock = new Mock(); + // Make sure we have at least one application for this scenario + appModelContextMock.SetupGet(n => n.Applications) + .Returns(new List(){new Mock().Object}); var haConnectionMock = new Mock(); + haConnectionMock.SetupGet(n => n.OnHomeAssistantEvent).Returns(new Subject()); var haRunnerMock = new Mock(); haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); + var environmentMock = new Mock(); + environmentMock.Setup(n => n.EnvironmentName).Returns("Development"); + var repositoryMock = new Mock(); + var provider = new ServiceCollection() .AddSingleton(haRunnerMock.Object) .AddScoped(_ => haContextMock.Object) + .AddSingleton(environmentMock.Object) .AddNetDaemonStateManager() + .AddSingleton(repositoryMock.Object) .BuildServiceProvider(); using var scopedProvider = provider.CreateScope(); var homeAssistantStateUpdater = scopedProvider.ServiceProvider.GetRequiredService(); + + // ACT + await homeAssistantStateUpdater.InitializeAsync(haConnectionMock.Object, appModelContextMock.Object) + .ConfigureAwait(false); + // ASSERT + repositoryMock.Verify(n => n.RemoveNotUsedStatesAsync(It.IsAny>(), It.IsAny()), Times.Never); + + } + + [Fact] + public async Task TestAppDisabledShouldCallSetStateAsyncEnabled() + { + // ARRANGE + var (haConnectionMock, provider) = SetupProviderAndMocks(); + var appModelContextMock = new Mock(); + appModelContextMock.SetupGet(n => n.Applications).Returns(new List()); + var appMock = new Mock(); + + var homeAssistantStateUpdater = + provider.GetRequiredService(); Subject hassEvent = new(); haConnectionMock.SetupGet(n => n.OnHomeAssistantEvent).Returns(hassEvent); appMock.SetupGet(n => n.Id).Returns("app"); @@ -317,24 +335,18 @@ await homeAssistantStateUpdater.InitializeAsync(haConnectionMock.Object, appMode public async Task TestAppNoChangeShouldNotCallSetStateAsync() { // ARRANGE - var haContextMock = new Mock(); + var (haConnectionMock, provider) = SetupProviderAndMocks(); + + var homeAssistantStateUpdater = + provider.GetRequiredService(); + + Subject hassEvent = new(); + var appModelContextMock = new Mock(); appModelContextMock.SetupGet(n => n.Applications).Returns(new List()); - var appMock = new Mock(); - var haConnectionMock = new Mock(); - var haRunnerMock = new Mock(); - haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); - var provider = new ServiceCollection() - .AddSingleton(haRunnerMock.Object) - .AddScoped(_ => haContextMock.Object) - .AddNetDaemonStateManager() - .BuildServiceProvider(); - using var scopedProvider = provider.CreateScope(); + var appMock = new Mock(); - var homeAssistantStateUpdater = - scopedProvider.ServiceProvider.GetRequiredService(); - Subject hassEvent = new(); haConnectionMock.SetupGet(n => n.OnHomeAssistantEvent).Returns(hassEvent); appMock.SetupGet(n => n.Id).Returns("app"); appModelContextMock.SetupGet(n => n.Applications).Returns( @@ -374,23 +386,19 @@ await homeAssistantStateUpdater.InitializeAsync(haConnectionMock.Object, appMode public async Task TestAppOneStateIsNullShouldNotCallSetStateAsync() { // ARRANGE - var haContextMock = new Mock(); + var (haConnectionMock, provider) = SetupProviderAndMocks(); + var appModelContextMock = new Mock(); appModelContextMock.SetupGet(n => n.Applications).Returns(new List()); + var appMock = new Mock(); - var haConnectionMock = new Mock(); + var haRunnerMock = new Mock(); haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); - var provider = new ServiceCollection() - .AddSingleton(haRunnerMock.Object) - .AddScoped(_ => haContextMock.Object) - .AddNetDaemonStateManager() - .BuildServiceProvider(); - using var scopedProvider = provider.CreateScope(); var homeAssistantStateUpdater = - scopedProvider.ServiceProvider.GetRequiredService(); + provider.GetRequiredService(); Subject hassEvent = new(); haConnectionMock.SetupGet(n => n.OnHomeAssistantEvent).Returns(hassEvent); appMock.SetupGet(n => n.Id).Returns("app"); @@ -419,4 +427,37 @@ public async Task TestAppOneStateIsNullShouldNotCallSetStateAsync() // ASSERT appMock.Verify(n => n.SetStateAsync(ApplicationState.Disabled), Times.Never); } + + private (Mock connection, IServiceProvider serviceProvider) SetupProviderAndMocks() + { + var haConnectionMock = new Mock(); + var haRunnerMock = new Mock(); + haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); + + var provider = new ServiceCollection() + .AddSingleton(haRunnerMock.Object) + .AddSingleton(new Mock().Object) + .AddNetDaemonStateManager() + .BuildServiceProvider(); + var scopedProvider = provider.CreateScope(); + + return (haConnectionMock, scopedProvider.ServiceProvider); + } + + private (Mock connection, IServiceProvider serviceProvider) SetupProviderAndMocksDevelopment() + { + var haConnectionMock = new Mock(); + var haRunnerMock = new Mock(); + haRunnerMock.SetupGet(n => n.CurrentConnection).Returns(haConnectionMock.Object); + var environmentMock = new Mock(); + environmentMock.Setup(n => n.EnvironmentName).Returns("Development"); + var provider = new ServiceCollection() + .AddSingleton(haRunnerMock.Object) + .AddSingleton(environmentMock.Object) + .AddNetDaemonStateManager() + .BuildServiceProvider(); + var scopedProvider = provider.CreateScope(); + + return (haConnectionMock, scopedProvider.ServiceProvider); + } } diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateRepositoryTests.cs b/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateRepositoryTests.cs index 4cfb4a552..0a7ec8a4e 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateRepositoryTests.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateRepositoryTests.cs @@ -1,4 +1,5 @@ using System.Net; +using Microsoft.Extensions.Hosting; using NetDaemon.Client.Internal.Exceptions; using NetDaemon.Runtime.Internal; using NetDaemon.Runtime.Internal.Model; @@ -10,11 +11,13 @@ public class AppStateRepositoryTests private readonly Mock _connectionMock = new(); private readonly AppStateRepository _repository; private readonly Mock _runnerMock = new(); + private readonly Mock _hostEnvironment = new(); public AppStateRepositoryTests() { _runnerMock.SetupGet(x => x.CurrentConnection).Returns(_connectionMock.Object); - _repository = new AppStateRepository(_runnerMock.Object); + _hostEnvironment.Setup(n => n.EnvironmentName).Returns(""); + _repository = new AppStateRepository(_runnerMock.Object, _hostEnvironment.Object); } [Fact] @@ -89,7 +92,6 @@ public async Task RemoveNotUsedStatesAsyncShouldRemoveEntitiesThatDoesNotCorresp var applicationIds = new[] {"some_app_id"}; await _repository.RemoveNotUsedStatesAsync(applicationIds, CancellationToken.None); - // var command = new DeleteInputBooleanHelperCommand() {InputBooleanId = "some_app_id2", Type = "input_boolean/list""}; _connectionMock.Verify( n => n.SendCommandAndReturnResponseAsync( It.Is(n => n.InputBooleanId == "netdaemon_some_app_id2"), @@ -112,7 +114,6 @@ public async Task RemoveNotUsedStatesAsyncShouldRemoveAllHelpersIfNoAppsPresent( var applicationIds = new List(); await _repository.RemoveNotUsedStatesAsync(applicationIds, CancellationToken.None); - // var command = new DeleteInputBooleanHelperCommand() {InputBooleanId = "some_app_id2", Type = "input_boolean/list""}; _connectionMock.Verify( n => n.SendCommandAndReturnResponseAsync( It.Is(n => n.InputBooleanId == "netdaemon_some_app_id"), @@ -131,6 +132,7 @@ public async Task RemoveNotUsedStatesAsyncShouldNotRemoveNonNetDaemonInputBoolea { new InputBooleanHelper {Id = "netdaemon_some_app_id", Name = "netdaemon_some_app_id"}, new InputBooleanHelper {Id = "netdaemon_some_app_id2", Name = "netdaemon_some_app_id2"}, + new InputBooleanHelper {Id = "dev_netdaemon_some_app_id2", Name = "dev_netdaemon_some_app_id2"}, new InputBooleanHelper {Id = "non_netdaemon_input_boolean", Name = "non_netdaemon_input_boolean"} }; _connectionMock.Setup(n => @@ -141,10 +143,14 @@ public async Task RemoveNotUsedStatesAsyncShouldNotRemoveNonNetDaemonInputBoolea var applicationIds = new[] {"some_app_id"}; await _repository.RemoveNotUsedStatesAsync(applicationIds, CancellationToken.None); - // var command = new DeleteInputBooleanHelperCommand() {InputBooleanId = "some_app_id2", Type = "input_boolean/list""}; _connectionMock.Verify( n => n.SendCommandAndReturnResponseAsync( It.Is(n => n.InputBooleanId == "non_netdaemon_input_boolean"), It.IsAny()), Times.Never); + + _connectionMock.Verify( + n => n.SendCommandAndReturnResponseAsync( + It.IsAny(), + It.IsAny()), Times.Exactly(2)); } } diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Internal/EntityMapperHelperTests.cs b/src/Runtime/NetDaemon.Runtime.Tests/Internal/EntityMapperHelperTests.cs index b3387680e..5eca3c514 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Internal/EntityMapperHelperTests.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Internal/EntityMapperHelperTests.cs @@ -18,6 +18,23 @@ public class EntityMapperHelperTests public void TestToSafeHomeAssistantEntityIdFromApplicationIdShouldGiveCorrectName(string fromId, string toId) { var expected = $"input_boolean.netdaemon_{toId}"; - EntityMapperHelper.ToSafeHomeAssistantEntityIdFromApplicationId(fromId).Should().Be(expected); + EntityMapperHelper.ToEntityIdFromApplicationId(fromId).Should().Be(expected); + } + + [Theory] + [InlineData("lowercase", "lowercase")] + [InlineData("lower.namespace.lowercase", "lower_namespace_lowercase")] + [InlineData("Namespace.Class", "namespace_class")] + [InlineData("Namespace.ClassNameWithUpperAndLower", "namespace_class_name_with_upper_and_lower")] + [InlineData("ALLUPPERCASE", "alluppercase")] + [InlineData("DIClass", "diclass")] + [InlineData("DiClass", "di_class")] + [InlineData("Di_Class", "di_class")] + [InlineData("di_class", "di_class")] + [InlineData("di__class", "di_class")] + public void TestToSafeHomeAssistantEntityIdFromApplicationIdShouldGiveCorrectNameDevelopment(string fromId, string toId) + { + var expected = $"input_boolean.dev_netdaemon_{toId}"; + EntityMapperHelper.ToEntityIdFromApplicationId(fromId, true).Should().Be(expected); } } diff --git a/src/Runtime/NetDaemon.Runtime/Internal/AppStateManager.cs b/src/Runtime/NetDaemon.Runtime/Internal/AppStateManager.cs index 5da4b35c3..535cd9b65 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/AppStateManager.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/AppStateManager.cs @@ -7,20 +7,23 @@ namespace NetDaemon.Runtime.Internal; internal class AppStateManager : IAppStateManager, IHandleHomeAssistantAppStateUpdates, IDisposable { private readonly IAppStateRepository _appStateRepository; + private readonly IHostEnvironment _hostEnvironment; private readonly CancellationTokenSource _cancelTokenSource = new(); private readonly ConcurrentDictionary _stateCache = new(); public AppStateManager( - IAppStateRepository appStateRepository + IAppStateRepository appStateRepository, + IHostEnvironment hostEnvironment ) { _appStateRepository = appStateRepository; + _hostEnvironment = hostEnvironment; } public async Task InitializeAsync(IHomeAssistantConnection haConnection, IAppModelContext appContext) { _stateCache.Clear(); - if (appContext.Applications.Count > 0) + if (appContext.Applications.Count > 0 && !_hostEnvironment.IsDevelopment()) await _appStateRepository.RemoveNotUsedStatesAsync(appContext.Applications.Select(a => a.Id).ToList()!, _cancelTokenSource.Token); @@ -39,8 +42,8 @@ await _appStateRepository.RemoveNotUsedStatesAsync(appContext.Applications.Selec foreach (var app in appContext.Applications) { var entityId = - EntityMapperHelper.ToSafeHomeAssistantEntityIdFromApplicationId(app.Id ?? - throw new InvalidOperationException()); + EntityMapperHelper.ToEntityIdFromApplicationId(app.Id ?? + throw new InvalidOperationException(), _hostEnvironment.IsDevelopment()); if (entityId != changedEvent.NewState.EntityId) continue; var appState = changedEvent.NewState?.State == "on" diff --git a/src/Runtime/NetDaemon.Runtime/Internal/AppStateRepository.cs b/src/Runtime/NetDaemon.Runtime/Internal/AppStateRepository.cs index 10752023b..bd99e58d9 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/AppStateRepository.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/AppStateRepository.cs @@ -7,10 +7,12 @@ namespace NetDaemon.Runtime.Internal; internal class AppStateRepository : IAppStateRepository { private readonly IHomeAssistantRunner _runner; + private readonly IHostEnvironment _hostEnvironment; - public AppStateRepository(IHomeAssistantRunner runner) + public AppStateRepository(IHomeAssistantRunner runner, IHostEnvironment hostEnvironment) { _runner = runner; + _hostEnvironment = hostEnvironment; } public async Task GetOrCreateAsync(string applicationId, CancellationToken token) @@ -18,7 +20,7 @@ public async Task GetOrCreateAsync(string applicationId, CancellationToken var haConnection = _runner.CurrentConnection ?? throw new InvalidOperationException(); - var entityId = EntityMapperHelper.ToSafeHomeAssistantEntityIdFromApplicationId(applicationId); + var entityId = EntityMapperHelper.ToEntityIdFromApplicationId(applicationId, _hostEnvironment.IsDevelopment()); try { @@ -43,7 +45,7 @@ public async Task UpdateAsync(string applicationId, bool enabled, CancellationTo var haConnection = _runner.CurrentConnection ?? throw new InvalidOperationException(); - var entityId = EntityMapperHelper.ToSafeHomeAssistantEntityIdFromApplicationId(applicationId); + var entityId = EntityMapperHelper.ToEntityIdFromApplicationId(applicationId, _hostEnvironment.IsDevelopment()); await haConnection.CallServiceAsync("input_boolean", enabled ? "turn_on" : "turn_off", new HassTarget {EntityIds = new[] {entityId}}, @@ -56,11 +58,11 @@ public async Task RemoveNotUsedStatesAsync(IReadOnlyCollection applicati throw new InvalidOperationException(); var helpers = await haConnection.ListInputBooleanHelpersAsync(token).ConfigureAwait(false); - var entityIds = applicationIds.Select(EntityMapperHelper.ToSafeHomeAssistantEntityIdFromApplicationId) + var entityIds = applicationIds.Select(n => EntityMapperHelper.ToEntityIdFromApplicationId(n, _hostEnvironment.IsDevelopment())) .ToHashSet(); var notUsedHelperIds = helpers.Where(n => - !entityIds.Contains($"input_boolean.{n.Name}") && n.Id.StartsWith("netdaemon_")); + !entityIds.Contains($"input_boolean.{n.Name}") && (n.Id.StartsWith("netdaemon_")||n.Id.StartsWith("dev_netdaemon_"))); foreach (var helper in notUsedHelperIds) await haConnection.DeleteInputBooleanHelperAsync(helper.Id, token).ConfigureAwait(false); diff --git a/src/Runtime/NetDaemon.Runtime/Internal/EntityMapperHelper.cs b/src/Runtime/NetDaemon.Runtime/Internal/EntityMapperHelper.cs index e615b8d05..e73374f76 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/EntityMapperHelper.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/EntityMapperHelper.cs @@ -10,9 +10,14 @@ public static class EntityMapperHelper /// Converts any unicode string to a safe Home Assistant name for the helper /// /// The unicode string to convert + public static string ToEntityIdFromApplicationId(string applicationId, bool isDevelopment = false) => + !isDevelopment ? + $"input_boolean.netdaemon_{ToSafeVersion(applicationId)}" : + $"input_boolean.dev_netdaemon_{ToSafeVersion(applicationId)}" ; + [SuppressMessage("Microsoft.Globalization", "CA1308")] [SuppressMessage("", "CA1062")] - public static string ToSafeHomeAssistantEntityIdFromApplicationId(string applicationId) + private static string ToSafeVersion(string applicationId) { var normalizedString = applicationId.Normalize(NormalizationForm.FormD); StringBuilder stringBuilder = new(applicationId.Length); @@ -47,6 +52,6 @@ public static string ToSafeHomeAssistantEntityIdFromApplicationId(string applica lastChar = c; } - return $"input_boolean.netdaemon_{stringBuilder.ToString().ToLowerInvariant()}"; + return stringBuilder.ToString().ToLowerInvariant(); } }