Skip to content

Commit

Permalink
Preparation for BLE
Browse files Browse the repository at this point in the history
  • Loading branch information
jdomnitz committed Jan 10, 2025
1 parent 87c7709 commit 68a32b9
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 35 deletions.
4 changes: 2 additions & 2 deletions MatterDotNet/Entities/Controller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ public async Task Start()
throw new NotImplementedException("BLE Commissioning is not supported yet");

// Discover the Node
ODNode? commissionableNode = await DiscoveryService.Shared.Find(payload.VendorID, payload.ProductID, payload.Discriminator, payload.DiscriminatorLength == 12);
ODNode? commissionableNode = await DiscoveryService.Shared.Find(payload.VendorID, payload.ProductID, payload.Discriminator, payload.LongDiscriminator);
if (commissionableNode == null)
return null;

try
{
// Establish PASE session
unsecureSession = SessionManager.GetUnsecureSession(new IPEndPoint(commissionableNode.Address!, commissionableNode.Port), true);
unsecureSession = SessionManager.GetUnsecureSession(new IPEndPoint(commissionableNode.IPAddress!, commissionableNode.Port), true);
PASE pase = new PASE(unsecureSession);
paseSecureSession = await pase.EstablishSecureSession(payload.Passcode);
if (paseSecureSession == null)
Expand Down
2 changes: 1 addition & 1 deletion MatterDotNet/Entities/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private Node(ODNode connection, Fabric fabric, OperationalCertificate noc)
/// <exception cref="IOException"></exception>
public async Task<SecureSession> GetCASESession()
{
using (SessionContext session = SessionManager.GetUnsecureSession(new IPEndPoint(connection.Address!, connection.Port), true))
using (SessionContext session = SessionManager.GetUnsecureSession(new IPEndPoint(connection.IPAddress!, connection.Port), true))
return await GetCASESession(session);
}
/// <summary>
Expand Down
8 changes: 4 additions & 4 deletions MatterDotNet/OperationalDiscovery/CommissioningPayload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public enum FlowType
/// <summary>
/// Length of Discriminator (bits)
/// </summary>
public byte DiscriminatorLength { get; set; }
public bool LongDiscriminator { get; set; }
/// <summary>
/// Device Type
/// </summary>
Expand All @@ -116,8 +116,8 @@ private CommissioningPayload(string QRCode)

Flow = (FlowType)readBits(data, 35, 2);
Capabilities = (DiscoveryCapabilities)readBits(data, 37, 8);
DiscriminatorLength = 12;
Discriminator = (ushort)readBits(data, 45, DiscriminatorLength);
LongDiscriminator = true;
Discriminator = (ushort)readBits(data, 45, 12);
Passcode = readBits(data, 57, 27);
uint padding = readBits(data, 84, 4);
if (padding != 0)
Expand Down Expand Up @@ -163,7 +163,7 @@ public static CommissioningPayload FromPIN(string pin)
ret.Passcode = (uint)(group1 & 0x3FFF);
ushort group2 = ushort.Parse(pin.Substring(6, 4));
ret.Passcode |= (uint)(group2 << 14);
ret.DiscriminatorLength = 4;
ret.LongDiscriminator = false;
if (vidpid)
{
if (pin.Length != 21)
Expand Down
43 changes: 34 additions & 9 deletions MatterDotNet/OperationalDiscovery/DiscoveryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

using System.Buffers.Binary;
using TinyDNS;
using TinyDNS.Enums;
using TinyDNS.Records;
Expand Down Expand Up @@ -44,6 +45,26 @@ public static DiscoveryService Shared
}
}

/// <summary>
/// Generate commissionable node info from BLE advertisement
/// </summary>
/// <param name="id"></param>
/// <param name="name"></param>
/// <param name="bleAdvertisement"></param>
/// <returns></returns>
public static ODNode FromAdvertisement(string id, string name, ReadOnlySpan<byte> bleAdvertisement)
{
ODNode ret = new ODNode();
if (bleAdvertisement[0] == 0x0)
ret.CommissioningMode = CommissioningMode.Basic;
ret.Discriminator = (ushort)(BinaryPrimitives.ReadUInt16LittleEndian(bleAdvertisement.Slice(1, 2)) & 0xFFF);
ret.Vendor = BinaryPrimitives.ReadUInt16LittleEndian(bleAdvertisement.Slice(3, 2));
ret.Product = BinaryPrimitives.ReadUInt16LittleEndian(bleAdvertisement.Slice(5, 2));
ret.DeviceName = name;
ret.BTAddress = id;
return ret;
}

