diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateManagerTests.cs b/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateManagerTests.cs index db49431f9..0c08c5883 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateManagerTests.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Internal/AppStateManagerTests.cs @@ -377,4 +377,21 @@ public void TestAppOneStateIsNullShouldNotCallSetStateAsync() // ASSERT appMock.Verify(n => n.SetStateAsync(ApplicationState.Disabled), Times.Never); } -} \ No newline at end of file + + [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 TestToSafeHomeAssistantEntityIdFromApplicationIdShouldGiveCorrectName(string fromId, string toId) + { + var expected = $"input_boolean.netdaemon_{toId}"; + AppStateManager.ToSafeHomeAssistantEntityIdFromApplicationId(fromId).Should().Be(expected); + } +} diff --git a/src/Runtime/NetDaemon.Runtime/Internal/AppStateManager.cs b/src/Runtime/NetDaemon.Runtime/Internal/AppStateManager.cs index 4d0739e23..ffcc0cee0 100644 --- a/src/Runtime/NetDaemon.Runtime/Internal/AppStateManager.cs +++ b/src/Runtime/NetDaemon.Runtime/Internal/AppStateManager.cs @@ -104,21 +104,36 @@ public static string ToSafeHomeAssistantEntityIdFromApplicationId(string applica var normalizedString = applicationId.Normalize(NormalizationForm.FormD); StringBuilder stringBuilder = new(applicationId.Length); + char lastChar = '\0'; + foreach (var c in normalizedString) + { switch (CharUnicodeInfo.GetUnicodeCategory(c)) { case UnicodeCategory.LowercaseLetter: + stringBuilder.Append(c); + break; case UnicodeCategory.UppercaseLetter: + if (CharUnicodeInfo.GetUnicodeCategory(lastChar) == UnicodeCategory.LowercaseLetter) + { + if (lastChar != '_') + stringBuilder.Append('_'); + } + stringBuilder.Append(char.ToLowerInvariant(c)); + break; case UnicodeCategory.DecimalDigitNumber: stringBuilder.Append(c); break; - case UnicodeCategory.SpaceSeparator: case UnicodeCategory.ConnectorPunctuation: case UnicodeCategory.DashPunctuation: - stringBuilder.Append('_'); + case UnicodeCategory.OtherPunctuation: + if (lastChar != '_') + stringBuilder.Append('_'); break; } + lastChar = c; + } return $"input_boolean.netdaemon_{stringBuilder.ToString().ToLowerInvariant()}"; } @@ -130,8 +145,8 @@ private void ClearExistingCacheOnNewConnection() private async Task GetOrCreateStateForApp(string entityId) { - var haConnection = _provider.GetRequiredService() ?? - throw new InvalidOperationException(); + var haConnection = _provider.GetRequiredService(); + try { var state = await haConnection.GetEntityStateAsync(entityId, _cancelTokenSource.Token)