nanoFramework.M2Mqtt
5.1.146
Prefix Reserved
dotnet add package nanoFramework.M2Mqtt --version 5.1.146
NuGet\Install-Package nanoFramework.M2Mqtt -Version 5.1.146
<PackageReference Include="nanoFramework.M2Mqtt" Version="5.1.146" />
paket add nanoFramework.M2Mqtt --version 5.1.146
#r "nuget: nanoFramework.M2Mqtt, 5.1.146"
// Install nanoFramework.M2Mqtt as a Cake Addin #addin nuget:?package=nanoFramework.M2Mqtt&version=5.1.146 // Install nanoFramework.M2Mqtt as a Cake Tool #tool nuget:?package=nanoFramework.M2Mqtt&version=5.1.146
Document Language: English | 简体中文
.NET nanoFramework M2Mqtt
Welcome to the MQTT Client Library for .NET nanoFramework. The current version supports v3.1, v3.1.1 and v5.0.
This is an initial port of the MQTT Client Library M2Mqtt. The original project has an official website here.
Since that time, the MQTT Client had quite some changes and has been adapted to .NET nanoFramework.
Build status
Component | Build Status | NuGet Package |
---|---|---|
nanoFramework.M2Mqtt |
Project Description
M2Mqtt is a MQTT client for Internet of Things and M2M communication.
MQTT, short for Message Queue Telemetry Transport, is a light weight messaging protocol that enables embedded devices with limited resources to perform asynchronous communication on a constrained network.
MQTT protocol is based on publish/subscribe pattern so that a client can subscribe to one or more topics and receive messages that other clients publish on these topics.
This library contains an sample MQTT client that you can use to connect to any MQTT broker.
The binaries are available as a NuGet package.
For all information about MQTT protocol, please visit MQTT official web site. It is recommended to have a good understanding of how MQTT protocol is working to properly use it. The mechanism of Quality of Service is an important one to understand.
Usage
The usage is globally the same whatever version is used. There are some specificities between v3.1.1 and v5.0. The version 5.0 brings more control and additional properties. For convenience, they are all commented with v5.0 only
in the properties comments. If you're using a v5.0 property with the v3.1 or v3.1.1 protocol, they'll just be ignored.
Here is a basic example of creating a v3.1.1 server and connecting to it:
MqttClient mqtt = new MqttClient("test.mosquitto.org", 8883, true, new X509Certificate(CertMosquitto), null, MqttSslProtocols.TLSv1_2);
var ret = mqtt.Connect("nanoTestDevice", true);
if (ret != MqttReasonCode.Success)
{
Debug.WriteLine($"ERROR connecting: {ret}");
mqtt.Disconnect();
return;
}
For the v5.0, you just need to specify the version before the connection:
MqttClient mqtt = new MqttClient("test.mosquitto.org", 8883, true, new X509Certificate(CertMosquitto), null, MqttSslProtocols.TLSv1_2);
mqtt.ProtocolVersion = MqttProtocolVersion.Version_5;
var ret = mqtt.Connect("nanoTestDevice", true);
if (ret != MqttReasonCode.Success)
{
Debug.WriteLine($"ERROR connecting: {ret}");
mqtt.Disconnect();
return;
}
Note: in both example, a specific certificate is needed to connect to the Mosquitto server. You will find it in the sample.
v5.0 specific Authentication flow
The MQTT version 5.0 supports specific Authentication flow. After a Connect, the Authentication mechanism can be used like a challenge request. In this case, you'll have to:
- Make sure you setup v5 as a protocol
- Place the property
IsAuthenticationFlow
to true - Register to the
Authentication
event - Manage the answers accordingly by sending another Authentication message or anything that is needed regarding your case.
Note: the protocol is using the AuthenticationMethod
and AuthenticationData
as properties for this specific mechanism.
Here are examples given by the specifications:
Non-normative example showing a SCRAM challenge
- Client to Server: CONNECT Authentication Method="SCRAM-SHA-1" Authentication Data=client-first-data
- Server to Client: AUTH rc=0x18 Authentication Method="SCRAM-SHA-1" Authentication Data=server-first-data
- Client to Server AUTH rc=0x18 Authentication Method="SCRAM-SHA-1" Authentication Data=client-final-data
- Server to Client CONNACK rc=0 Authentication Method="SCRAM-SHA-1" Authentication Data=server-final-data
Non-normative example showing a Kerberos challenge
- Client to Server CONNECT Authentication Method="GS2-KRB5"
- Server to Client AUTH rc=0x18 Authentication Method="GS2-KRB5"
- Client to Server AUTH rc=0x18 Authentication Method="GS2-KRB5" Authentication Data=initial context token
- Server to Client AUTH rc=0x18 Authentication Method="GS2-KRB5" Authentication Data=reply context token
- Client to Server AUTH rc=0x18 Authentication Method="GS2-KRB5"
- Server to Client CONNACK rc=0 Authentication Method="GS2-KRB5" Authentication Data=outcome of authentication
In those mechanism, the IsConnected
property will be setup only once a Connack with a success code will be received. As those authentication mechanism are specific and user setup, this specific MqttClient
offers the ability to use this mechanism.
Subscribing to events
The MqttClient offers events. You can subscribe to them. As an example, you can get additional information on when the connection is opened with the v5.0 protocol. The below example show what is required to connect to Azure IoT Hub with the MQTT v5.0 protocol enabled:
// Create the client
MqttClient mqtt = new MqttClient(IoTHub, 8883, true, new X509Certificate(CertAzure), null, MqttSslProtocols.TLSv1_2);
// Setup the version
mqtt.ProtocolVersion = MqttProtocolVersion.Version_5;
// Register to events
mqtt.ConnectionOpened += MqttConnectionOpened;
// You can add additional properties
var at = DateTime.UtcNow;
var atString = (at.ToUnixTimeSeconds() * 1000).ToString();
var expiry = at.AddMinutes(40);
var expiryString = (expiry.ToUnixTimeSeconds() * 1000).ToString();
string toSign = $"{IoTHub}\n{DeviceID}\n\n{atString}\n{expiryString}\n";
var hmac = new HMACSHA256(Convert.FromBase64String(Sas));
var sas = hmac.ComputeHash(Encoding.UTF8.GetBytes(toSign));
mqtt.AuthenticationMethod = "SAS";
mqtt.AuthenticationData = sas;
mqtt.UserProperties.Add(new UserProperty("sas-at", atString));
mqtt.UserProperties.Add(new UserProperty("sas-expiry", expiryString));
mqtt.UserProperties.Add(new UserProperty("api-version", "2020-10-01-preview"));
mqtt.UserProperties.Add(new UserProperty("host", IoTHub));
var ret = mqtt.Connect(DeviceID, null, null, false, MqttQoSLevel.AtLeastOnce, false, null, null, true, 60);
// You will have more code here
private static void MqttConnectionOpened(object sender, ConnectionOpenedEventArgs e)
{
Debug.WriteLine($"Connection open");
Debug.WriteLine($" ClientID: {((MqttClient)sender).ClientId}");
Debug.WriteLine($" Assigned client id: {e.Message.AssignedClientIdentifier}");
if (e.Message.AuthenticationData != null) Debug.WriteLine($" Auth data length: {e.Message.AuthenticationData.Length}");
Debug.WriteLine($" Auth method: {e.Message.AuthenticationMethod}");
Debug.WriteLine($" Dup flag: {e.Message.DupFlag}");
Debug.WriteLine($" Max packet size: {e.Message.MaximumPacketSize}");
Debug.WriteLine($" Max QoS: {e.Message.MaximumQoS}");
Debug.WriteLine($" Msg ID: {e.Message.MessageId}");
Debug.WriteLine($" Qos level: {e.Message.QosLevel}");
Debug.WriteLine($" Reason: {e.Message.Reason}");
Debug.WriteLine($" Receive max: {e.Message.ReceiveMaximum}");
Debug.WriteLine($" Rep info: {e.Message.ResponseInformation}");
Debug.WriteLine($" Retain: {e.Message.Retain}");
Debug.WriteLine($" Retain available: {e.Message.RetainAvailable}");
Debug.WriteLine($" Return code: {e.Message.ReturnCode}");
Debug.WriteLine($" Server keep alive: {e.Message.ServerKeepAlive}");
Debug.WriteLine($" Server ref: {e.Message.ServerReference}");
Debug.WriteLine($" Session exp inter: {e.Message.SessionExpiryInterval}");
Debug.WriteLine($" Session present: {e.Message.SessionPresent}");
Debug.WriteLine($" Shared subs available: {e.Message.SharedSubscriptionAvailable}");
Debug.WriteLine($" Shared identifier available: {e.Message.SubscriptionIdentifiersAvailable}");
Debug.WriteLine($" Topic alias max: {e.Message.TopicAliasMaximum}");
Debug.WriteLine($" Num user props: {e.Message.UserProperties.Count}");
foreach (UserProperty prop in e.Message.UserProperties)
{
Debug.WriteLine($" Key : {prop.Name}");
Debug.WriteLine($" Value: {prop.Value}");
}
Debug.WriteLine($" Wildcard available: {e.Message.WildcardSubscriptionAvailable}");
}
Example
The M2Mqtt library provides a main class MqttClient
that represents the MQTT client to connect to a broker. You can connect to the broker providing its IP address or host name and optionally some parameters related to MQTT protocol.
After connecting to the broker you can use Publish()
method to publish a message to a topic and Subscribe()
method to subscribe to a topic and receive message published on it. The MqttClient
class is events based so that you receive an event when a message is published to a topic you subscribed to. You can receive event when a message publishing is complete, you have subscribed or unsubscribed to a topic.
Following an example of client subscriber to a topic :
string MQTT_BROKER_ADDRESS = "192.168.1.2";
// create client instance
MqttClient client = new MqttClient(IPAddress.Parse(MQTT_BROKER_ADDRESS));
// register to message received
client.MqttMsgPublishReceived += client_MqttMsgPublishReceived;
string clientId = Guid.NewGuid().ToString();
client.Connect(clientId);
// subscribe to the topic "/home/temperature" with QoS 2
client.Subscribe(new string[] { "/home/temperature" }, new MqttQoSLevel[] { MqttMsgBase.ExactlyOnce });
// You can add some code here
static void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
{
// handle message received
}
Following an example of client publisher to a topic :
string MQTT_BROKER_ADDRESS = "192.168.1.2";
// create client instance
MqttClient client = new MqttClient(IPAddress.Parse(MQTT_BROKER_ADDRESS));
string clientId = Guid.NewGuid().ToString();
client.Connect(clientId);
string strValue = Convert.ToString(value);
// publish a message on "/home/temperature" topic with QoS 2
client.Publish("/home/temperature", Encoding.UTF8.GetBytes(strValue), MqttQoSLevel.ExactlyOnce, false);
// More code goes here
Avoiding certificate check
In some cases, it can be handy to avoid the certificate checks when connecting through TLS connection. While this scenario is not recommended, you can adjust for it like this:
// You can specify no certificate at all
MqttClient mqtt = new MqttClient(IoTHub, 8883, true, null, null, MqttSslProtocols.TLSv1_2);
// And you have to setup the ValidateServerCertificate to false
mqtt.Settings.ValidateServerCertificate = false;
string clientId = Guid.NewGuid().ToString();
client.Connect(clientId);
Feedback and documentation
For documentation, providing feedback, issues and finding out how to contribute please refer to the Home repo.
Join our Discord community here.
Credits
The list of contributors to this project can be found at CONTRIBUTORS. This library was created and maintained by Paolo Patierno and it's part of the Eclipse Project.
License
The nanoFramework Class Libraries are licensed under the MIT license.
Code of Conduct
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behaviour in our community. For more information see the .NET Foundation Code of Conduct.
.NET Foundation
This project is supported by the .NET Foundation.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET Framework | net is compatible. |
-
- nanoFramework.CoreLibrary (>= 1.15.5)
- nanoFramework.Runtime.Events (>= 1.11.18)
- nanoFramework.Runtime.Native (>= 1.7.1)
- nanoFramework.System.Collections (>= 1.5.45)
- nanoFramework.System.Net (>= 1.11.1)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on nanoFramework.M2Mqtt:
Package | Downloads |
---|---|
nanoFramework.Azure.Devices.Client
This package includes the .NET nanoFramework.Azure.Devices.Client assembly for .NET nanoFramework C# projects. This is an SDK for Azure IoT Hub using MQTT broker. |
|
nanoFramework.Aws.IoTCore.Devices
This package includes the .NET nanoFramework.Aws.IoTCore.Devices assembly for nanoFramework C# projects. This is an SDK for Aws IoTCore. |
|
MakoIoT.Device.Services.Mqtt
MQTT library for message bus of MAKO-IoT |
|
MakoIoT.Device.Services.AzureIotHub
AzureIoTHub bus provider for MAKO-IoT |
|
DevBot9.NanoFramework.Homie.Utilities
Homie implementation for nanoFramework. |
GitHub repositories (2)
Showing the top 2 popular GitHub repositories that depend on nanoFramework.M2Mqtt:
Repository | Stars |
---|---|
dotnet/samples
Sample code referenced by the .NET documentation
|
|
nanoframework/Samples
🍬 Code samples from the nanoFramework team used in testing, proof of concepts and other explorational endeavours
|
Version | Downloads | Last updated |
---|---|---|
5.1.146 | 961 | 10/11/2024 |
5.1.145 | 562 | 9/26/2024 |
5.1.138 | 887 | 7/30/2024 |
5.1.130 | 1,582 | 5/13/2024 |
5.1.128 | 324 | 5/10/2024 |
5.1.126 | 377 | 4/30/2024 |
5.1.123 | 498 | 4/9/2024 |
5.1.120 | 468 | 4/3/2024 |
5.1.116 | 937 | 1/29/2024 |
5.1.114 | 302 | 1/26/2024 |
5.1.112 | 344 | 1/24/2024 |
5.1.110 | 349 | 1/21/2024 |
5.1.107 | 1,784 | 11/10/2023 |
5.1.103 | 246 | 11/9/2023 |
5.1.101 | 333 | 11/8/2023 |
5.1.99 | 126 | 11/8/2023 |
5.1.96 | 801 | 10/10/2023 |
5.1.94 | 1,151 | 8/28/2023 |
5.1.92 | 281 | 8/28/2023 |
5.1.90 | 299 | 8/28/2023 |
5.1.79 | 3,372 | 1/14/2023 |
5.1.75 | 921 | 12/28/2022 |
5.1.70 | 697 | 12/27/2022 |
5.1.68 | 385 | 12/22/2022 |
5.1.61 | 1,011 | 11/24/2022 |
5.1.59 | 738 | 11/22/2022 |
5.1.53 | 1,298 | 11/4/2022 |
5.1.48 | 909 | 10/28/2022 |
5.1.46 | 406 | 10/28/2022 |
5.1.44 | 2,038 | 10/26/2022 |
5.1.42 | 659 | 10/25/2022 |
5.1.40 | 412 | 10/25/2022 |
5.1.34 | 3,317 | 10/14/2022 |
5.1.27 | 1,022 | 10/10/2022 |
5.1.25 | 1,226 | 10/8/2022 |
5.1.22 | 1,784 | 9/22/2022 |
5.1.20 | 1,046 | 9/22/2022 |
5.1.18 | 1,656 | 9/15/2022 |
5.1.16 | 1,029 | 9/15/2022 |
5.1.11 | 2,477 | 8/4/2022 |
5.1.9 | 773 | 8/4/2022 |
5.1.6 | 995 | 8/4/2022 |
5.1.4 | 702 | 8/3/2022 |
5.1.2 | 1,007 | 8/3/2022 |
5.0.2.28 | 715 | 8/3/2022 |
5.0.2.26 | 2,978 | 6/13/2022 |
5.0.2.24 | 1,364 | 6/8/2022 |
5.0.2.22 | 745 | 6/8/2022 |
5.0.2.17 | 1,143 | 5/26/2022 |
5.0.2.15 | 1,671 | 5/18/2022 |
5.0.2.13 | 1,414 | 5/3/2022 |
5.0.2 | 1,971 | 3/28/2022 |
5.0.2-preview.100 | 139 | 3/28/2022 |
5.0.2-preview.98 | 153 | 3/28/2022 |
5.0.2-preview.96 | 139 | 3/28/2022 |
5.0.2-preview.94 | 155 | 3/28/2022 |
5.0.2-preview.91 | 192 | 3/17/2022 |
5.0.2-preview.89 | 145 | 3/17/2022 |
5.0.2-preview.87 | 177 | 3/15/2022 |
5.0.2-preview.85 | 138 | 3/15/2022 |
5.0.2-preview.83 | 159 | 3/15/2022 |
5.0.2-preview.80 | 450 | 2/17/2022 |
5.0.2-preview.78 | 183 | 2/8/2022 |
5.0.2-preview.76 | 210 | 2/4/2022 |
5.0.2-preview.74 | 155 | 2/4/2022 |
5.0.2-preview.72 | 190 | 1/28/2022 |
5.0.2-preview.70 | 158 | 1/28/2022 |
5.0.2-preview.68 | 167 | 1/28/2022 |
5.0.2-preview.65 | 162 | 1/25/2022 |
5.0.2-preview.63 | 159 | 1/21/2022 |
5.0.2-preview.61 | 153 | 1/21/2022 |
5.0.2-preview.59 | 152 | 1/21/2022 |
5.0.2-preview.57 | 150 | 1/21/2022 |
5.0.2-preview.55 | 177 | 1/14/2022 |
5.0.2-preview.53 | 152 | 1/14/2022 |
5.0.2-preview.49 | 218 | 1/5/2022 |
5.0.2-preview.47 | 152 | 1/5/2022 |
5.0.2-preview.46 | 197 | 1/4/2022 |
5.0.2-preview.45 | 180 | 12/31/2021 |
5.0.2-preview.44 | 152 | 12/31/2021 |
5.0.2-preview.43 | 163 | 12/29/2021 |
5.0.2-preview.41 | 198 | 12/17/2021 |
5.0.2-preview.39 | 320 | 12/4/2021 |
5.0.2-preview.37 | 177 | 12/3/2021 |
5.0.2-preview.35 | 182 | 12/3/2021 |
5.0.2-preview.33 | 170 | 12/2/2021 |
5.0.2-preview.31 | 173 | 12/2/2021 |
5.0.2-preview.29 | 173 | 12/1/2021 |
5.0.2-preview.26 | 369 | 11/14/2021 |
5.0.2-preview.24 | 280 | 11/13/2021 |
5.0.2-preview.20 | 214 | 11/12/2021 |
5.0.2-preview.18 | 195 | 10/23/2021 |
5.0.2-preview.15 | 287 | 10/19/2021 |
5.0.2-preview.12 | 176 | 10/18/2021 |
5.0.2-preview.9 | 915 | 9/17/2021 |
5.0.2-preview.6 | 205 | 9/16/2021 |
5.0.2-preview.1 | 641 | 8/2/2021 |
5.0.1 | 971 | 8/2/2021 |
5.0.1-preview.5 | 245 | 7/27/2021 |
5.0.1-preview.4 | 201 | 7/26/2021 |
5.0.1-preview.3 | 258 | 7/17/2021 |
5.0.0 | 635 | 7/16/2021 |
5.0.0-preview.4 | 150 | 7/16/2021 |
5.0.0-preview.3 | 157 | 7/16/2021 |
5.0.0-preview.2 | 883 | 7/3/2021 |
4.6.1-preview.90 | 231 | 7/3/2021 |
4.6.1-preview.89 | 227 | 6/20/2021 |
4.6.1-preview.88 | 270 | 6/19/2021 |
4.6.1-preview.86 | 268 | 6/19/2021 |
4.6.1-preview.85 | 177 | 6/18/2021 |
4.6.1-preview.84 | 165 | 6/17/2021 |
4.6.1-preview.83 | 204 | 6/8/2021 |
4.6.1-preview.82 | 177 | 6/7/2021 |
4.6.1-preview.80 | 173 | 6/7/2021 |
4.6.1-preview.79 | 202 | 6/7/2021 |
4.6.1-preview.78 | 201 | 6/7/2021 |
4.6.1-preview.77 | 207 | 6/6/2021 |
4.6.1-preview.76 | 231 | 6/1/2021 |
4.6.1-preview.75 | 202 | 5/31/2021 |
4.6.1-preview.74 | 208 | 5/30/2021 |
4.6.1-preview.73 | 180 | 5/26/2021 |
4.6.1-preview.72 | 194 | 5/22/2021 |
4.6.1-preview.71 | 228 | 5/21/2021 |
4.6.1-preview.70 | 178 | 5/19/2021 |
4.6.1-preview.69 | 177 | 5/19/2021 |
4.6.1-preview.68 | 189 | 5/19/2021 |
4.6.1-preview.67 | 170 | 5/15/2021 |
4.6.1-preview.66 | 161 | 5/15/2021 |
4.6.1-preview.65 | 171 | 5/13/2021 |
4.6.1-preview.64 | 177 | 5/13/2021 |
4.6.1-preview.63 | 197 | 5/11/2021 |
4.6.1-preview.62 | 175 | 5/6/2021 |
4.6.1-preview.61 | 163 | 5/6/2021 |
4.6.1-preview.60 | 158 | 5/5/2021 |
4.6.1-preview.59 | 155 | 5/5/2021 |
4.6.1-preview.58 | 156 | 5/5/2021 |
4.6.1-preview.57 | 156 | 5/5/2021 |
4.6.1-preview.56 | 201 | 4/10/2021 |
4.6.1-preview.54 | 172 | 3/31/2021 |
4.6.1-preview.52 | 181 | 3/31/2021 |
4.6.1-preview.51 | 173 | 3/29/2021 |
4.6.1-preview.28 | 526 | 12/7/2020 |
4.6.1-preview.27 | 317 | 10/21/2020 |
4.6.1-preview.25 | 301 | 10/21/2020 |
4.6.1-preview.24 | 289 | 10/21/2020 |
4.6.1-preview.22 | 281 | 10/20/2020 |
4.6.1-preview.21 | 339 | 10/1/2020 |
4.6.1-preview.19 | 332 | 10/1/2020 |
4.6.1-preview.18 | 258 | 9/30/2020 |
4.6.1-preview.17 | 276 | 9/30/2020 |
4.6.1-preview.16 | 299 | 9/27/2020 |
4.6.1-preview.15 | 292 | 9/27/2020 |
4.6.1-preview.13 | 283 | 9/19/2020 |
4.6.1-preview.12 | 285 | 9/19/2020 |
4.6.1-preview.11 | 288 | 9/19/2020 |
4.6.1-preview.10 | 328 | 7/2/2020 |
4.6.0-preview.7 | 291 | 6/17/2020 |
4.4.1-preview.5 | 294 | 6/12/2020 |
4.4.1-preview.2 | 320 | 6/11/2020 |
4.4.1-preview.1 | 301 | 6/5/2020 |
4.4.0-preview.30 | 471 | 6/3/2020 |
4.4.0-preview.29 | 298 | 6/3/2020 |
4.4.0-preview.28 | 316 | 6/1/2020 |
4.4.0-preview.27 | 390 | 5/31/2020 |
4.4.0-preview.26 | 296 | 5/29/2020 |
4.4.0-preview.25 | 302 | 5/8/2020 |
4.4.0-preview.24 | 296 | 5/8/2020 |
4.4.0-preview.23 | 294 | 4/27/2020 |
4.4.0-preview.22 | 322 | 4/16/2020 |
4.4.0-preview.20 | 304 | 4/16/2020 |
4.4.0-preview.19 | 281 | 4/14/2020 |
4.4.0-preview.18 | 283 | 4/14/2020 |
4.4.0-preview.17 | 270 | 4/14/2020 |
4.4.0-preview.16 | 281 | 3/25/2020 |
4.4.0-preview.10 | 378 | 11/14/2019 |
4.4.0-preview.9 | 300 | 11/12/2019 |
4.4.0-preview.8 | 292 | 11/8/2019 |
4.4.0-preview.7 | 293 | 11/7/2019 |
4.4.0-preview.5 | 309 | 11/5/2019 |
4.4.0-preview.4 | 317 | 10/30/2019 |
4.4.0-preview.3 | 326 | 10/30/2019 |
4.3.1 | 875 | 10/15/2019 |
4.3.1-preview.17 | 306 | 10/15/2019 |
4.3.1-preview.16 | 301 | 9/24/2019 |
4.3.1-preview.15 | 309 | 9/24/2019 |
4.3.1-preview.14 | 315 | 9/11/2019 |
4.3.1-preview.13 | 309 | 9/11/2019 |
4.3.1-preview.10 | 308 | 7/20/2019 |
4.3.1-preview.6 | 410 | 7/15/2019 |
4.3.1-preview.3 | 321 | 7/12/2019 |
4.3.1-preview.1 | 398 | 7/10/2019 |