Use IoT Plug and Play models in an IoT solution

This article describes how, in an IoT solution, you can identify model ID of an IoT Plug and Play device and then retrieve its model definition.

There are two broad categories of IoT solution:

  • A purpose-built solution works with a known set of models for the IoT Plug and Play devices that connect to the solution. You use these models when you develop the solution.

  • A model-driven solution works with the model of any IoT Plug and Play device. Building a model-driven solution is more complex, but the benefit is that your solution works with any devices that are added in the future. A model-driven IoT solution retrieves a model and uses it to determine the telemetry, properties, and commands the device implements.

To use an IoT Plug and Play model, an IoT solution:

  1. Identifies the model ID of the model implemented by the IoT Plug and Play device, module, or IoT Edge module connected to the solution.

  2. Uses the model ID to retrieve the model definition of the connected device from a model repository or custom store.

Identify model ID

When an IoT Plug and Play device connects to IoT Hub, it registers the model ID of the model it implements with IoT Hub.

IoT Hub notifies the solution with the device model ID as part of the device connection flow.

A solution can get the model ID of the IoT Plug and Play device by using one of the following three methods:

Get Device Twin API

The solution can use the Get Device Twin API to retrieve model ID of the IoT Plug and Play device.

Tip

For modules and IoT Edge modules, use ModuleClient.getTwin.

In the following device twin response snippet, modelId contains the model ID of an IoT Plug and Play device:

{
    "deviceId": "sample-device",
    "etag": "AAAAAAAAAAc=",
    "deviceEtag": "NTk0ODUyODgx",
    "status": "enabled",
    "statusUpdateTime": "0001-01-01T00:00:00Z",
    "connectionState": "Disconnected",
    "lastActivityTime": "2020-07-17T06:12:26.8402249Z",
    "cloudToDeviceMessageCount": 0,
    "authenticationType": "sas",
    "x509Thumbprint": {
        "primaryThumbprint": null,
        "secondaryThumbprint": null
    },
    "modelId": "dtmi:com:example:TemperatureController;1",
    "version": 15,
    "properties": {...}
    }
}

Get Digital Twin API

The solution can use the Get Digital Twin API to retrieve the model ID of the model implemented by the IoT Plug and Play device.

In the following digital twin response snippet, $metadata.$model contains the model ID of an IoT Plug and Play device:

{
    "$dtId": "sample-device",
    "$metadata": {
        "$model": "dtmi:com:example:TemperatureController;1",
        "serialNumber": {
            "lastUpdateTime": "2020-07-17T06:10:31.9609233Z"
        }
    }
}

Digital twin change event notification

A device connection results in a Digital Twin change event notification. A solution needs to subscribe to this event notification. To learn how to enable routing for digital twin events, see Use IoT Hub message routing to send device-to-cloud messages to different endpoints.

The solution can use the event shown in the following snippet to learn about the IoT Plug and Play device that's connecting and get its model ID:

iothub-connection-device-id:sample-device
iothub-enqueuedtime:7/22/2020 8:02:27 PM
iothub-message-source:digitalTwinChangeEvents
correlation-id:100f322dc2c5
content-type:application/json-patch+json
content-encoding:utf-8
[
  {
    "op": "replace",
    "path": "/$metadata/$model",
    "value": "dtmi:com:example:TemperatureController;1"
  }
]

Retrieve a model definition

A solution uses model ID identified above to retrieve the corresponding model definition.

A solution can get the model definition by using one of the following options:

Model repository

Solutions can retrieve DTDL models from the device model repository (DMR). The DMR is a public repository, hosted by Microsoft, that contains a collection of curated DTDL models. The public device models stored in the DMR are available for everyone to consume and integrate in their applications from the public endpoint https://devicemodels.azure.com.

After you identify the model ID for a new device connection, follow these steps:

  1. Retrieve the model definition using the model ID from the model repository. For more information, see Resolve models.

  2. Using the model definition of the connected device, you can enumerate the capabilities of the device.

  3. Using the enumerated capabilities of the device, you can enable users to interact with the device.

Resolve models

The DMR conventions include other artifacts for simplifying consumption of hosted models. These features are optional for custom or private repositories.

To programmatically access the public DTDL models in the DMR, you can use the ModelsRepositoryClient available in the NuGet package Azure.IoT.ModelsRepository. This client is configured by default to query the public DMR available at devicemodels.azure.com and can be configured to any custom repository.

The client accepts a DTMI as input and returns a dictionary with all required interfaces:

using Azure.IoT.ModelsRepository;

var client = new ModelsRepositoryClient();
ModelResult models = client.GetModel("dtmi:com:example:TemperatureController;1");
models.Content.Keys.ToList().ForEach(k => Console.WriteLine(k));

The expected output displays the DTMI of the three interfaces found in the dependency chain:

dtmi:com:example:TemperatureController;1
dtmi:com:example:Thermostat;1
dtmi:azure:DeviceManagement:DeviceInformation;1

The ModelsRepositoryClient can be configured to query a custom DMR -available through http(s)- and to specify the dependency resolution by using the ModelDependencyResolution flag:

  • Disabled. Returns the specified interface only, without any dependency.
  • Enabled. Returns all the interfaces in the dependency chain

Tip

Custom repositories might not expose the .expanded.json file. When this file isn't available, the client will fallback to process each dependency locally.

The following sample code shows how to initialize the ModelsRepositoryClient by using a custom repository base URL, in this case using the raw URLs from the GitHub API without using the expanded form since it's not available in the raw endpoint. The AzureEventSourceListener is initialized to inspect the HTTP request performed by the client:

using AzureEventSourceListener listener = AzureEventSourceListener.CreateConsoleLogger();

var client = new ModelsRepositoryClient(
    new Uri("https://raw.githubusercontent.com/Azure/iot-plugandplay-models/main"));

ModelResult model = await client.GetModelAsync(
    "dtmi:com:example:TemperatureController;1", 
    dependencyResolution: ModelDependencyResolution.Enabled);

model.Content.Keys.ToList().ForEach(k => Console.WriteLine(k));

There are more samples available in the Azure SDK GitHub repository: Azure.Iot.ModelsRepository/samples.

Custom store

Solutions can store these model definitions in a local file system, in a public file store, or use a custom implementation.

After you identify the model ID for a new device connection, follow these steps:

  1. Retrieve the model definition using the model ID from your custom store.

  2. Using the model definition of the connected device, you can enumerate the capabilities of the device.

  3. Using the enumerated capabilities of the device, you can enable users to interact with the device.

Next steps

Now that you've learned how to integrate IoT Plug and Play models in an IoT solution, some suggested next steps are: