Skip to content

Commit

Permalink
Fix CodeGen ServiceMetaDataParser, handle "step":"any" (#983)
Browse files Browse the repository at this point in the history
  • Loading branch information
FrankBakkerNl authored Nov 2, 2023
1 parent 80d8bf8 commit 22039b7
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace NetDaemon.HassModel.CodeGenerator.Model;
internal record Selector()
{
public bool Multiple { get; init; }

public string? Type { get; init; }
}

Expand Down Expand Up @@ -45,12 +45,18 @@ internal record NumberSelector : Selector
[Required]
public double Max { get; init; }

public float? Step { get; init; }

// Step can also contain the string "any" which is not usefull for our purpose, se we deserialize as a string and then try to parse as a double
[JsonPropertyName("step")]
public string? StepValue { get; init; }

[JsonIgnore]
public double? Step => double.TryParse(StepValue, out var d) ? d: null;

public string? UnitOfMeasurement { get; init; }
}

internal record TargetSelector : Selector
internal record TargetSelector : Selector
{
[JsonConverter(typeof(SingleObjectAsArrayConverter<EntitySelector>))]
public EntitySelector[] Entity { get; init; } = Array.Empty<EntitySelector>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ internal static class ServiceMetaDataParser
PropertyNamingPolicy = SnakeCaseNamingPolicy.Instance
};


public static IReadOnlyCollection<HassServiceDomain> Parse(JsonElement element) => Parse(element, out _);

/// <summary>
/// Parses all json elements to instance result from GetServices call
/// </summary>
/// <param name="element">JsonElement containing the result data</param>
public static IReadOnlyCollection<HassServiceDomain> Parse(JsonElement element)
/// <param name="errors">Outputs Any Exceptions during deserialization</param>
public static IReadOnlyCollection<HassServiceDomain> Parse(JsonElement element, out List<Exception> errors)
{
errors = new List<Exception>();
if (element.ValueKind != JsonValueKind.Object)
throw new InvalidOperationException("Not expected result from the GetServices result");

var hassServiceDomains = new List<HassServiceDomain>();
foreach (var property in element.EnumerateObject())
{
Expand All @@ -32,6 +37,7 @@ public static IReadOnlyCollection<HassServiceDomain> Parse(JsonElement element)
{
Console.Error.WriteLine($"JSON deserialization of {nameof(HassServiceDomain)} failed: {e.Message}");
Console.Error.WriteLine($"Deserialization source was: {property.Value}");
errors.Add(e);
}
}
return hassServiceDomains;
Expand All @@ -41,10 +47,10 @@ private static IReadOnlyCollection<HassService> GetServices(JsonElement element)
{
return element.EnumerateObject()
.Select(serviceDomainProperty =>
GetServiceFields(serviceDomainProperty.Name, serviceDomainProperty.Value)).ToList();
GetService(serviceDomainProperty.Name, serviceDomainProperty.Value)).ToList();
}

private static HassService GetServiceFields(string service, JsonElement element)
private static HassService GetService(string service, JsonElement element)
{
var result = element.Deserialize<HassService>(SnakeCaseNamingPolicySerializerOptions)! with
{
Expand All @@ -69,4 +75,4 @@ private static HassServiceField GetField(string fieldName, JsonElement element)
Field = fieldName,
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class ServiceMetaDataParserTest
[Fact]
public void TestSomeBasicServicesCanBeParsed()
{
var sample = """
var sample = """
{
"homeassistant": {
"save_persistent_states": {
Expand All @@ -36,13 +36,12 @@ public void TestSomeBasicServicesCanBeParsed()
}
}
""";
var element = JsonDocument.Parse(sample).RootElement;
var res = ServiceMetaDataParser.Parse(element);
var res = Parse(sample);
res.Should().HaveCount(1);
res.First().Domain.Should().Be("homeassistant");
res.First().Services.ElementAt(1).Target!.Entity.SelectMany(e=>e.Domain).Should().BeEmpty();
}

[Fact]
public void TestMultiDomainTarget()
{
Expand Down Expand Up @@ -73,11 +72,11 @@ public void TestMultiDomainTarget()
}
""";
var result = Parse(sample);

result.First().Services.First().Fields!.First().Selector.Should()
.BeAssignableTo<EntitySelector>().Which.Domain.Should().BeEquivalentTo("climate", "select");
}

[Fact]
public void TestMultiDomainTargetWithRequiredFieldAsString()
{
Expand Down Expand Up @@ -108,7 +107,7 @@ public void TestMultiDomainTargetWithRequiredFieldAsString()
}
""";
var result = Parse(sample);

result.First().Services.First().Fields!.First().Required.Should().BeTrue();
}

Expand All @@ -125,10 +124,10 @@ public void DeserializeTargetEntityArray()
"target":{
"entity":[
{
"domain":"targetdomain1"
"domain":"targetdomain1"
},
{
"domain":["targetdomain2", "targetdomain3"]
"domain":["targetdomain2", "targetdomain3"]
}

]
Expand All @@ -144,9 +143,74 @@ public void DeserializeTargetEntityArray()

}


[Fact]
public void NumericStepCanBeAny()
{
var sample = """
{
"homeassistant":
{
"set_location":{
"name":"Set location",
"description":"Updates the Home Assistant location.",
"fields":{
"latitude":{
"required":true,
"example":32.87336,
"selector":{
"number":{
"mode":"box",
"min":-90,
"max":90,
"step":"any"
}
},
"name":"Latitude",
"description":"Latitude of your location."
},
"longitude":{
"required":true,
"example":117.22743,
"selector":{
"number":{
"mode":"box",
"min":-180,
"max":180,
"step":"any"
}
},
"name":"Longitude",
"description":"Longitude of your location."
},
"elevation":{
"required":false,
"example":120,
"selector":{
"number":{
"mode":"box",
"step":"1"
}
},
"name":"Elevation",
"description":"Elevation of your location."
}
}
}
}
}
""";
var result = Parse(sample);

var steps = result.Single().Services.Single().Fields!.Select(f => (f.Selector as NumberSelector)!.Step).ToArray();
steps.Should().Equal(null, null, 1); // any is mapped to null
}

private static IReadOnlyCollection<HassServiceDomain> Parse(string sample)
{
var element = JsonDocument.Parse(sample).RootElement;
return ServiceMetaDataParser.Parse(element);
var element = JsonDocument.Parse(sample).RootElement;
var result = ServiceMetaDataParser.Parse(element, out var errors);
errors.Should().BeEmpty();
return result;
}
}
}
Loading

0 comments on commit 22039b7

Please sign in to comment.