Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public static IEnumerable<MemberDeclarationSyntax> Generate(IEnumerable<HassServ
private static IEnumerable<MemberDeclarationSyntax> GenerateExtensionMethodsForService(string domain, HassService service, ILookup<string, string> entityClassNameByDomain)
{
// There can be multiple Target Domains, so generate methods for each
var targetEntityDomains = service.Target?.Entity?.Domain ?? Array.Empty<string>();
var targetEntityDomains = service.Target?.Entity.SelectMany(e => e.Domain) ?? Array.Empty<string>();

return targetEntityDomains.SelectMany(targetEntityDomain => GenerateExtensionMethodsForService(domain, service, targetEntityDomain, entityClassNameByDomain));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,7 @@ internal record NumberSelector : Selector

internal record TargetSelector : Selector
{
public AreaSelector? Area { get; init; }

public DeviceSelector? Device { get; init; }

public EntitySelector? Entity { get; init; }
[JsonConverter(typeof(SingleObjectAsArrayConverter<EntitySelector>))]
public EntitySelector[] Entity { get; init; } = Array.Empty<EntitySelector>();
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Text.Json.Serialization;

namespace NetDaemon.HassModel.CodeGenerator.Model;

/// <summary>
/// Converts either a single object or an array to an array
/// </summary>
class SingleObjectAsArrayConverter<T> : JsonConverter<T[]>
{
public override T[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.StartObject)
{
return new [] { JsonSerializer.Deserialize<T>(ref reader, options)! };
}

return JsonSerializer.Deserialize<T[]>(ref reader, options);
}

public override void Write(Utf8JsonWriter writer, T[] value, JsonSerializerOptions options)
=> throw new NotSupportedException();
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ public void TestNumberExtensionMethodGeneration()
new() {
Service = "set_value",
Target = new TargetSelector {
Entity = new() { Domain = new [] {"number"} }
Entity = new[] { new EntitySelector { Domain = new[] { "number" } } }
},
Fields = new HassServiceField[] {
new() { Field = "value", Selector = new NumberSelector(), },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void TestSomeBasicServicesCanBeParsed()
var res = ServiceMetaDataParser.Parse(element);
res.Should().HaveCount(1);
res.First().Domain.Should().Be("homeassistant");
res.First().Services.ElementAt(1).Target!.Entity!.Domain.Should().BeEmpty();
res.First().Services.ElementAt(1).Target!.Entity.SelectMany(e=>e.Domain).Should().BeEmpty();
}

[Fact]
Expand Down Expand Up @@ -112,6 +112,38 @@ public void TestMultiDomainTargetWithRequiredFieldAsString()
result.First().Services.First().Fields!.First().Required.Should().BeTrue();
}

[Fact]
public void DeserializeTargetEntityArray()
{
var sample = """
{
"testdomain": {
"purge_entities":{
"name":"Purge Entities",
"fields":{
},
"target":{
"entity":[
{
"domain":"targetdomain1"
},
{
"domain":["targetdomain2", "targetdomain3"]
}

]
}
}
}
}
""";
var result = Parse(sample);
result.First().Services.First().Target!.Entity.Should().HaveCount(2);
result.First().Services.First().Target!.Entity[0].Domain.Should().Equal("targetdomain1");
result.First().Services.First().Target!.Entity[1].Domain.Should().Equal("targetdomain2", "targetdomain3");

}

private static IReadOnlyCollection<HassServiceDomain> Parse(string sample)
{
var element = JsonDocument.Parse(sample).RootElement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,22 @@ public void TestServicesGeneration()
Services = new HassService[] {
new() {
Service = "turn_off",
Target = new TargetSelector { Entity = new() { Domain = new [] {"light"} } }
Target = new TargetSelector
{
Entity = new[] { new EntitySelector { Domain = new[] { "light" } } }
}

},
new() {
Service = "turn_on",
Fields = new HassServiceField[] {
new() { Field = "transition", Selector = new NumberSelector(), },
new() { Field = "brightness", Selector = new NumberSelector { Step = 0.2f }, }
},
Target = new TargetSelector { Entity = new() { Domain = new [] {"light" } } }
Target = new TargetSelector
{
Entity = new[] { new EntitySelector { Domain = new[] { "light" } } }
}
}
}
}
Expand Down Expand Up @@ -82,12 +89,18 @@ public void TestServiceWithoutAnyTargetEntity_ExtensionMethodSkipped()
Domain = "smart_things",
Services = new HassService[] {
new() {
Service = "dig",
Target = new TargetSelector { Entity = new() { Domain = new [] {"humidifiers"} } },
Service = "dig",
Target = new TargetSelector
{
Entity = new[] { new EntitySelector { Domain = new[] { "humidifiers" } } }
},
},
new() {
Service = "orbit",
Target = new TargetSelector { Entity = new() { Domain = new [] {"orbiter" }} },
Target = new TargetSelector
{
Entity = new[] { new EntitySelector { Domain = new[] { "orbiter" } } }
},
}
},
}
Expand Down Expand Up @@ -134,7 +147,10 @@ public void TestServiceWithoutAnyMethods_ClassSkipped()
Services = new HassService[] {
new() {
Service = "push_button",
Target = new TargetSelector { Entity = new() { Domain = new [] {"uselessbox" }} },
Target = new TargetSelector
{
Entity = new[] { new EntitySelector { Domain = new[] { "uselessbox" } } }
},
},
},
}
Expand Down Expand Up @@ -178,7 +194,7 @@ public void TestServiceWithKeyWordFieldName_ParamEscaped()
new() {
Service = "set_value",
Target = new TargetSelector {
Entity = new() { Domain = new [] {"light"} }
Entity = new[] { new EntitySelector { Domain = new[] { "light" } } }
},
Fields = new HassServiceField[] {
new() { Field = "class", Selector = new NumberSelector(), },
Expand Down