Skip to content

Commit

Permalink
Removes HassClient from HassModel (#599)
Browse files Browse the repository at this point in the history
* Removes HassClient from HassModel

* Fix warnings
  • Loading branch information
FrankBakkerNl committed Jan 13, 2022
1 parent bcd94cb commit 9d6a4e0
Show file tree
Hide file tree
Showing 47 changed files with 622 additions and 1,008 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace NetDaemon.HassModel.CodeGenerator;
using NetDaemon.Client.Common.HomeAssistant.Model;

namespace NetDaemon.HassModel.CodeGenerator;

internal static class EntitiesGenerator
{
Expand Down
4 changes: 3 additions & 1 deletion src/HassModel/NetDaemon.HassModel.CodeGenerator/EntitySet.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace NetDaemon.HassModel.CodeGenerator;
using NetDaemon.Client.Common.HomeAssistant.Model;

namespace NetDaemon.HassModel.CodeGenerator;

internal record EntitySet(string Domain, bool IsNumeric, IEnumerable<HassState> EntityStates)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
namespace NetDaemon.HassModel.CodeGenerator;
using NetDaemon.Client.Common.HomeAssistant.Model;

public static class ExtensionMethodsGenerator
namespace NetDaemon.HassModel.CodeGenerator;

internal static class ExtensionMethodsGenerator
{
public static IEnumerable<MemberDeclarationSyntax> Generate(IEnumerable<HassServiceDomain> serviceDomains, IReadOnlyCollection<HassState> entities)
{
Expand Down
3 changes: 2 additions & 1 deletion src/HassModel/NetDaemon.HassModel.CodeGenerator/Generator.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System.Runtime.CompilerServices;
using NetDaemon.Client.Common.HomeAssistant.Model;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

[assembly: InternalsVisibleTo("NetDaemon.Daemon.Tests")]

namespace NetDaemon.HassModel.CodeGenerator;

public static class Generator
internal static class Generator
{
public static string GenerateCode(string nameSpace, IReadOnlyCollection<HassState> entities, IReadOnlyCollection<HassServiceDomain> services)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
global using System.Collections.Generic;
global using System.Linq;
global using System.Text.Json;
global using JoySoftware.HomeAssistant.Model;
global using Microsoft.CodeAnalysis.CSharp.Syntax;
global using NetDaemon.HassModel.CodeGenerator;
global using NetDaemon.HassModel.CodeGenerator.Model;
global using NetDaemon.HassModel.CodeGenerator.Helpers;
global using NetDaemon.HassModel.CodeGenerator.Extensions;
global using NetDaemon.HassModel.Common;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace NetDaemon.HassModel.CodeGenerator.Model;

internal record HassService
{
public string? Service { get; init; }
public string? Description { get; init; }
public IReadOnlyCollection<HassServiceField>? Fields { get; init; }
public TargetSelector? Target { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace NetDaemon.HassModel.CodeGenerator.Model;

internal record HassServiceDomain
{
public string? Domain { get; init; }
public IReadOnlyCollection<HassService>? Services { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace NetDaemon.HassModel.CodeGenerator.Model;

internal record HassServiceField
{
public string? Field { get; init; }
public string? Description { get; init; }
public bool? Required { get; set; }
public object? Example { get; init; }
public object? Selector { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
using System.Buffers;
using System.Reflection;

namespace NetDaemon.HassModel.CodeGenerator.Model;

internal static class JsonExtensions
{
private static readonly JsonSerializerOptions SnakeCaseNamingPolicySerializerOptions = new()
{
PropertyNamingPolicy = SnakeCaseNamingPolicy.Instance
};

/// <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> ToServicesResult(this JsonElement element)
{
var result = new List<HassServiceDomain>();
Type[] executingAssemblyTypes = Assembly.GetExecutingAssembly().GetTypes();

if (element.ValueKind != JsonValueKind.Object)
throw new InvalidOperationException("Not expected result from the GetServices result");

foreach (var property in element.EnumerateObject())
{
var serviceDomain = new HassServiceDomain
{
Domain = property.Name,
Services = getServices(property.Value)
};
result.Add(serviceDomain);
}

IReadOnlyCollection<HassService> getServices(JsonElement element)
{
var servicesList = new List<HassService>();
foreach (var serviceDomainProperty in element.EnumerateObject())
{
servicesList.Add(getServiceFields(serviceDomainProperty.Name, serviceDomainProperty.Value));
}
return servicesList;
}

HassService getServiceFields(string service, JsonElement element)
{
var serviceFields = new List<HassServiceField>();

string? serviceDescription = null;
TargetSelector? target = null;

foreach (var serviceProperty in element.EnumerateObject())
{
switch (serviceProperty.Name)
{
case "description":
serviceDescription = serviceProperty.Value.GetString();
break;
case "fields":
foreach (var fieldsProperty in serviceProperty.Value.EnumerateObject())
{
serviceFields.Add(getField(fieldsProperty.Name, fieldsProperty.Value));
}
break;
case "target":
target = getSelector(serviceProperty.Name, serviceProperty.Value) as TargetSelector;
break;
}
}
return new HassService
{
Service = service,
Fields = serviceFields,
Description = serviceDescription,
Target = target
};
}

object? getSelector(string selectorName, JsonElement element)
{
var selectorType = executingAssemblyTypes.FirstOrDefault(x => string.Equals($"{selectorName}Selector", x.Name, StringComparison.OrdinalIgnoreCase));

if (selectorType is null)
{
return null;
}

if (element.ValueKind == JsonValueKind.Object && !element.EnumerateObject().Any() ||
element.ValueKind != JsonValueKind.Object && element.GetString() is null)
{
return Activator.CreateInstance(selectorType);
}

var bufferWriter = new ArrayBufferWriter<byte>();
using (var writer = new Utf8JsonWriter(bufferWriter))
{
element.WriteTo(writer);
}

return JsonSerializer.Deserialize(bufferWriter.WrittenSpan,
selectorType,
SnakeCaseNamingPolicySerializerOptions);
}

HassServiceField getField(string fieldName, JsonElement element)
{
object? example = null;
string? fieldDescription = null;
bool? required = null;
object? selector = null;

foreach (var fieldProperty in element.EnumerateObject())
{
switch (fieldProperty.Name)
{
case "description":
fieldDescription = fieldProperty.Value.GetString();
break;
case "required":
required = fieldProperty.Value.GetBoolean();
break;
case "example":
switch (fieldProperty.Value.ValueKind)
{
case JsonValueKind.String:
example = fieldProperty.Value.GetString();
break;
case JsonValueKind.Number:
if (fieldProperty.Value.TryGetInt64(out long longVal))
example = longVal;
else
example = fieldProperty.Value.GetDouble();
break;
case JsonValueKind.Object:

example = fieldProperty.Value;
break;
case JsonValueKind.True:
example = true;
break;
case JsonValueKind.False:
example = false;
break;
case JsonValueKind.Array:
example = fieldProperty.Value;
break;
}
break;
case "selector":
var selectorProperty = fieldProperty.Value.EnumerateObject().First();
selector = getSelector(selectorProperty.Name, selectorProperty.Value);
break;
}
}
return new HassServiceField
{
Field = fieldName,
Example = example,
Description = fieldDescription,
Required = required,
Selector = selector
};
}

return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.ComponentModel;
using System.Text.Json.Serialization;

namespace NetDaemon.HassModel.CodeGenerator.Model;

internal class NullableEnumStringConverter<TEnum> : JsonConverter<TEnum>
{
private readonly bool _isNullable;
private readonly Type _enumType;

public NullableEnumStringConverter() {
_isNullable = Nullable.GetUnderlyingType(typeof(TEnum)) is not null;

_enumType = _isNullable ?
Nullable.GetUnderlyingType(typeof(TEnum))! :
typeof(TEnum);
}

public override TEnum? Read(ref Utf8JsonReader reader,
Type typeToConvert, JsonSerializerOptions options)
{
var value = reader.GetString();

if (_isNullable && string.IsNullOrEmpty(value))
return default;

if (string.IsNullOrEmpty(value))
{
throw new InvalidEnumArgumentException(
$"A value must be provided for non-nullable enum property of type \"{_enumType.FullName}\"");
}

if (Enum.TryParse(_enumType, value, true, out var result))
{
return (TEnum) result!;
}

return default;
}

public override void Write(Utf8JsonWriter writer,
TEnum value, JsonSerializerOptions options)
{
throw new NotSupportedException($"Serialization not supported for the enum \"{_enumType.Name}\"");
}
}
93 changes: 93 additions & 0 deletions src/HassModel/NetDaemon.HassModel.CodeGenerator/Model/Selectors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;

namespace NetDaemon.HassModel.CodeGenerator.Model;

internal record ActionSelector
{
}

internal record AddonSelector
{
}

internal record AreaSelector
{
public DeviceSelector? Device { get; init; }

public EntitySelector? Entity { get; init; }
}

internal record BooleanSelector
{
}

internal record DeviceSelector
{
public string? Integration { get; init; }

public string? Manufacturer { get; init; }

public string? Model { get; init; }

public EntitySelector? Entity { get; init; }
}

internal record EntitySelector
{
public string? Integration { get; init; }

public string? Domain { get; init; }

public string? DeviceClass { get; init; }
}

internal record NumberSelector
{
[Required]
public double Min { get; init; }

[Required]
public double Max { get; init; }

public float? Step { get; init; }

public string? UnitOfMeasurement { get; init; }

[JsonConverter(typeof(NullableEnumStringConverter<NumberSelectorMode?>))]
public NumberSelectorMode? Mode { get; init; }
}

internal enum NumberSelectorMode
{
Box,
Slider
}

internal record ObjectSelector
{
}

internal record SelectSelector
{
[Required]
public IReadOnlyCollection<string>? Options { get; init; }
}

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

public DeviceSelector? Device { get; init; }

public EntitySelector? Entity { get; init; }
}

internal record TextSelector
{
public bool? Multiline { get; init; }
}

internal record TimeSelector
{
}
Loading

0 comments on commit 9d6a4e0

Please sign in to comment.