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
30 changes: 30 additions & 0 deletions NetDaemon.sln
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DebugWebHost", "src\debug\D
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetDaemon.Tests.Integration", "tests\Integration\NetDaemon.Tests.Integration\NetDaemon.Tests.Integration.csproj", "{9B6A778A-A2D6-43A6-9FED-8D5B2BC18514}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyLibrary", "src\debug\MyLibrary\MyLibrary.csproj", "{CF7273C1-A4FE-4598-9C99-D746CE279A32}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyNDApp", "src\debug\MyNDApp\MyNDApp.csproj", "{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -307,6 +311,30 @@ Global
{9B6A778A-A2D6-43A6-9FED-8D5B2BC18514}.Release|x64.Build.0 = Release|Any CPU
{9B6A778A-A2D6-43A6-9FED-8D5B2BC18514}.Release|x86.ActiveCfg = Release|Any CPU
{9B6A778A-A2D6-43A6-9FED-8D5B2BC18514}.Release|x86.Build.0 = Release|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Debug|x64.ActiveCfg = Debug|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Debug|x64.Build.0 = Debug|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Debug|x86.ActiveCfg = Debug|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Debug|x86.Build.0 = Debug|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Release|Any CPU.Build.0 = Release|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Release|x64.ActiveCfg = Release|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Release|x64.Build.0 = Release|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Release|x86.ActiveCfg = Release|Any CPU
{CF7273C1-A4FE-4598-9C99-D746CE279A32}.Release|x86.Build.0 = Release|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Debug|x64.ActiveCfg = Debug|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Debug|x64.Build.0 = Debug|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Debug|x86.ActiveCfg = Debug|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Debug|x86.Build.0 = Debug|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Release|Any CPU.Build.0 = Release|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Release|x64.ActiveCfg = Release|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Release|x64.Build.0 = Release|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Release|x86.ActiveCfg = Release|Any CPU
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -331,6 +359,8 @@ Global
{F4B29B77-9B92-4037-A884-288CA5EF0B78} = {DFF3E7AA-7A50-4A1E-B3F8-EC01531FB83D}
{3EB8C461-C91E-4900-BFBD-0986CBBE87A6} = {DFF3E7AA-7A50-4A1E-B3F8-EC01531FB83D}
{AEBC7828-7C19-4A86-B6E2-58B5171347B1} = {E15D4280-7FFC-4F8B-9B8C-CF9AF2BF838C}
{CF7273C1-A4FE-4598-9C99-D746CE279A32} = {E15D4280-7FFC-4F8B-9B8C-CF9AF2BF838C}
{FEE1EA29-4609-4200-A92C-C61ECB7A3D0B} = {E15D4280-7FFC-4F8B-9B8C-CF9AF2BF838C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7C5FBB7F-654C-4CAC-964F-6D71AF3D62F8}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,12 @@ public static IEnumerable<MemberDeclarationSyntax> Generate(IReadOnlyCollection<
{
yield return GenerateEntiesForDomainClass(domainMetadata.Key, domainMetadata);
}

foreach (var domainMetadata in metaData)
{
yield return GenerateEntityType(domainMetadata);

yield return AttributeTypeGenerator.GenerateAttributeRecord(domainMetadata);
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ther should be one line between functions.

private static TypeDeclarationSyntax GenerateRootEntitiesInterface(IEnumerable<string> domains)
{
var autoProperties = domains.Select(domain =>
Expand Down Expand Up @@ -79,22 +76,27 @@ private static MemberDeclarationSyntax GenerateEntityProperty(EntityMetaData ent
/// </summary>
private static MemberDeclarationSyntax GenerateEntityType(EntityDomainMetadata domainMetaData)
{
string attributesGeneric = domainMetaData.AttributesClassName;
var attributesGeneric = domainMetaData.AttributesClassName;

var baseType = domainMetaData.IsNumeric ? typeof(NumericEntity) : typeof(Entity);
var entityStateType = domainMetaData.IsNumeric ? typeof(NumericEntityState) : typeof(EntityState);

var baseClass = $"{SimplifyTypeName(baseType)}<{domainMetaData.EntityClassName}, {SimplifyTypeName(entityStateType)}<{attributesGeneric}>, {attributesGeneric}>";

var coreinterface = domainMetaData.CoreInterfaceName;
if (coreinterface != null)
{
baseClass += $", {coreinterface}";
}

var (className, variableName) = GetNames<IHaContext>();
var classDeclaration = $$"""
record {{domainMetaData.EntityClassName}} : {{baseClass}}
{
public {{domainMetaData.EntityClassName}}({{className}} {{variableName}}, string entityId) : base({{variableName}}, entityId)
{}
public {{domainMetaData.EntityClassName}}({{className}} {{variableName}}, string entityId) : base({{variableName}}, entityId)
{}

public {{domainMetaData.EntityClassName}}({{SimplifyTypeName(typeof(Entity))}} entity) : base(entity)
{}
public {{domainMetaData.EntityClassName}}({{SimplifyTypeName(typeof(IEntityCore))}} entity) : base(entity)
{}
}
""";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal static class ExtensionMethodsGenerator
{
public static IEnumerable<MemberDeclarationSyntax> Generate(IEnumerable<HassServiceDomain> serviceDomains, IReadOnlyCollection<EntityDomainMetadata> entityDomains)
{
var entityClassNameByDomain = entityDomains.ToLookup(e => e.Domain, e => e.EntityClassName);
var entityClassNameByDomain = entityDomains.ToLookup(e => e.Domain, e => e.CoreInterfaceName ?? e.EntityClassName);

return serviceDomains
.Select(sd => GenerateDomainExtensionClass(sd, entityClassNameByDomain))
Expand Down Expand Up @@ -60,8 +60,9 @@ private static IEnumerable<MemberDeclarationSyntax> GenerateExtensionMethodsForS
private static IEnumerable<MemberDeclarationSyntax> GenerateExtensionMethodsForService(string domain, HassService service, string targetEntityDomain, ILookup<string, string> entityClassNameByDomain)
{
var entityTypeName = entityClassNameByDomain[targetEntityDomain].FirstOrDefault();

if (entityTypeName == null) yield break;

var serviceName = service.Service;
var serviceArguments = ServiceArguments.Create(domain, service);
var enumerableTargetTypeName = $"IEnumerable<{entityTypeName}>";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ private static ClassDeclarationSyntax GenerateServiceCollectionExtension(IReadOn
/// Generates the AddHomeAssistantGenerated method
/// </summary>
//
// public static IServiceCollection AddGeneratedCode(this IServiceCollection serviceCollection)
// {
// serviceCollection.AddTransient<Entities>();
// serviceCollection.AddTransient<AutomationEntities>();
// serviceCollection.AddTransient<BinarySensorEntities>();
// serviceCollection.AddTransient<Services>();
// serviceCollection.AddTransient<AlarmControlPanelServices>();
// return serviceCollection;
// }
// public static IServiceCollection AddGeneratedCode(this IServiceCollection serviceCollection)
// {
// serviceCollection.AddTransient<Entities>();
// serviceCollection.AddTransient<AutomationEntities>();
// serviceCollection.AddTransient<BinarySensorEntities>();
// serviceCollection.AddTransient<Services>();
// serviceCollection.AddTransient<AlarmControlPanelServices>();
// return serviceCollection;
// }
private static MethodDeclarationSyntax BuildAddHomeAssistantGenerated(IEnumerable<EntityDomainMetadata> domains, IEnumerable<HassServiceDomain> orderedServiceDomains)
{

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace NetDaemon.HassModel.CodeGenerator;

public record CodeGenerationSettings
record CodeGenerationSettings
{
public string OutputFile { get; init; } = "HomeAssistantGenerated.cs";
public string OutputFolder { get; init; } = "NetDaemonCodegen";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,30 @@ record EntityDomainMetadata(
IReadOnlyList<EntityAttributeMetaData> Attributes
)
{
private static readonly HashSet<string> CoreInterfaces =
typeof(IEntityCore).Assembly.GetTypes()
.Where(t => t.IsInterface && t.IsAssignableTo(typeof(IEntityCore)))
.Select(t => t.Name)
.ToHashSet();

private readonly string prefixedDomain = (IsNumeric && EntityIdHelper.MixedDomains.Contains(Domain) ? "numeric_" : "") + Domain;

[JsonIgnore]
public string EntityClassName => $"{prefixedDomain}Entity".ToValidCSharpPascalCase();

/// <summary>
/// Returns the name of the corresponding Core Interface if it exists, or null if it does not
/// </summary>
[JsonIgnore]
public string? CoreInterfaceName
{
get
{
var name = $"I{Domain.ToValidCSharpPascalCase()}EntityCore";
return CoreInterfaces.Contains(name) ? name : null;
}
}

[JsonIgnore]
public string AttributesClassName => $"{prefixedDomain}Attributes".ToValidCSharpPascalCase();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
</PackageDescription>
<PackageReleaseNotes>Please advice this is still in beta</PackageReleaseNotes>
<tags>Home Assistant</tags>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ public void Run(IHaContext ha)
LightEntity light2 = entities.Light.Light2;
SwitchEntity switch1 = entities.Switch.Switch1;
SwitchEntity switch2 = entities.Switch.Switch2;

// Now check if the entity classes implement the core interfaces
ILightEntityCore lightAsCore = entities.Light.Light1;
ISwitchEntityCore switch1AsCore = entities.Switch.Switch1;
}
}
""";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,17 @@ public void Run(IHaContext ha)

s.Light.TurnOff(new ServiceTarget());

var light = new RootNameSpace.LightEntity(ha, "light.testLight");

LightEntity light = new RootNameSpace.LightEntity(ha, "light.testLight");
light.TurnOn();
light.TurnOn(transition: 12, brightness: 324.5f);
light.TurnOn(new (){ Transition = 12L, Brightness = 12.3f });
light.TurnOff();

ILightEntityCore lightCore = light;
lightCore.TurnOn();
lightCore.TurnOn(transition: 12, brightness: 324.5f);
lightCore.TurnOn(new (){ Transition = 12L, Brightness = 12.3f });
lightCore.TurnOff();
}
}
""";
Expand Down Expand Up @@ -179,7 +184,6 @@ public void Run(Entities entities, Services services)
CodeGenTestHelper.AssertCodeCompiles(code.ToString(), appCode);
}


[Fact]
public void TestServiceWithKeyWordFieldName_ParamEscaped()
{
Expand Down
108 changes: 108 additions & 0 deletions src/HassModel/NetDeamon.HassModel/Entities/Core/CoreInterfaces.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
namespace NetDaemon.HassModel.Entities;

// These 'Core' interfaces serve as common types between 3rd party libraries and classes that are generated by the end
// user using nd-codegen

/// <summary> Core interface for automation entities </summary>
public interface IAutomationEntityCore: IEntityCore {}

/// <summary> Core interface for binary_sensor entities </summary>
public interface IBinarySensorEntityCore: IEntityCore {}

/// <summary> Core interface for button entities </summary>
public interface IButtonEntityCore: IEntityCore {}

/// <summary> Core interface for calendar entities </summary>
public interface ICalendarEntityCore: IEntityCore {}

/// <summary> Core interface for camera entities </summary>
public interface ICameraEntityCore: IEntityCore {}

/// <summary> Core interface for climate entities </summary>
public interface IClimateEntityCore: IEntityCore {}

/// <summary> Core interface for cover entities </summary>
public interface ICoverEntityCore: IEntityCore {}

/// <summary> Core interface for device_tracker entities </summary>
public interface IDeviceTrackerEntityCore: IEntityCore {}

/// <summary> Core interface for group entities </summary>
public interface IGroupEntityCore: IEntityCore {}

/// <summary> Core interface for input_boolean entities </summary>
public interface IInputBooleanEntityCore: IEntityCore {}

/// <summary> Core interface for input_button entities </summary>
public interface IInputButtonEntityCore: IEntityCore {}

/// <summary> Core interface for input_datetime entities </summary>
public interface IInputDatetimeEntityCore: IEntityCore {}

/// <summary> Core interface for input_number entities </summary>
public interface IInputNumberEntityCore: IEntityCore {}

/// <summary> Core interface for input_select entities </summary>
public interface IInputSelectEntityCore: IEntityCore {}

/// <summary> Core interface for input_text entities </summary>
public interface IInputTextEntityCore: IEntityCore {}

/// <summary> Core interface for light entities </summary>
public interface ILightEntityCore: IEntityCore {}

/// <summary> Core interface for lock entities </summary>
public interface ILockEntityCore: IEntityCore {}

/// <summary> Core interface for media_player entities </summary>
public interface IMediaPlayerEntityCore: IEntityCore {}

/// <summary> Core interface for number entities </summary>
public interface INumberEntityCore: IEntityCore {}

/// <summary> Core interface for person entities </summary>
public interface IPersonEntityCore: IEntityCore {}

/// <summary> Core interface for proximity entities </summary>
public interface IProximityEntityCore: IEntityCore {}

/// <summary> Core interface for remote entities </summary>
public interface IRemoteEntityCore: IEntityCore {}

/// <summary> Core interface for scene entities </summary>
public interface ISceneEntityCore: IEntityCore {}

/// <summary> Core interface for schedule entities </summary>
public interface IScheduleEntityCore: IEntityCore {}

/// <summary> Core interface for script entities </summary>
public interface IScriptEntityCore: IEntityCore {}

/// <summary> Core interface for select entities </summary>
public interface ISelectEntityCore: IEntityCore {}

/// <summary> Core interface for sensor entities </summary>
public interface ISensorEntityCore: IEntityCore {}

/// <summary> Core interface for sun entities </summary>
public interface ISunEntityCore: IEntityCore {}

/// <summary> Core interface for switch entities </summary>
public interface ISwitchEntityCore: IEntityCore {}

/// <summary> Core interface for timer entities </summary>
public interface ITimerEntityCore: IEntityCore {}

/// <summary> Core interface for update entities </summary>
public interface IUpdateEntityCore: IEntityCore {}

/// <summary> Core interface for vacuum entities </summary>
public interface IVacuumEntityCore: IEntityCore {}

/// <summary> Core interface for weather entities </summary>
public interface IWeatherEntityCore: IEntityCore {}

/// <summary> Core interface for zone entities </summary>
public interface IZoneEntityCore: IEntityCore {}


17 changes: 17 additions & 0 deletions src/HassModel/NetDeamon.HassModel/Entities/Core/IEntityCore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace NetDaemon.HassModel.Entities;

/// <summary>
/// Core interface for any entity
/// </summary>
public interface IEntityCore
{
/// <summary>
/// The IHAContext
/// </summary>
public IHaContext HaContext { get; }

/// <summary>
/// Entity id being handled by this entity
/// </summary>
public string EntityId { get; }
}
Loading