Skip to content

Commit 1e03339

Browse files
Add module support (#174)
* Initial commit * adjusting auth Co-authored-by: José Simões <[email protected]>
1 parent 51dbc8b commit 1e03339

File tree

3 files changed

+116
-36
lines changed

3 files changed

+116
-36
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,20 @@ Here are existing QoS levels that you can use:
313313

314314
While it's possible to configure QoS 0 (AtMostOnce) for faster message exchange, you should note that the delivery isn't guaranteed nor acknowledged. For this reason, QoS 0 is often referred as "fire and forget".
315315

316+
## Module support
317+
318+
Modules are supported, you will have to use the constructor to pass the module ID either with a SAS token, either with a certificate. The rest fully works like a normal device. Everything is fully supported including module direct method, telemetry and of course twins!
319+
320+
For example here with a SAS token. Note that the certificates are fully supported as well. And if you are not storing the Azure root certificate on the device, you'll need to pass it in the constructor.
321+
322+
```csharp
323+
const string DeviceID = "nanoEdgeTwin";
324+
const string ModuleID = "myModule";
325+
const string IotBrokerAddress = "youriothub.azure-devices.net";
326+
const string SasKey = "yoursaskey";
327+
DeviceClient module = new DeviceClient(IotBrokerAddress, DeviceID, ModuleID, SasKey);
328+
```
329+
316330
## Azure IoT Device Provisioning Service (DPS) support
317331

318332
This SDK also supports the Azure IoT Device Provisioning Service. Group and individual provisioning scenarios are supported either with a symmetric key either with certificates. To understand the mechanism behind DPS, it is recommended to read the [documentation](https://docs.microsoft.com/azure/iot-dps/).

nanoFramework.Azure.Devices.Client/DeviceClient.cs

Lines changed: 96 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
namespace nanoFramework.Azure.Devices.Client
1616
{
1717
/// <summary>
18-
/// Azure IoT Client SDK for .NET nanoFramework using MQTT
18+
/// Azure IoT Client SDK for .NET nanoFramework using MQTT.
1919
/// </summary>
2020
public class DeviceClient : IDisposable
2121
{
22-
const string TwinReportedPropertiesTopic = "$iothub/twin/PATCH/properties/reported/";
23-
const string TwinDesiredPropertiesTopic = "$iothub/twin/GET/";
24-
const string DirectMethodTopic = "$iothub/methods/POST/";
22+
private const string TwinReportedPropertiesTopic = "$iothub/twin/PATCH/properties/reported/";
23+
private const string TwinDesiredPropertiesTopic = "$iothub/twin/GET/";
24+
private const string DirectMethodTopic = "$iothub/methods/POST/";
25+
private const string ApiVersion = "2021-04-12";
2526

2627
private readonly string _iotHubName;
2728
private readonly string _deviceId;
@@ -59,61 +60,120 @@ public class DeviceClient : IDisposable
5960
/// <summary>
6061
/// Creates an <see cref="DeviceClient"/> class.
6162
/// </summary>
62-
/// <param name="iotHubName">The Azure IoT name fully qualified (ex: youriothub.azure-devices.net)</param>
63+
/// <param name="iotHubName">The Azure IoT name fully qualified (ex: youriothub.azure-devices.net).</param>
6364
/// <param name="deviceId">The device ID which is the name of your device.</param>
65+
/// <param name="moduleId">The module ID which is attached to the device ID.</param>
6466
/// <param name="sasKey">One of the SAS Key either primary, either secondary.</param>
65-
/// <param name="qosLevel">The default quality level delivery for the MQTT messages, default to the lower quality</param>
66-
/// <param name="azureCert">Azure certificate for the connection to Azure IoT Hub</param>
67-
/// <param name="modelId">Azure Plug and Play model ID</param>
68-
public DeviceClient(string iotHubName, string deviceId, string sasKey, MqttQoSLevel qosLevel = MqttQoSLevel.AtLeastOnce, X509Certificate azureCert = null, string modelId = null)
67+
/// <param name="qosLevel">The default quality level delivery for the MQTT messages, default to the lower quality.</param>
68+
/// <param name="azureCert">Azure certificate for the connection to Azure IoT Hub.</param>
69+
/// <param name="modelId">Azure Plug and Play model ID.</param>
70+
public DeviceClient(string iotHubName, string deviceId, string moduleId, string sasKey, MqttQoSLevel qosLevel = MqttQoSLevel.AtLeastOnce, X509Certificate azureCert = null, string modelId = null)
71+
6972
{
7073
_isCertificate = false;
7174
_clientCert = null;
7275
_privateKey = null;
7376
_iotHubName = iotHubName;
74-
_deviceId = deviceId;
77+
ModelId = modelId;
78+
ModuleId = moduleId;
7579
_sasKey = sasKey;
76-
_telemetryTopic = $"devices/{_deviceId}/messages/events/";
77-
_ioTHubStatus.Status = Status.Disconnected;
78-
_ioTHubStatus.Message = string.Empty;
79-
_deviceMessageTopic = $"devices/{_deviceId}/messages/devicebound/";
8080
QosLevel = qosLevel;
8181
_azureRootCACert = azureCert;
82-
ModelId = modelId;
82+
83+
if (string.IsNullOrEmpty(moduleId))
84+
{
85+
_telemetryTopic = $"devices/{deviceId}/messages/events/";
86+
_deviceId = deviceId;
87+
}
88+
else
89+
{
90+
_telemetryTopic = $"devices/{deviceId}/modules/{ModuleId}/messages/events/";
91+
_deviceId = deviceId + "/" + moduleId;
92+
}
93+
94+
_deviceMessageTopic = $"devices/{_deviceId}/messages/devicebound/";
95+
96+
_ioTHubStatus.Status = Status.Disconnected;
97+
_ioTHubStatus.Message = string.Empty;
8398
}
8499

85100
/// <summary>
86101
/// Creates an <see cref="DeviceClient"/> class.
87102
/// </summary>
88103
/// <param name="iotHubName">Your Azure IoT Hub fully qualified domain name (example: youriothub.azure-devices.net).</param>
89104
/// <param name="deviceId">The device ID (name of your device).</param>
90-
/// <param name="clientCert">The certificate to connect the device (containing both public and private keys). Pass null if you are using the certificate store on the device</param>
105+
/// /// <param name="moduleId">The module ID which is attached to the device ID.</param>
106+
/// <param name="clientCert">The certificate to connect the device (containing both public and private keys). Pass null if you are using the certificate store on the device.</param>
91107
/// <param name="qosLevel">The default quality of assurance level for delivery for the MQTT messages (defaults to the lowest quality).</param>
92-
/// /// <param name="azureCert">Azure certificate for the connection to Azure IoT Hub</param>
93-
/// /// <param name="modelId">Azure Plug and Play model ID</param>
94-
public DeviceClient(string iotHubName, string deviceId, X509Certificate2 clientCert, MqttQoSLevel qosLevel = MqttQoSLevel.AtMostOnce, X509Certificate azureCert = null, string modelId = null)
108+
/// /// <param name="azureCert">Azure certificate for the connection to Azure IoT Hub.</param>
109+
/// /// <param name="modelId">Azure Plug and Play model ID.</param>
110+
public DeviceClient(string iotHubName, string deviceId, string moduleId, X509Certificate2 clientCert, MqttQoSLevel qosLevel = MqttQoSLevel.AtMostOnce, X509Certificate azureCert = null, string modelId = null)
95111
{
96112
_isCertificate = true;
97113
_clientCert = clientCert;
98114
// In case we are using the store, the magic should happen automaticall
99115
_privateKey = _clientCert != null ? Convert.ToBase64String(clientCert.PrivateKey) : null;
100116
_iotHubName = iotHubName;
101-
_deviceId = deviceId;
102-
_sasKey = null;
103-
_telemetryTopic = $"devices/{_deviceId}/messages/events/";
104-
_ioTHubStatus.Status = Status.Disconnected;
105-
_ioTHubStatus.Message = string.Empty;
106-
_deviceMessageTopic = $"devices/{_deviceId}/messages/devicebound/";
117+
ModelId = modelId;
118+
ModuleId = moduleId;
107119
QosLevel = qosLevel;
108120
_azureRootCACert = azureCert;
109-
ModelId = modelId;
121+
122+
if (string.IsNullOrEmpty(moduleId))
123+
{
124+
_telemetryTopic = $"devices/{deviceId}/messages/events/";
125+
_deviceId = deviceId;
126+
}
127+
else
128+
{
129+
_telemetryTopic = $"devices/{deviceId}/modules/{ModuleId}/messages/events/";
130+
_deviceId = deviceId + "/" + moduleId;
131+
}
132+
133+
_deviceMessageTopic = $"devices/{_deviceId}/messages/devicebound/";
134+
135+
_ioTHubStatus.Status = Status.Disconnected;
136+
_ioTHubStatus.Message = string.Empty;
110137
}
111138

112139
/// <summary>
113-
/// Azure Plug and Play model ID
140+
/// Creates an <see cref="DeviceClient"/> class.
141+
/// </summary>
142+
/// <param name="iotHubName">The Azure IoT name fully qualified (ex: youriothub.azure-devices.net).</param>
143+
/// <param name="deviceId">The device ID which is the name of your device.</param>
144+
/// <param name="sasKey">One of the SAS Key either primary, either secondary.</param>
145+
/// <param name="qosLevel">The default quality level delivery for the MQTT messages, default to the lower quality.</param>
146+
/// <param name="azureCert">Azure certificate for the connection to Azure IoT Hub.</param>
147+
/// <param name="modelId">Azure Plug and Play model ID.</param>
148+
public DeviceClient(string iotHubName, string deviceId, string sasKey, MqttQoSLevel qosLevel = MqttQoSLevel.AtLeastOnce, X509Certificate azureCert = null, string modelId = null)
149+
: this(iotHubName, deviceId, string.Empty, sasKey, qosLevel, azureCert, modelId)
150+
{
151+
}
152+
153+
/// <summary>
154+
/// Creates an <see cref="DeviceClient"/> class.
155+
/// </summary>
156+
/// <param name="iotHubName">Your Azure IoT Hub fully qualified domain name (example: youriothub.azure-devices.net).</param>
157+
/// <param name="deviceId">The device ID (name of your device).</param>
158+
/// <param name="clientCert">The certificate to connect the device (containing both public and private keys). Pass null if you are using the certificate store on the device.</param>
159+
/// <param name="qosLevel">The default quality of assurance level for delivery for the MQTT messages (defaults to the lowest quality).</param>
160+
/// /// <param name="azureCert">Azure certificate for the connection to Azure IoT Hub.</param>
161+
/// /// <param name="modelId">Azure Plug and Play model ID.</param>
162+
public DeviceClient(string iotHubName, string deviceId, X509Certificate2 clientCert, MqttQoSLevel qosLevel = MqttQoSLevel.AtMostOnce, X509Certificate azureCert = null, string modelId = null)
163+
: this(iotHubName, deviceId, string.Empty, clientCert, qosLevel, azureCert, modelId)
164+
{
165+
}
166+
167+
/// <summary>
168+
/// Azure Plug and Play model ID.
114169
/// </summary>
115170
public string ModelId { get; internal set; }
116171

172+
/// <summary>
173+
/// The module ID attached to the device ID.
174+
/// </summary>
175+
public string ModuleId { get; internal set; }
176+
117177
/// <summary>
118178
/// The latest Twin received.
119179
/// </summary>
@@ -137,7 +197,7 @@ public DeviceClient(string iotHubName, string deviceId, X509Certificate2 clientC
137197
/// <summary>
138198
/// Open the connection with Azure IoT. This will initiate a connection from the device to the Azure IoT Hub instance.
139199
/// </summary>
140-
/// <returns></returns>
200+
/// <returns>True if open.</returns>
141201
public bool Open()
142202
{
143203
// Creates MQTT Client usinf the default port of 8883 and the TLS 1.2 protocol
@@ -156,7 +216,7 @@ public bool Open()
156216
// event when connection has been dropped
157217
_mqttc.ConnectionClosed += ClientConnectionClosed;
158218

159-
string userName = $"{_iotHubName}/{_deviceId}/api-version=2020-09-30";
219+
string userName = $"{_iotHubName}/{_deviceId}/?api-version={ApiVersion}";
160220
if (!string.IsNullOrEmpty(ModelId))
161221
{
162222
userName += $"&model-id={HttpUtility.UrlEncode(ModelId)}";
@@ -210,7 +270,7 @@ public bool Open()
210270
}
211271

212272
/// <summary>
213-
/// Reconnect to Azure Iot Hub
273+
/// Reconnect to Azure Iot Hub.
214274
/// </summary>
215275
public void Reconnect()
216276
{
@@ -247,7 +307,7 @@ public void Close()
247307
/// <summary>
248308
/// Gets the twin.
249309
/// </summary>
250-
/// <param name="cancellationToken">A cancellation token</param>
310+
/// <param name="cancellationToken">A cancellation token.</param>
251311
/// <returns>The twin.</returns>
252312
/// <remarks>It is strongly recommended to use a cancellation token that can be canceled and manage this on the
253313
/// caller code level. A reasonable time of few seconds is recommended with a retry mechanism.</remarks>
@@ -282,11 +342,11 @@ public bool UpdateReportedProperties(TwinCollection reported, CancellationToken
282342
StatusUpdated?.Invoke(this, new StatusUpdatedEventArgs(_ioTHubStatus));
283343

284344
if (cancellationToken.CanBeCanceled)
285-
{
345+
{
286346
while (!conf.Received && !cancellationToken.IsCancellationRequested)
287347
{
288348
cancellationToken.WaitHandle.WaitOne(200, true);
289-
}
349+
}
290350
}
291351

292352
_waitForConfirmation.Remove(conf);
@@ -332,11 +392,11 @@ public bool SendMessage(string message, CancellationToken cancellationToken = de
332392
_waitForConfirmation.Add(conf);
333393

334394
if (cancellationToken.CanBeCanceled)
335-
{
395+
{
336396
while (!conf.Received && !cancellationToken.IsCancellationRequested)
337397
{
338398
cancellationToken.WaitHandle.WaitOne(200, true);
339-
}
399+
}
340400
}
341401

342402
_waitForConfirmation.Remove(conf);
@@ -398,7 +458,7 @@ private void ClientMqttMsgReceived(object sender, MqttMsgPublishEventArgs e)
398458
const string C9PatternMainStyle = "<<Main>$>g__";
399459
string method = e.Topic.Substring(DirectMethodTopic.Length);
400460
string methodName = method.Substring(0, method.IndexOf('/'));
401-
int rid = Convert.ToInt32(method.Substring(method.IndexOf('=') + 1),16);
461+
int rid = Convert.ToInt32(method.Substring(method.IndexOf('=') + 1), 16);
402462
_ioTHubStatus.Status = Status.DirectMethodCalled;
403463
_ioTHubStatus.Message = $"{method}/{message}";
404464
StatusUpdated?.Invoke(this, new StatusUpdatedEventArgs(_ioTHubStatus));

nanoFramework.Azure.Devices.Client/SHA256.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
namespace System.Security.Cryptography
99
{
10+
/// <summary>
11+
/// Computes the SHA256 hash for the input data.
12+
/// </summary>
1013
public class SHA256
1114
{
1215
// Number used in SHA256 hash function
@@ -28,6 +31,9 @@ public class SHA256
2831
/// <returns></returns>
2932
public static SHA256 Create() => new SHA256();
3033

34+
/// <summary>
35+
/// Initializes a new instance of SHA256.
36+
/// </summary>
3137
protected SHA256()
3238
{ }
3339

0 commit comments

Comments
 (0)