/// <summary>
/// Find commissionable nodes matching the given params
/// </summary>
Expand Down Expand Up @@ -109,9 +130,13 @@ public async Task<List<ODNode>> Find(uint discriminator, bool fullLen)
/// <returns></returns>
public async Task<ODNode?> Find(string operationalInstanceName)
{
List<ODNode> results = Parse(await mdns.ResolveServiceInstance(operationalInstanceName, "_matter._tcp", "local"));
if (results.Count > 0)
return results[0];
List<ODNode> results;
for (int i = 0; i < 10; i++)
{
results = Parse(await mdns.ResolveServiceInstance(operationalInstanceName, "_matter._tcp", "local"));
if (results.Count > 0)
return results[0];
}
return null;
}

Expand Down Expand Up @@ -155,14 +180,14 @@ private List<ODNode> Parse(List<Message> msgs)
{
if (node.Port == 0 && additional is SRVRecord service)
node.Port = service.Port;
else if (node.Address == null && additional is ARecord A)
node.Address = A.Address;
else if (node.Address == null && additional is AAAARecord AAAA)
node.Address = AAAA.Address;
else if (node.IPAddress == null && additional is ARecord A)
node.IPAddress = A.Address;
else if (node.IPAddress == null && additional is AAAARecord AAAA)
node.IPAddress = AAAA.Address;
else if (additional is TxtRecord txt)
PopulateText(txt, ref node);
}
if (node.Address == null || node.Port == 0)
if (node.IPAddress == null || node.Port == 0)
continue;
ret.Add(node);
}
Expand Down Expand Up @@ -196,7 +221,7 @@ private void PopulateText(TxtRecord txt, ref ODNode node)
break;
case "D":
if (ushort.TryParse(kv[1], out ushort descriminator))
node.Descriminator = descriminator;
node.Discriminator = descriminator;
break;
case "SII":
if (int.TryParse(kv[1], out int sessionIdle))
Expand Down
15 changes: 12 additions & 3 deletions MatterDotNet/OperationalDiscovery/ODNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ public record ODNode
/// <summary>
/// Discovered IP Address
/// </summary>
public IPAddress? Address { get; set; }
public IPAddress? IPAddress { get; set; }
/// <summary>
/// Discovered Port
/// </summary>
public ushort Port { get; set; }
/// <summary>
/// Discovered BT LE Address
/// </summary>
public string BTAddress { get; set; }
/// <summary>
/// Idle Session Interval
/// </summary>
public int IdleInterval { get; set; }
Expand Down Expand Up @@ -66,12 +70,17 @@ public record ODNode
/// </summary>
public uint Product { get; set; }
/// <summary>
/// Descriminator
/// Discriminator
/// </summary>
public ushort Descriminator { get; set; }
public ushort Discriminator { get; set; }
/// <summary>
/// Current Commissioning Mode
/// </summary>
public CommissioningMode CommissioningMode { get; set; }

public override string ToString()
{
return $"Vendor: {Vendor}, Product: {Product}, Discriminator: {Discriminator:X3}, Name: {DeviceName}, Address: {(BTAddress != null ? BTAddress : $"{IPAddress}:{Port}")}, Type: {Type}, Mode: {CommissioningMode}";
}
}
}
2 changes: 1 addition & 1 deletion MatterDotNet/Protocol/Connection/MRPConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public async Task SendAck(SessionContext? session, ushort exchange, uint counter
ack.Flags |= MessageFlags.DestinationNodeID;
ack.Message.AckCounter = counter;
ack.Message.Protocol = Payloads.ProtocolType.SecureChannel;
PayloadWriter writer = new PayloadWriter(Frame.MAX_SIZE + 4);
PayloadWriter writer = new PayloadWriter(Frame.MAX_SIZE);
ack.Serialize(writer, session!);
if (AckTable.TryGetValue(exchange, out uint ctr) && ctr == counter)
AckTable.TryRemove(exchange, out _);
Expand Down
11 changes: 7 additions & 4 deletions MatterDotNet/Protocol/Payloads/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ namespace MatterDotNet.Protocol.Payloads
/// </summary>
public class Frame
{
internal const int MAX_SIZE = 1280;
/// <summary>
/// Frame MTU Size
/// </summary>
public const int MAX_SIZE = 1280;
internal static readonly byte[] PRIVACY_INFO = Encoding.UTF8.GetBytes("PrivacyKey");

/// <summary>
Expand Down Expand Up @@ -68,7 +71,7 @@ public override string ToString()
return $"Frame [F:{Flags}, Session: {SessionID}, S:{Security}, From: {SourceNodeID}, To: {DestinationID}, Ctr: {Counter}, Message: {Message}";
}

internal void Serialize(PayloadWriter stream, SessionContext session)
public void Serialize(PayloadWriter stream, SessionContext session)
{
stream.Write((byte)Flags);
stream.Write(SessionID);
Expand Down Expand Up @@ -121,13 +124,13 @@ internal void Serialize(PayloadWriter stream, SessionContext session)
}
}

