1+ using JoySoftware . HomeAssistant . Client ;
12using JoySoftware . HomeAssistant . NetDaemon . Daemon . Config ;
23using Microsoft . CodeAnalysis ;
34using Microsoft . CodeAnalysis . CSharp ;
45using Microsoft . CodeAnalysis . CSharp . Syntax ;
6+ using System ;
57using System . Collections . Generic ;
68using System . Linq ;
79using System . Runtime . CompilerServices ;
@@ -64,6 +66,7 @@ public class CodeGenerator
6466 // Add the classes implementing the specific entities
6567 foreach ( var domain in GetDomainsFromEntities ( entities ) )
6668 {
69+
6770 if ( _FluentApiMapper . ContainsKey ( domain ) )
6871 {
6972 var classDeclaration = $@ "public partial class { domain . ToCamelCase ( ) } Entities
@@ -100,11 +103,16 @@ public class CodeGenerator
100103 return code . NormalizeWhitespace ( indentation : " " , eol : "\n " ) . ToFullString ( ) ;
101104 }
102105
103- public string ? GenerateCodeRx ( string nameSpace , IEnumerable < string > entities )
106+ public string ? GenerateCodeRx ( string nameSpace , IEnumerable < string > entities , IEnumerable < HassServiceDomain > services )
104107 {
105108 var code = SyntaxFactory . CompilationUnit ( ) ;
106109
107110 // Add Usings statements
111+ code = code . AddUsings ( SyntaxFactory . UsingDirective ( SyntaxFactory . ParseName ( "System" ) ) ) ;
112+ code = code . AddUsings ( SyntaxFactory . UsingDirective ( SyntaxFactory . ParseName ( "System.Collections.Generic" ) ) ) ;
113+ code = code . AddUsings ( SyntaxFactory . UsingDirective ( SyntaxFactory . ParseName ( "System.Dynamic" ) ) ) ;
114+ code = code . AddUsings ( SyntaxFactory . UsingDirective ( SyntaxFactory . ParseName ( "System.Linq" ) ) ) ;
115+ code = code . AddUsings ( SyntaxFactory . UsingDirective ( SyntaxFactory . ParseName ( "JoySoftware.HomeAssistant.NetDaemon.Common" ) ) ) ;
108116 code = code . AddUsings ( SyntaxFactory . UsingDirective ( SyntaxFactory . ParseName ( "JoySoftware.HomeAssistant.NetDaemon.Common.Reactive" ) ) ) ;
109117
110118 // Add namespace
@@ -122,22 +130,93 @@ public class CodeGenerator
122130
123131 foreach ( var domain in domains )
124132 {
125- if ( _FluentApiMapper . ContainsKey ( domain ) )
133+ var camelCaseDomain = domain . ToCamelCase ( ) ;
134+ var property = $@ "public { camelCaseDomain } Entities { camelCaseDomain } => new { camelCaseDomain } Entities(this);";
135+ var propertyDeclaration = CSharpSyntaxTree . ParseText ( property ) . GetRoot ( ) . ChildNodes ( ) . OfType < PropertyDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
136+ extensionClass = extensionClass . AddMembers ( propertyDeclaration ) ;
137+ }
138+ namespaceDeclaration = namespaceDeclaration . AddMembers ( extensionClass ) ;
139+
140+ foreach ( var domain in GetDomainsFromEntities ( entities ) )
141+ {
142+ var classDeclaration = $@ "public partial class { domain . ToCamelCase ( ) } Entity : RxEntity
143+ {{
144+ public string EntityId => EntityIds.First();
145+
146+ public string? Area => DaemonRxApp.State(EntityId)?.Area;
147+
148+ public dynamic? Attribute => DaemonRxApp.State(EntityId)?.Attribute;
149+
150+ public DateTime LastChanged => DaemonRxApp.State(EntityId).LastChanged;
151+
152+ public DateTime LastUpdated => DaemonRxApp.State(EntityId).LastUpdated;
153+
154+ public dynamic? State => DaemonRxApp.State(EntityId)?.State;
155+
156+ public { domain . ToCamelCase ( ) } Entity(INetDaemonReactive daemon, IEnumerable<string> entityIds) : base(daemon, entityIds)
157+ {{
158+ }}
159+ }}" ;
160+ var entityClass = CSharpSyntaxTree . ParseText ( classDeclaration ) . GetRoot ( ) . ChildNodes ( ) . OfType < ClassDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
161+
162+ // var entityIdProperty = $@"public string EntityId => EntityIds.First();";
163+ // var entityIdPropertyDeclaration = CSharpSyntaxTree.ParseText(entityIdProperty).GetRoot().ChildNodes().OfType<PropertyDeclarationSyntax>().FirstOrDefault();
164+ // entityClass = entityClass.AddMembers(entityIdPropertyDeclaration);
165+
166+ // var stateProperty = $@"public EntityState? State => DaemonRxApp.State(EntityId)?.State;";
167+ // var statePropertyDeclaration = CSharpSyntaxTree.ParseText(stateProperty).GetRoot().ChildNodes().OfType<PropertyDeclarationSyntax>().FirstOrDefault();
168+ // entityClass = entityClass.AddMembers(statePropertyDeclaration);
169+
170+ // They allready have default implementation
171+ var skipServices = new string [ ] { "turn_on" , "turn_off" , "toggle" } ;
172+
173+ foreach ( var s in services . Where ( n => n . Domain == domain ) . SelectMany ( n => n . Services ) )
126174 {
127- var camelCaseDomain = domain . ToCamelCase ( ) ;
128- var property = $@ "public { camelCaseDomain } Entities { camelCaseDomain } => new { camelCaseDomain } Entities(this);";
129- var propertyDeclaration = CSharpSyntaxTree . ParseText ( property ) . GetRoot ( ) . ChildNodes ( ) . OfType < PropertyDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
130- extensionClass = extensionClass . AddMembers ( propertyDeclaration ) ;
175+ if ( s . Service is null )
176+ continue ;
177+
178+ var name = s . Service [ ( s . Service . IndexOf ( "." ) + 1 ) ..] ;
179+
180+ if ( Array . IndexOf ( skipServices , name ) >= 0 )
181+ continue ;
182+
183+ // Quick check to make sure the name is a valid C# identifier. Should really check to make
184+ // sure it doesn't collide with a reserved keyword as well.
185+ if ( ! char . IsLetter ( name [ 0 ] ) && ( name [ 0 ] != '_' ) )
186+ {
187+ name = "s_" + name ;
188+ }
189+ var hasEntityId = s . Fields . Count ( c => c . Field == "entity_id" ) > 0 ? true : false ;
190+ var entityAssignmentStatement = hasEntityId ? @"serviceData[""entity_id""] = EntityId;" : "" ;
191+ var methodCode = $@ "public void { name . ToCamelCase ( ) } (dynamic? data=null)
192+ {{
193+ var serviceData = new FluentExpandoObject();
194+
195+ if (data is ExpandoObject)
196+ {{
197+ serviceData.CopyFrom(data);
198+ }}
199+ else if (data is object)
200+ {{
201+ var expObject = ((object)data).ToExpandoObject();
202+ serviceData.CopyFrom(expObject);
203+ }}
204+ { entityAssignmentStatement }
205+ DaemonRxApp.CallService(""{ domain } "", ""{ s . Service } "", serviceData);
206+ }}
207+ " ;
208+ var methodDeclaration = CSharpSyntaxTree . ParseText ( methodCode ) . GetRoot ( ) . ChildNodes ( ) . OfType < MethodDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
209+ entityClass = entityClass . AddMembers ( methodDeclaration ) ;
131210 }
211+ namespaceDeclaration = namespaceDeclaration . AddMembers ( entityClass ) ;
212+
132213 }
133- namespaceDeclaration = namespaceDeclaration . AddMembers ( extensionClass ) ;
134214
135215 // Add the classes implementing the specific entities
136216 foreach ( var domain in GetDomainsFromEntities ( entities ) )
137217 {
138- if ( _FluentApiMapper . ContainsKey ( domain ) )
139- {
140- var classDeclaration = $@ "public partial class { domain . ToCamelCase ( ) } Entities
218+
219+ var classDeclaration = $@ "public partial class { domain . ToCamelCase ( ) } Entities
141220 {{
142221 private readonly NetDaemonRxApp _app;
143222
@@ -146,24 +225,23 @@ public class CodeGenerator
146225 _app = app;
147226 }}
148227 }}" ;
149- var entityClass = CSharpSyntaxTree . ParseText ( classDeclaration ) . GetRoot ( ) . ChildNodes ( ) . OfType < ClassDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
150- foreach ( var entity in entities . Where ( n => n . StartsWith ( domain ) ) )
228+ var entityClass = CSharpSyntaxTree . ParseText ( classDeclaration ) . GetRoot ( ) . ChildNodes ( ) . OfType < ClassDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
229+ foreach ( var entity in entities . Where ( n => n . StartsWith ( domain ) ) )
230+ {
231+
232+ var name = entity [ ( entity . IndexOf ( "." ) + 1 ) ..] ;
233+ // Quick check to make sure the name is a valid C# identifier. Should really check to make
234+ // sure it doesn't collide with a reserved keyword as well.
235+ if ( ! char . IsLetter ( name [ 0 ] ) && ( name [ 0 ] != '_' ) )
151236 {
152-
153- var name = entity [ ( entity . IndexOf ( "." ) + 1 ) ..] ;
154- // Quick check to make sure the name is a valid C# identifier. Should really check to make
155- // sure it doesn't collide with a reserved keyword as well.
156- if ( ! char . IsLetter ( name [ 0 ] ) && ( name [ 0 ] != '_' ) )
157- {
158- name = "e_" + name ;
159- }
160-
161- var propertyCode = $@ "public RxEntity { name . ToCamelCase ( ) } => _app.Entity(""{ entity } "");";
162- var propDeclaration = CSharpSyntaxTree . ParseText ( propertyCode ) . GetRoot ( ) . ChildNodes ( ) . OfType < PropertyDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
163- entityClass = entityClass . AddMembers ( propDeclaration ) ;
237+ name = "e_" + name ;
164238 }
165- namespaceDeclaration = namespaceDeclaration . AddMembers ( entityClass ) ;
239+
240+ var propertyCode = $@ "public { domain . ToCamelCase ( ) } Entity { name . ToCamelCase ( ) } => new { domain . ToCamelCase ( ) } Entity(_app, new string[] {{""{entity}""}});";
241+ var propDeclaration = CSharpSyntaxTree . ParseText ( propertyCode ) . GetRoot ( ) . ChildNodes ( ) . OfType < PropertyDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
242+ entityClass = entityClass . AddMembers ( propDeclaration ) ;
166243 }
244+ namespaceDeclaration = namespaceDeclaration . AddMembers ( entityClass ) ;
167245 }
168246
169247 code = code . AddMembers ( namespaceDeclaration ) ;
0 commit comments