From 0d5d3d290d91d76eb1d91356b25ca0d29cac62ce Mon Sep 17 00:00:00 2001 From: jdomnitz <380352+jdomnitz@users.noreply.github.com> Date: Fri, 29 Nov 2024 18:37:37 -0500 Subject: [PATCH] Add a code generator to generate the models for us --- .gitignore | 3 +- Generator/ClassGenerator.cs | 223 +++++++++++++++++++++++++++++ Generator/DataType.cs | 29 ++++ Generator/Generator.cs | 47 ++++++ Generator/Generator.csproj | 26 ++++ Generator/StructParser.cs | 237 +++++++++++++++++++++++++++++++ Generator/Tag.cs | 49 +++++++ MatterDotNet.sln | 6 + MatterDotNet/MatterDotNet.csproj | 17 ++- Test/Test.csproj | 5 +- 10 files changed, 638 insertions(+), 4 deletions(-) create mode 100644 Generator/ClassGenerator.cs create mode 100644 Generator/DataType.cs create mode 100644 Generator/Generator.cs create mode 100644 Generator/Generator.csproj create mode 100644 Generator/StructParser.cs create mode 100644 Generator/Tag.cs diff --git a/.gitignore b/.gitignore index 9491a2f..8fc5ae0 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,5 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd +/Generator/Structures diff --git a/Generator/ClassGenerator.cs b/Generator/ClassGenerator.cs new file mode 100644 index 0000000..25d01b7 --- /dev/null +++ b/Generator/ClassGenerator.cs @@ -0,0 +1,223 @@ +// MatterDotNet Copyright (C) 2024 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or any later version. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace Generator +{ + public static class ClassGenerator + { + private static readonly string HEADER = "// MatterDotNet Copyright (C) 2024 \n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or any later version.\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY, without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n// See the GNU Affero General Public License for more details.\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see .\n//\n// WARNING: This file was auto-generated. Do not edit.\n\nusing MatterDotNet.Protocol.Parsers;\nusing MatterDotNet.Protocol.Payloads;\nusing System.Diagnostics.CodeAnalysis;\n"; + public static bool Emit(Stream stream, Tag tag) + { + StreamWriter writer = new StreamWriter(stream); + { + writer.WriteLine(HEADER); + writer.WriteLine($"namespace MatterDotNet.Messages\n{{"); + WriteTag(" ", tag, writer); + writer.Write("}"); + writer.Flush(); + } + return true; + } + + private static void WriteTag(string indent, Tag tag, StreamWriter writer) + { + writer.Write($"{indent}public class {tag.Name}"); + if (tag.Parent == null) + writer.WriteLine($" : TLVPayload\n{indent}{{\n{indent} /// \n{indent} public {tag.Name}() {{}}\n\n{indent} /// \n{indent} [SetsRequiredMembers]\n{indent} public {tag.Name}(Memory data) : this(new TLVReader(data)) {{}}\n"); + else + writer.WriteLine($"\n{indent}{{"); + foreach (Tag child in tag.Children) + { + if ((child.Type == DataType.Array || child.Type == DataType.List) && child.Children.Count > 0) + WriteTag(indent + " ", child.Children[0], writer); + } + foreach (Tag child in tag.Children) + { + if (child.Type != DataType.Structure) + writer.WriteLine($"{indent} {(child.Optional? "public" : "public required")} {GetType(child)}{((child.Nullable || child.Optional) ? "?" : "")} {child.Name} {{ get; set; }} "); + } + if (tag.Parent == null) { + writer.WriteLine($"\n{indent} /// \n{indent} [SetsRequiredMembers]\n{indent} public {tag.Name}(TLVReader reader) {{"); + writer.WriteLine($"{indent} reader.StartStructure();"); + foreach (Tag child in tag.Children) + { + string totalIndent = $"{indent} "; + if (child.Optional) + { + writer.WriteLine($"{totalIndent}if (reader.IsTag({child.TagNumber}))"); + totalIndent += " "; + } + switch (child.Type) + { + case DataType.Boolean: + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetBool({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? ".Value;" : ";")}"); + break; + case DataType.Integer: + if (child.LengthBytes == 1) + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetSByte({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? ".Value;" : ";")}"); + else if (child.LengthBytes == 2) + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetShort({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? ".Value;" : ";")}"); + else if (child.LengthBytes == 4) + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetInt({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? ".Value;" : ";")}"); + else + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetLong({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? ".Value;" : ";")}"); + break; + case DataType.UnsignedInteger: + if (child.LengthBytes == 1) + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetByte({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? ".Value;" : ";")}"); + else if (child.LengthBytes == 2) + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetUShort({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? ".Value;" : ";")}"); + else if (child.LengthBytes == 4) + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetUInt({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? ".Value;" : ";")}"); + else + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetULong({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? ".Value;" : ";")}"); + break; + case DataType.FloatingPoint: + if (child.LengthBytes == 4) + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetFloat({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? ".Value;" : ";")}"); + else + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetDouble({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? ".Value;" : ";")}"); + break; + case DataType.Bytes: + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetBytes({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? "!;" : ";")}"); + break; + case DataType.String: + writer.WriteLine($"{totalIndent}{child.Name} = reader.GetString({child.TagNumber}{(child.Nullable ? ", true)" : ")")}{(!child.Nullable && !child.Optional ? "!;" : ";")}"); + break; + case DataType.Reference: + writer.WriteLine($"{totalIndent}{child.Name} = new {child.ReferenceName}(reader);"); + break; + + } + } + writer.WriteLine($"{indent} }}\n\n{indent} /// \n{indent} public override void Serialize(TLVWriter writer) {{"); + writer.WriteLine($"{indent} writer.StartStructure();"); + foreach (Tag child in tag.Children) + { + string totalIndent = $"{indent} "; + if (child.Optional) + { + writer.WriteLine($"{totalIndent}if ({child.Name} != null)"); + totalIndent += " "; + } + switch (child.Type) + { + case DataType.Boolean: + writer.WriteLine($"{totalIndent}writer.WriteBool({child.TagNumber}, {child.Name});"); + break; + case DataType.Integer: + if (child.LengthBytes == 1) + writer.WriteLine($"{totalIndent}writer.WriteSByte({child.TagNumber}, {child.Name});"); + else if (child.LengthBytes == 2) + writer.WriteLine($"{totalIndent}writer.WriteShort({child.TagNumber}, {child.Name});"); + else if (child.LengthBytes == 4) + writer.WriteLine($"{totalIndent}writer.WriteInt({child.TagNumber}, {child.Name});"); + else + writer.WriteLine($"{totalIndent}writer.WriteLong({child.TagNumber}, {child.Name});"); + break; + case DataType.UnsignedInteger: + if (child.LengthBytes == 1) + writer.WriteLine($"{totalIndent}writer.WriteByte({child.TagNumber}, {child.Name});"); + else if (child.LengthBytes == 2) + writer.WriteLine($"{totalIndent}writer.WriteUShort({child.TagNumber}, {child.Name});"); + else if (child.LengthBytes == 4) + writer.WriteLine($"{totalIndent}writer.WriteUInt({child.TagNumber}, {child.Name});"); + else + writer.WriteLine($"{totalIndent}writer.WriteULong({child.TagNumber}, {child.Name});"); + break; + case DataType.FloatingPoint: + if (child.LengthBytes == 4) + writer.WriteLine($"{totalIndent}writer.WriteFloat({child.TagNumber}, {child.Name});"); + else + writer.WriteLine($"{totalIndent}writer.WriteDouble({child.TagNumber}, {child.Name});"); + break; + case DataType.Bytes: + writer.WriteLine($"{totalIndent}writer.WriteBytes({child.TagNumber}, {child.Name}, {child.LengthBytes});"); + break; + case DataType.String: + writer.WriteLine($"{totalIndent}writer.WriteString({child.TagNumber}, {child.Name}, {child.LengthBytes});"); + break; + case DataType.Reference: + writer.WriteLine($"{totalIndent}{child.Name}.Serialize(writer);"); + break; + + } + } + writer.WriteLine($"{indent} writer.EndContainer();\n{indent} }}"); + } + writer.WriteLine($"{indent}}}"); + } + + private static string GetType(Tag tag) + { + switch (tag.Type) + { + case DataType.Array: + if (tag.Children.Count == 0) + return "object[]"; + return tag.Children[0].Name + "[]"; + case DataType.Boolean: + return "bool"; + case DataType.Bytes: + return "byte[]"; + case DataType.FloatingPoint: + if (tag.LengthBytes == 4) + return "float"; + else if (tag.LengthBytes == 8) + return "double"; + throw new InvalidDataException(tag.Name + " has length " + tag.LengthBytes); + case DataType.Integer: + switch (tag.LengthBytes) + { + case 1: + return "sbyte"; + case 2: + return "short"; + case 4: + return "int"; + case 8: + return "long"; + default: + throw new InvalidDataException(tag.Name + " has length " + tag.LengthBytes); + } + case DataType.List: + if (tag.Children.Count == 0) + return "List"; + return "List<" + tag.Children[0].Name + ">"; + case DataType.Null: + throw new InvalidDataException("type of " + tag.Name + " is null"); + case DataType.Reference: + return tag.ReferenceName!; + case DataType.String: + return "string"; + case DataType.Structure: + return "class"; + case DataType.UnsignedInteger: + switch (tag.LengthBytes) + { + case 1: + return "byte"; + case 2: + return "ushort"; + case 4: + return "uint"; + case 8: + return "ulong"; + default: + throw new InvalidDataException(tag.Name + " has length " + tag.LengthBytes); + } + default: + throw new InvalidDataException("Unsupported type " + tag.Type); + } + } + } +} diff --git a/Generator/DataType.cs b/Generator/DataType.cs new file mode 100644 index 0000000..0cd4906 --- /dev/null +++ b/Generator/DataType.cs @@ -0,0 +1,29 @@ +// MatterDotNet Copyright (C) 2024 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or any later version. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace Generator +{ + public enum DataType + { + Array, + Boolean, + Bytes, + FloatingPoint, + Integer, + List, + Null, + Reference, + String, + Structure, + UnsignedInteger, + } +} diff --git a/Generator/Generator.cs b/Generator/Generator.cs new file mode 100644 index 0000000..e1da10d --- /dev/null +++ b/Generator/Generator.cs @@ -0,0 +1,47 @@ +// MatterDotNet Copyright (C) 2024 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or any later version. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +namespace Generator +{ + internal class Generator + { + static void Main(string[] args) + { + Directory.CreateDirectory("outputs"); + foreach (string file in Directory.EnumerateFiles("..\\..\\..\\Structures")) + { + Tag[] structs; + using (FileStream fs = File.OpenRead(file)) + structs = StructParser.ParseStruct(fs); + if (structs.Length == 0) + Console.WriteLine("Failed to parse structure"); + else + Console.WriteLine("Read Structure Successfully: \n******************************************************\n" + string.Join('\n', (object[])structs) + "\n*************************************"); + foreach (Tag tag in structs) + { + if (File.Exists("outputs\\" + tag.Name + ".cs")) + File.Delete("outputs\\" + tag.Name + ".cs"); + using (FileStream outstream = File.OpenWrite("outputs\\" + tag.Name + ".cs")) + { + if (ClassGenerator.Emit(outstream, tag)) + Console.WriteLine(tag.Name + " Written Successfully!"); + else + Console.WriteLine("Write Failed"); + } + } + } + Console.ReadLine(); + } + + + } +} diff --git a/Generator/Generator.csproj b/Generator/Generator.csproj new file mode 100644 index 0000000..ec97ca4 --- /dev/null +++ b/Generator/Generator.csproj @@ -0,0 +1,26 @@ + + + + Exe + net8.0 + enable + enable + 0.1.0 + jdomnitz + SmartHomeOS and Contributors + AGPL-3.0-or-later + MatterDotNet Code Generator + A C# implementation of the Matter 1.3 Standard (Formally known as project chip) + Copyright MatterDotNet Contributors + README.md + https://github.com/SmartHomeOS/MatterDotNet/ + matter; matter-controller; smarthome; project-chip; dotnet; + Library is not yet functional. See README for details. + logo.png + + + + + + + diff --git a/Generator/StructParser.cs b/Generator/StructParser.cs new file mode 100644 index 0000000..4467975 --- /dev/null +++ b/Generator/StructParser.cs @@ -0,0 +1,237 @@ +// MatterDotNet Copyright (C) 2024 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or any later version. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY, without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using MatterDotNet.Protocol.Cryptography; +using System.Text; + +namespace Generator +{ + public static class StructParser + { + public static Tag[] ParseStruct(Stream stream) + { + List tags = new List(); + Tag? root = null; + using (StreamReader sr = new StreamReader(stream)) + { + Tag? parent = null; + Tag? lastTag = null; + while (!sr.EndOfStream) + { + string? line = sr.ReadLine(); + if (line == null) + break; + else + line = line.Trim(); + if (line == "{") + parent = lastTag; + else if (line == "}" || line == "},") { + if (parent != null) + parent = parent.Parent; + if (parent == null) + { + tags.Add(root); + root = null; + lastTag = null; + } + } + else if (!string.IsNullOrWhiteSpace(line) && !line.StartsWith("//")) + { + lastTag = parseLine(line, parent); + if (root == null) + { + root = lastTag; + parent = lastTag; + } + else + { + parent!.Children.Add(lastTag); + } + } + } + } + return tags.ToArray(); + } + + private static Tag? parseLine(string line, Tag? parent) + { + string[] parts = line.TrimEnd(',').Split([":", "=>"], StringSplitOptions.TrimEntries); + if (parts.Length < 2) + throw new Exception("Failed to parse: " + line); + Tag tag = new Tag(); + tag.Parent = parent; + string[] nameParts = parts[0].Split([" [", "]"], StringSplitOptions.TrimEntries); + tag.Name = SanitizeName(nameParts[0]); + if (nameParts.Length > 1) + { + string[] constraints = nameParts[1].Split(',', StringSplitOptions.TrimEntries); + foreach (string constraint in constraints) + { + switch (constraint) + { + case "optional": + tag.Optional = true; + break; + default: + if (int.TryParse(constraint, out int tagnum)) + tag.TagNumber = tagnum; + break; + } + } + } + parts[1] = parts[1].Replace("ec-pub-key", $"OCTET STRING [ length {Crypto.PUBLIC_KEY_SIZE_BYTES} ]"); + parts[1] = parts[1].Replace("ec-signature", $"OCTET STRING [ length {Crypto.GROUP_SIZE_BYTES * 2} ]"); + parts[1] = parts[1].Replace("destination-identifier", $"OCTET STRING [ length {Crypto.HASH_LEN_BYTES} ]"); + string[] typeParts = parts[1].Split([" [", "]"], StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + switch (typeParts[0].ToUpper()) + { + case "UNSIGNED INTEGER": + tag.Type = DataType.UnsignedInteger; + break; + case "BOOLEAN": + tag.Type = DataType.Boolean; + break; + case "STRUCTURE": + tag.Type = DataType.Structure; + break; + case "SIGNED INTEGER": + tag.Type = DataType.Integer; + break; + case "OCTET STRING": + tag.Type = DataType.Bytes; + break; + case "STRING": + tag.Type = DataType.String; + break; + case "ARRAY": + case "ARRAY OF": + tag.Type = DataType.Array; + break; + case "LIST": + case "LIST OF": + tag.Type = DataType.List; + break; + case "FLOAT32": + tag.Type = DataType.FloatingPoint; + tag.LengthBytes = 4; + break; + case "FLOAT64": + tag.Type = DataType.FloatingPoint; + tag.LengthBytes = 8; + break; + default: + tag.Type = DataType.Reference; + tag.ReferenceName = SanitizeName(typeParts[0]); + break; + } + if (typeParts.Length > 1) + { + string[] constraints = typeParts[1].Split(',', StringSplitOptions.TrimEntries); + foreach (string constraint in constraints) + { + switch (constraint) + { + case "length 8-bits": + case "range 8-bits": + tag.LengthBytes = 1; + break; + case "length 16-bits": + case "range 16-bits": + tag.LengthBytes = 2; + break; + case "length 32-bits": + case "range 32-bits": + tag.LengthBytes = 4; + break; + case "length 64-bits": + case "range 64-bits": + tag.LengthBytes = 8; + break; + case "length CRYPTO_AEAD_MIC_LENGTH_BYTES": + tag.Min = tag.Max = Crypto.AEAD_MIC_LENGTH_BYTES; + break; + case "length CRYPTO_PUBLIC_KEY_SIZE_BYTES": + tag.Min = tag.Max = Crypto.PUBLIC_KEY_SIZE_BYTES; + break; + case "length CRYPTO_HASH_LEN_BYTES": + tag.Min = tag.Max = Crypto.HASH_LEN_BYTES; + break; + case "nullable": + tag.Nullable = true; + break; + } + if (constraint.StartsWith("length", StringComparison.OrdinalIgnoreCase)) + { + if (constraint.Contains("..")) + { + string[] rangeParts = constraint.Substring(7).Split(".."); + tag.Min = int.Parse(rangeParts[0]); + if (int.TryParse(rangeParts[1], out int max)) + tag.Max = max; + } + else if (int.TryParse(constraint, out int count)) + { + tag.Min = count; + tag.Max = count; + } + } + } + } + if (tag.LengthBytes == 0 && tag.Max != 0) + { + if (tag.Type == DataType.Integer) + { + if (tag.Max < sbyte.MaxValue) + tag.LengthBytes = 1; + else if (tag.Max < short.MaxValue) + tag.LengthBytes = 2; + else if (tag.Max < int.MaxValue) + tag.LengthBytes = 4; + else + tag.LengthBytes = 8; + } + else + { + if (tag.Max < byte.MaxValue) + tag.LengthBytes = 1; + else if (tag.Max < ushort.MaxValue) + tag.LengthBytes = 2; + else if (tag.Max < uint.MaxValue) + tag.LengthBytes = 4; + else + tag.LengthBytes = 8; + } + } + return tag; + } + + private static string SanitizeName(string name) + { + name = name.Replace("-struct", ""); + bool cap = true; + StringBuilder ret = new StringBuilder(name.Length); + foreach (char c in name) + { + if (c == ' ' || c == '-') + cap = true; + else if (cap) + { + ret.Append(char.ToUpper(c)); + cap = false; + } + else + ret.Append(c); + } + return ret.ToString(); + } + } +} diff --git a/Generator/Tag.cs b/Generator/Tag.cs new file mode 100644 index 0000000..50d599f --- /dev/null +++ b/Generator/Tag.cs @@ -0,0 +1,49 @@ + + +using System.Text; + +namespace Generator +{ + public class Tag + { + public Tag() + { + Children = new List(); + } + + public DataType Type { get; set; } + public string? Name { get; set; } + public int LengthBytes { get; set; } + public long Min { get; set; } + public long Max { get; set; } + public bool Nullable { get; set; } + public bool Optional { get; set; } + public List Children { get; set; } + public Tag? Parent { get; set; } + public int TagNumber { get; set; } + public string? ReferenceName { get; set; } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + if (Type == DataType.Reference) + sb.AppendLine(ReferenceName + " " + Name + ":"); + sb.AppendLine(Type + " " + Name); + foreach (Tag child in Children) + sb.Append(getPrefix() + child.ToString()); + return sb.ToString(); + } + + private string getPrefix() + { + string prefix = string.Empty; + Tag tag = this; + while (tag.Parent != null) + { + prefix += '\t'; + tag = tag.Parent; + } + return prefix; + } + } +} diff --git a/MatterDotNet.sln b/MatterDotNet.sln index 17938b4..099438a 100644 --- a/MatterDotNet.sln +++ b/MatterDotNet.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleConsole", "ExampleCo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{DDE2325B-62EF-460A-8A4B-6ED3311AEFAF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generator", "Generator\Generator.csproj", "{014FD19C-98D5-4CB0-8FE0-332CA37C1BA1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {DDE2325B-62EF-460A-8A4B-6ED3311AEFAF}.Debug|Any CPU.Build.0 = Debug|Any CPU {DDE2325B-62EF-460A-8A4B-6ED3311AEFAF}.Release|Any CPU.ActiveCfg = Release|Any CPU {DDE2325B-62EF-460A-8A4B-6ED3311AEFAF}.Release|Any CPU.Build.0 = Release|Any CPU + {014FD19C-98D5-4CB0-8FE0-332CA37C1BA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {014FD19C-98D5-4CB0-8FE0-332CA37C1BA1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {014FD19C-98D5-4CB0-8FE0-332CA37C1BA1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {014FD19C-98D5-4CB0-8FE0-332CA37C1BA1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MatterDotNet/MatterDotNet.csproj b/MatterDotNet/MatterDotNet.csproj index ec9d086..b94c991 100644 --- a/MatterDotNet/MatterDotNet.csproj +++ b/MatterDotNet/MatterDotNet.csproj @@ -2,8 +2,21 @@ net8.0; net9.0 - enable - enable + enable + enable + 0.1.0 + jdomnitz + SmartHomeOS and Contributors + AGPL-3.0-or-later + MatterDotNet + A C# implementation of the Matter 1.3 Standard (Formally known as project chip) + Copyright MatterDotNet Contributors + README.md + https://github.com/SmartHomeOS/MatterDotNet/ + matter; matter-controller; smarthome; project-chip; dotnet; + Library is not yet functional. See README for details. + logo.png + True diff --git a/Test/Test.csproj b/Test/Test.csproj index 1b36e86..51931cc 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -4,7 +4,10 @@ net8.0 enable enable - + 0.1.0 + jdomnitz + SmartHomeOS and Contributors + AGPL-3.0-or-later false true