internal Frame(IPayload? payload, byte opCode)
public Frame(IPayload? payload, byte opCode)
{
Valid = true;
Message = new Version1Payload(payload, opCode);
}

internal Frame(Span<byte> payload)
public Frame(Span<byte> payload)
{
Valid = true;
Flags = (MessageFlags)payload[0];
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@
**Coming Soon** - New implementation in progress

#### What's Working
* Automatic Code Generator (based on the specification)
* Cryptography
* Framing & TLV Parsing / Generation
* Sessions & Exchanges
* QR Code / MDNS Payload Parsing
* Device Commissioning
* Automatic Code Generation (directly from the specification)
* Commissioning by QR code or PIN
* Operational Discovery over IP or BT LE
* Reading/Writing Attributes
* Executing cluster commands

#### What's In Progress:
* Cluster generation

#### Coming Soon
* Matter 1.3 Standard Implementation
* Matter 1.4 Standard Implementation
* Multicast/Group Control
* Subscriptions
* Events

#### Other Projects:
* Check out my other projects for [HomeKit](https://github.com/SmartHomeOS/HomeKitDotNet) and [ZWave](https://github.com/SmartHomeOS/ZWaveDotNet)
Expand Down
8 changes: 4 additions & 4 deletions Test/PayloadParsingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void PIN_AllOnes()
Assert.That(parser.VendorID, Is.EqualTo(65535), "Invalid Vendor ID");
Assert.That(parser.ProductID, Is.EqualTo(65535), "Invalid Product ID");
Assert.That(parser.Passcode, Is.EqualTo(0x7FFFFFF), "Invalid Passcode");
Assert.That(parser.DiscriminatorLength, Is.EqualTo(4), "Invalid Discriminator Length");
Assert.That(parser.LongDiscriminator, Is.EqualTo(false), "Invalid Discriminator Length");
}

[Test]
Expand All @@ -38,7 +38,7 @@ public void PIN_TestValuesLong()
Assert.That(parser.VendorID, Is.EqualTo(1), "Invalid Vendor ID");
Assert.That(parser.ProductID, Is.EqualTo(1), "Invalid Product ID");
Assert.That(parser.Passcode, Is.EqualTo(12345679), "Invalid Passcode");
Assert.That(parser.DiscriminatorLength, Is.EqualTo(4), "Invalid Discriminator Length");
Assert.That(parser.LongDiscriminator, Is.EqualTo(false), "Invalid Discriminator Length");
}

[Test]
Expand All @@ -50,7 +50,7 @@ public void PIN_TestValuesShort()
Assert.That(parser.VendorID, Is.EqualTo(0), "Vendor ID should not exist");
Assert.That(parser.ProductID, Is.EqualTo(0), "Product ID should not exist");
Assert.That(parser.Passcode, Is.EqualTo(97095205), "Invalid Passcode");
Assert.That(parser.DiscriminatorLength, Is.EqualTo(4), "Invalid Discriminator Length");
Assert.That(parser.LongDiscriminator, Is.EqualTo(false), "Invalid Discriminator Length");
}

[Test]
Expand All @@ -64,7 +64,7 @@ public void QR_Test()
Assert.That(parser.Passcode, Is.EqualTo(20202021), "Invalid Passcode");
Assert.That(parser.Capabilities, Is.EqualTo(CommissioningPayload.DiscoveryCapabilities.BLE), "Invalid Capabilities");
Assert.That(parser.Flow, Is.EqualTo(CommissioningPayload.FlowType.STANDARD), "Invalid Capabilities");
Assert.That(parser.DiscriminatorLength, Is.EqualTo(12), "Invalid Discriminator Length");
Assert.That(parser.LongDiscriminator, Is.EqualTo(true), "Invalid Discriminator Length");
}
}
}

0 comments on commit 68a32b9

Please sign in to comment.