diff --git a/src/Client/NetDaemon.HassClient.Tests/Json/HassDeviceConverterTests.cs b/src/Client/NetDaemon.HassClient.Tests/Json/HassDeviceConverterTests.cs index 22fe7676e..3d711d3b0 100644 --- a/src/Client/NetDaemon.HassClient.Tests/Json/HassDeviceConverterTests.cs +++ b/src/Client/NetDaemon.HassClient.Tests/Json/HassDeviceConverterTests.cs @@ -14,41 +14,58 @@ public class JsonConverterTests [Fact] public void TestConversionWithNumericValue() { - const string jsonDevice = @" + const string jsonDevice = """ { - ""config_entries"": [], - ""connections"": [], - ""manufacturer"": ""Google Inc."", - ""model"": 123123, - ""name"": 123, - ""sw_version"": null, - ""id"": ""42cdda32a2a3428e86c2e27699d79ead"", - ""via_device_id"": null, - ""area_id"": null, - ""name_by_user"": null + "config_entries": [], + "connections": [], + "manufacturer": 1, + "model": 123123, + "name": 123, + "sw_version": null, + "id": "42cdda32a2a3428e86c2e27699d79ead", + "via_device_id": null, + "area_id": null, + "name_by_user": null, + "labels": ["label1", 123] } - "; + """; var hassDevice = JsonSerializer.Deserialize(jsonDevice, _defaultSerializerOptions); hassDevice!.Model.Should().Be("123123"); + hassDevice!.Manufacturer.Should().Be("1"); + hassDevice!.Name.Should().Be("123"); + hassDevice!.Labels.Should().BeEquivalentTo(["label1", "123"]); + } + + [Fact] + public void TestConversionWithLabelsSingleValue() + { + const string jsonDevice = """ + { + "labels": 123 + } + """; + var hassDevice = JsonSerializer.Deserialize(jsonDevice, _defaultSerializerOptions); + + hassDevice!.Labels.Should().BeEquivalentTo(["123"]); } [Fact] public void TestConversionWithStringValue() { - const string jsonDevice = @" - { - ""config_entries"": [], - ""connections"": [], - ""manufacturer"": ""Google Inc."", - ""model"": ""Chromecast"", - ""name"": ""My TV"", - ""sw_version"": null, - ""id"": ""42cdda32a2a3428e86c2e27699d79ead"", - ""via_device_id"": null, - ""area_id"": null, - ""name_by_user"": null - } - "; + const string jsonDevice = """ + { + "config_entries": [], + "connections": [], + "manufacturer": "Google Inc.", + "model": "Chromecast", + "name": "My TV", + "sw_version": null, + "id": "42cdda32a2a3428e86c2e27699d79ead", + "via_device_id": null, + "area_id": null, + "name_by_user": null + } + """; var hassDevice = JsonSerializer.Deserialize(jsonDevice, _defaultSerializerOptions); hassDevice!.Model.Should().Be("Chromecast"); } diff --git a/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassArea.cs b/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassArea.cs index d362739d3..7e7e86216 100644 --- a/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassArea.cs +++ b/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassArea.cs @@ -2,12 +2,23 @@ public record HassArea { - [JsonPropertyName("name")] public string? Name { get; init; } - [JsonPropertyName("area_id")] public string? Id { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("name")] + public string? Name { get; init; } - [JsonPropertyName("labels")] public IReadOnlyList Labels { get; init; } = []; + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("area_id")] + public string? Id { get; init; } - [JsonPropertyName("floor_id")] public string? FloorId { get; init; } + [JsonConverter(typeof(EnsureArrayOfStringConverter))] + [JsonPropertyName("labels")] + public IReadOnlyList Labels { get; init; } = []; - [JsonPropertyName("icon")] public string? Icon { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("floor_id")] + public string? FloorId { get; init; } + + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("icon")] + public string? Icon { get; init; } } diff --git a/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassDevice.cs b/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassDevice.cs index d6ee94d94..fcebcb36e 100644 --- a/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassDevice.cs +++ b/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassDevice.cs @@ -2,32 +2,53 @@ public record HassDevice { - [JsonPropertyName("manufacturer")] public string? Manufacturer { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("manufacturer")] + public string? Manufacturer { get; init; } - [JsonPropertyName("model")] [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("model")] public string? Model { get; init; } - [JsonPropertyName("id")] public string? Id { get; init; } + [JsonPropertyName("id")] + [JsonConverter(typeof(EnsureStringConverter))] + public string? Id { get; init; } - [JsonPropertyName("area_id")] public string? AreaId { get; init; } + [JsonPropertyName("area_id")] [JsonConverter(typeof(EnsureStringConverter))] - [JsonPropertyName("name")] public string? Name { get; init; } + public string? AreaId { get; init; } - [JsonPropertyName("name_by_user")] public string? NameByUser { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("name")] + public string? Name { get; init; } - [JsonPropertyName("labels")] public IReadOnlyList Labels { get; init; } = []; + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("name_by_user")] + public string? NameByUser { get; init; } - #pragma warning disable CA1056 // It's ok for this URL to be a string - [JsonPropertyName("configuration_url")] public string? ConfigurationUrl { get; init; } - #pragma warning restore CA1056 + [JsonConverter(typeof(EnsureArrayOfStringConverter))] + [JsonPropertyName("labels")] + public IReadOnlyList Labels { get; init; } = []; + +#pragma warning disable CA1056 // It's ok for this URL to be a string [JsonConverter(typeof(EnsureStringConverter))] - [JsonPropertyName("hw_version")] public string? HardwareVersion { get; init; } + [JsonPropertyName("configuration_url")] + public string? ConfigurationUrl { get; init; } +#pragma warning restore CA1056 + [JsonConverter(typeof(EnsureStringConverter))] - [JsonPropertyName("sw_version")] public string? SoftwareVersion { get; init; } + [JsonPropertyName("hw_version")] + public string? HardwareVersion { get; init; } + + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("sw_version")] + public string? SoftwareVersion { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] - [JsonPropertyName("serial_number")] public string? SerialNumber { get; init; } + [JsonPropertyName("serial_number")] + public string? SerialNumber { get; init; } [JsonConverter(typeof(EnsureArrayOfArrayOfStringConverter))] - [JsonPropertyName("identifiers")] public IReadOnlyList> Identifiers { get; init; } = []; + [JsonPropertyName("identifiers")] + public IReadOnlyList> Identifiers { get; init; } = []; } diff --git a/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassEntity.cs b/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassEntity.cs index 4e923d379..dfc121027 100644 --- a/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassEntity.cs +++ b/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassEntity.cs @@ -2,20 +2,34 @@ public record HassEntity { - [JsonPropertyName("device_id")] public string? DeviceId { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("device_id")] + public string? DeviceId { get; init; } - [JsonPropertyName("entity_id")] public string? EntityId { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("entity_id")] + public string? EntityId { get; init; } - [JsonPropertyName("area_id")] public string? AreaId { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("area_id")] + public string? AreaId { get; init; } - [JsonPropertyName("name")] public string? Name { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("name")] + public string? Name { get; init; } - [JsonPropertyName("icon")] public string? Icon { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("icon")] + public string? Icon { get; init; } - [JsonPropertyName("platform")] public string? Platform { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("platform")] + public string? Platform { get; init; } - [JsonPropertyName("labels")] public IReadOnlyList Labels { get; init; } = []; - - [JsonPropertyName("options")]public HassEntityOptions? Options { get; init; } -} + [JsonConverter(typeof(EnsureArrayOfStringConverter))] + [JsonPropertyName("labels")] + public IReadOnlyList Labels { get; init; } = []; + [JsonPropertyName("options")] + public HassEntityOptions? Options { get; init; } +} diff --git a/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassFloor.cs b/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassFloor.cs index 457276f91..a566b571b 100644 --- a/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassFloor.cs +++ b/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassFloor.cs @@ -2,11 +2,18 @@ public record HassFloor { - [JsonPropertyName("level")] public short? Level { get; init; } + [JsonPropertyName("level")] + public short? Level { get; init; } - [JsonPropertyName("icon")] public string? Icon { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("icon")] + public string? Icon { get; init; } - [JsonPropertyName("floor_id")] public string? Id { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("floor_id")] + public string? Id { get; init; } - [JsonPropertyName("name")] public string? Name { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("name")] + public string? Name { get; init; } } diff --git a/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassLabel.cs b/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassLabel.cs index 2cfc8c13a..d567fe4a7 100644 --- a/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassLabel.cs +++ b/src/Client/NetDaemon.HassClient/Common/HomeAssistant/Model/HassLabel.cs @@ -2,13 +2,23 @@ namespace NetDaemon.Client.HomeAssistant.Model; public record HassLabel { - [JsonPropertyName("color")] public string? Color { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("color")] + public string? Color { get; init; } - [JsonPropertyName("description")] public string? Description { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("description")] + public string? Description { get; init; } - [JsonPropertyName("icon")] public string? Icon { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("icon")] + public string? Icon { get; init; } - [JsonPropertyName("label_id")] public string? Id { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("label_id")] + public string? Id { get; init; } - [JsonPropertyName("name")] public string? Name { get; init; } + [JsonConverter(typeof(EnsureStringConverter))] + [JsonPropertyName("name")] + public string? Name { get; init; } } diff --git a/src/Client/NetDaemon.HassClient/Internal/Json/EnsureArrayOfStringConverter.cs b/src/Client/NetDaemon.HassClient/Internal/Json/EnsureArrayOfStringConverter.cs new file mode 100644 index 000000000..b7b3415c0 --- /dev/null +++ b/src/Client/NetDaemon.HassClient/Internal/Json/EnsureArrayOfStringConverter.cs @@ -0,0 +1,41 @@ +namespace NetDaemon.Client.Internal.Json; + +class EnsureArrayOfStringConverter : JsonConverter> +{ + private readonly EnsureStringConverter _ensureStringConverter = new(); + + public override IReadOnlyList Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.StartArray) + { + var list = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + string? item = _ensureStringConverter.Read(ref reader, typeof(string), options); + if (item != null) + { + list.Add(item); + } + } + return list; + } + + string? singleItem = _ensureStringConverter.Read(ref reader, typeof(string), options); + if (singleItem != null) + { + return new List { singleItem }; + } + + return []; + } + + public override void Write(Utf8JsonWriter writer, IReadOnlyList value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + foreach (var item in value) + { + writer.WriteStringValue(item); + } + writer.WriteEndArray(); + } +}