IoT Plug and Play eszköz fejlesztői útmutatója

Az IoT Plug and Play lehetővé teszi olyan IoT-eszközök létrehozását, amelyek az Azure IoT-alkalmazásokban hirdetik meg a képességeiket. Az IoT-Plug and Play-eszközök nem igényelnek manuális konfigurálást, amikor az ügyfél csatlakoztatja őket az IoT-Plug and Play-kompatibilis alkalmazásokhoz.

Az IoT-eszközök implementálhatók közvetlenül, használhatnak modulokat, vagy használhatnak IoT Edge modulokat.

Ez az útmutató az IoT Plug and Play konvencióinak megfelelő eszköz, modul vagy IoT Edge-modul létrehozásához szükséges alapvető lépéseket ismerteti.

IoT-Plug and Play eszköz, modul vagy IoT Edge modul létrehozásához kövesse az alábbi lépéseket:

  1. Győződjön meg arról, hogy az eszköz az MQTT vagy az MQTT over WebSockets protokollt használja a Azure IoT Hub való csatlakozáshoz.
  2. Hozzon létre egy Digital Twins Definition Language (DTDL) modellt az eszköz leírásához. További információ: Az IoT-Plug and Play modellek összetevőinek ismertetése.
  3. Frissítse az eszközt vagy modult, hogy bejelentse az model-id eszközkapcsolat részeként.
  4. Telemetria, tulajdonságok és parancsok implementálása az IoT Plug and Play konvenciók használatával

Ha az eszköz vagy a modul implementációja elkészült, az Azure IoT Explorerrel ellenőrizze, hogy az eszköz megfelel-e az IoT Plug and Play konvencióinak.

Modellazonosító bejelentése

A modellazonosító bejelentéséhez az eszköznek szerepelnie kell a kapcsolati adatokban:

static const char g_ThermostatModelId[] = "dtmi:com:example:Thermostat;1";
IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceHandle = NULL;
deviceHandle = CreateDeviceClientLLHandle();
iothubResult = IoTHubDeviceClient_LL_SetOption(
    deviceHandle, OPTION_MODEL_ID, g_ThermostatModelId);

Tipp

Modulok és IoT Edge helyett használja IoTHubModuleClient_LL a IoTHubDeviceClient_LL.

Tipp

Ez az egyetlen alkalom, amikor egy eszköz beállíthat modellazonosítót, és nem frissíthető az eszköz csatlakoztatása után.

DPS-adattartalom

A Device Provisioning Service -t (DPS) használó eszközök a következő JSON-hasznos adatokkal tartalmazhatják a modelId kiépítési folyamat során használni kívánt eszközöket:

{
    "modelId" : "dtmi:com:example:Thermostat;1"
}

Összetevők használata

Az IoT-Plug and Play modellek összetevőinek ismertetésében leírtaknak megfelelően az eszközkészítőknek el kell dönteniük, hogy összetevőket szeretnének-e használni az eszközeik leírásához. Összetevők használatakor az eszközöknek az alábbi szakaszokban leírt szabályokat kell követnie:

Telemetria

Az alapértelmezett összetevőkhöz nem szükséges speciális tulajdonságot hozzáadni a telemetriai üzenethez.

Beágyazott összetevők használatakor az eszközöknek egy üzenettulajdonságot kell beállítaniuk az összetevő nevével:

void PnP_ThermostatComponent_SendTelemetry(
    PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle,
    IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClientLL)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;
    IOTHUB_MESSAGE_HANDLE messageHandle = NULL;
    IOTHUB_CLIENT_RESULT iothubResult;

    char temperatureStringBuffer[32];

    if (snprintf(
        temperatureStringBuffer,
        sizeof(temperatureStringBuffer),
        g_temperatureTelemetryBodyFormat,
        pnpThermostatComponent->currentTemperature) < 0)
    {
        LogError("snprintf of current temperature telemetry failed");
    }
    else if ((messageHandle = PnP_CreateTelemetryMessageHandle(
        pnpThermostatComponent->componentName, temperatureStringBuffer)) == NULL)
    {
        LogError("Unable to create telemetry message");
    }
    else if ((iothubResult = IoTHubDeviceClient_LL_SendEventAsync(
        deviceClientLL, messageHandle, NULL, NULL)) != IOTHUB_CLIENT_OK)
    {
        LogError("Unable to send telemetry message, error=%d", iothubResult);
    }

    IoTHubMessage_Destroy(messageHandle);
}

// ...

PnP_ThermostatComponent_SendTelemetry(g_thermostatHandle1, deviceClient);

Csak olvasható tulajdonságok

Az alapértelmezett összetevőből származó tulajdonság jelentéséhez nincs szükség speciális szerkezetre:

static const char g_maxTemperatureSinceRebootFormat[] = "{\"maxTempSinceLastReboot\":%.2f}";

char maxTemperatureSinceRebootProperty[256];

snprintf(
    maxTemperatureSinceRebootProperty,
    sizeof(maxTemperatureSinceRebootProperty),
    g_maxTemperatureSinceRebootFormat,
    38.7);

IOTHUB_CLIENT_RESULT iothubClientResult = IoTHubDeviceClient_LL_SendReportedState(
    deviceClientLL,
    (const unsigned char*)maxTemperatureSinceRebootProperty,
    strlen(maxTemperatureSinceRebootProperty), NULL, NULL));

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
      "maxTempSinceLastReboot" : 38.7
  }
}

Beágyazott összetevők használatakor a tulajdonságokat az összetevő nevében kell létrehozni, és tartalmaznia kell egy jelölőt:

STRING_HANDLE PnP_CreateReportedProperty(
    const char* componentName,
    const char* propertyName,
    const char* propertyValue
)
{
    STRING_HANDLE jsonToSend;

    if (componentName == NULL) 
    {
        jsonToSend = STRING_construct_sprintf(
            "{\"%s\":%s}",
            propertyName, propertyValue);
    }
    else 
    {
       jsonToSend = STRING_construct_sprintf(
            "{\"""%s\":{\"__t\":\"c\",\"%s\":%s}}",
            componentName, propertyName, propertyValue);
    }

    if (jsonToSend == NULL)
    {
        LogError("Unable to allocate JSON buffer");
    }

    return jsonToSend;
}

void PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(
    PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle,
    IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClientLL)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent =
        (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;
    char maximumTemperatureAsString[32];
    IOTHUB_CLIENT_RESULT iothubClientResult;
    STRING_HANDLE jsonToSend = NULL;

    if (snprintf(maximumTemperatureAsString, sizeof(maximumTemperatureAsString),
        "%.2f", pnpThermostatComponent->maxTemperature) < 0)
    {
        LogError("Unable to create max temp since last reboot string for reporting result");
    }
    else if ((jsonToSend = PnP_CreateReportedProperty(
                pnpThermostatComponent->componentName,
                g_maxTempSinceLastRebootPropertyName,
                maximumTemperatureAsString)) == NULL)
    {
        LogError("Unable to build max temp since last reboot property");
    }
    else
    {
        const char* jsonToSendStr = STRING_c_str(jsonToSend);
        size_t jsonToSendStrLen = strlen(jsonToSendStr);

        if ((iothubClientResult = IoTHubDeviceClient_LL_SendReportedState(
                deviceClientLL,
                (const unsigned char*)jsonToSendStr,
                jsonToSendStrLen, NULL, NULL)) != IOTHUB_CLIENT_OK)
        {
            LogError("Unable to send reported state, error=%d", iothubClientResult);
        }
        else
        {
            LogInfo("Sending maximumTemperatureSinceLastReboot property to IoTHub for component=%s",
                pnpThermostatComponent->componentName);
        }
    }

    STRING_delete(jsonToSend);
}

// ...

PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(g_thermostatHandle1, deviceClient);

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "thermostat1" : {  
      "__t" : "c",  
      "maxTemperature" : 38.7
     }
  }
}

Írható tulajdonságok

Ezeket a tulajdonságokat az eszköz állíthatja be, vagy a megoldás frissítheti. Ha a megoldás frissít egy tulajdonságot, az ügyfél értesítést kap visszahívásként a vagy ModuleClienta DeviceClient . Az IoT Plug and Play konvenciók követéséhez az eszköznek tájékoztatnia kell a szolgáltatást a tulajdonság sikeres fogadásáról.

Ha a tulajdonság típusa, Objecta szolgáltatásnak akkor is teljes objektumot kell küldenie az eszköznek, ha csak az objektum mezőinek egy részét frissíti. Az eszköz által küldött nyugtázás teljes objektum is lehet.

Írható tulajdonság jelentése

Amikor egy eszköz írható tulajdonságot jelent, annak tartalmaznia kell a ack konvenciókban meghatározott értékeket.

Írható tulajdonság jelentése az alapértelmezett összetevőből:

IOTHUB_CLIENT_RESULT iothubClientResult;
char targetTemperatureResponseProperty[256];

snprintf(
    targetTemperatureResponseProperty,
    sizeof(targetTemperatureResponseProperty),
    "{\"targetTemperature\":{\"value\":%.2f,\"ac\":%d,\"av\":%d,\"ad\":\"%s\"}}",
    23.2, 200, 3, "Successfully updated target temperature");

iothubClientResult = IoTHubDeviceClient_LL_SendReportedState(
    deviceClientLL,
    (const unsigned char*)targetTemperatureResponseProperty,
    strlen(targetTemperatureResponseProperty), NULL, NULL);

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "Successfully updated target temperature"
      }
  }
}

Ha írható tulajdonságot szeretne jelenteni egy beágyazott összetevőből, az ikerpéldánynak tartalmaznia kell egy jelölőt:

STRING_HANDLE PnP_CreateReportedPropertyWithStatus(const char* componentName,
    const char* propertyName, const char* propertyValue,
    int result, const char* description, int ackVersion
)
{
    STRING_HANDLE jsonToSend;

    if (componentName == NULL) 
    {
        jsonToSend = STRING_construct_sprintf(
            "{\"%s\":{\"value\":%s,\"ac\":%d,\"ad\":\"%s\",\"av\":%d}}",
            propertyName, propertyValue,
            result, description, ackVersion);
    }
    else
    {
       jsonToSend = STRING_construct_sprintf(
            "{\"""%s\":{\"__t\":\"c\",\"%s\":{\"value\":%s,\"ac\":%d,\"ad\":\"%s\",\"av\":%d}}}",
            componentName, propertyName, propertyValue,
            result, description, ackVersion);
    }

    if (jsonToSend == NULL)
    {
        LogError("Unable to allocate JSON buffer");
    }

    return jsonToSend;
}

// ...

char targetTemperatureAsString[32];
IOTHUB_CLIENT_RESULT iothubClientResult;
STRING_HANDLE jsonToSend = NULL;

snprintf(targetTemperatureAsString,
    sizeof(targetTemperatureAsString),
    "%.2f",
    23.2);
jsonToSend = PnP_CreateReportedPropertyWithStatus(
    "thermostat1",
    "targetTemperature",
    targetTemperatureAsString,
    200,
    "complete",
    3);

const char* jsonToSendStr = STRING_c_str(jsonToSend);
size_t jsonToSendStrLen = strlen(jsonToSendStr);

iothubClientResult = IoTHubDeviceClient_LL_SendReportedState(
    deviceClientLL,
    (const unsigned char*)jsonToSendStr,
    jsonToSendStrLen, NULL, NULL);

STRING_delete(jsonToSend);

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "thermostat1": {
      "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
    }
  }
}

Feliratkozás a kívánt tulajdonságfrissítésre

A szolgáltatások frissíthetik a kívánt tulajdonságokat, amelyek értesítést aktiválnak a csatlakoztatott eszközökön. Ez az értesítés tartalmazza a frissített kívánt tulajdonságokat, beleértve a frissítést azonosító verziószámot. Az eszközöknek tartalmazniuk kell ezt a verziószámot a ack szolgáltatásnak visszaküldött üzenetben.

Az alapértelmezett összetevő az egyetlen tulajdonságot látja, és a kapott verzióval hozza létre a jelentést ack :

static void Thermostat_DeviceTwinCallback(
    DEVICE_TWIN_UPDATE_STATE updateState,
    const unsigned char* payload,
    size_t size,
    void* userContextCallback)
{
    // The device handle associated with this request is passed as the context,
    // since we will need to send reported events back.
    IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClientLL =
        (IOTHUB_DEVICE_CLIENT_LL_HANDLE)userContextCallback;

    char* jsonStr = NULL;
    JSON_Value* rootValue = NULL;
    JSON_Object* desiredObject;
    JSON_Value* versionValue = NULL;
    JSON_Value* targetTemperatureValue = NULL;

    jsonStr = CopyTwinPayloadToString(payload, size));
    rootValue = json_parse_string(jsonStr));
    desiredObject = GetDesiredJson(updateState, rootValue));
    targetTemperatureValue = json_object_get_value(desiredObject, "targetTemperature"));
    versionValue = json_object_get_value(desiredObject, "$version"));
    json_value_get_type(versionValue);
    json_value_get_type(targetTemperatureValue);

    double targetTemperature = json_value_get_number(targetTemperatureValue);
    int version = (int)json_value_get_number(versionValue);

    // ...

    // The device needs to let the service know that it has received the targetTemperature desired property.
    SendTargetTemperatureReport(deviceClientLL, targetTemperature, 200, version, "Successfully updated target temperature");

    json_value_free(rootValue);
    free(jsonStr);
}

// ...

IOTHUB_CLIENT_RESULT iothubResult;
iothubResult = IoTHubDeviceClient_LL_SetDeviceTwinCallback(
    deviceHandle, Thermostat_DeviceTwinCallback, (void*)deviceHandle))

A beágyazott összetevők ikereszköze a következő módon jeleníti meg a kívánt és jelentett szakaszokat:

{
  "desired" : {
    "targetTemperature": 23.2,
    "$version" : 3
  },
  "reported": {
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "Successfully updated target temperature"
      }
  }
}

A beágyazott összetevők megkapják az összetevő nevével burkolt kívánt tulajdonságokat, és a jelentett tulajdonságot jelentik vissza ack :

bool PnP_ProcessTwinData(
    DEVICE_TWIN_UPDATE_STATE updateState,
    const unsigned char* payload,
    size_t size, const char** componentsInModel,
    size_t numComponentsInModel,
    PnP_PropertyCallbackFunction pnpPropertyCallback,
    void* userContextCallback)
{
    char* jsonStr = NULL;
    JSON_Value* rootValue = NULL;
    JSON_Object* desiredObject;
    bool result;

    jsonStr = PnP_CopyPayloadToString(payload, size));
    rootValue = json_parse_string(jsonStr));
    desiredObject = GetDesiredJson(updateState, rootValue));
    
    result = VisitDesiredObject(
        desiredObject, componentsInModel,
        numComponentsInModel, pnpPropertyCallback,
        userContextCallback);


    json_value_free(rootValue);
    free(jsonStr);

    return result;
}

// ...
static const char g_thermostatComponent1Name[] = "thermostat1";
static const size_t g_thermostatComponent1Size = sizeof(g_thermostatComponent1Name) - 1;
static const char g_thermostatComponent2Name[] = "thermostat2";

static const char* g_modeledComponents[] = {g_thermostatComponent1Name, g_thermostatComponent2Name};
static const size_t g_numModeledComponents = sizeof(g_modeledComponents) / sizeof(g_modeledComponents[0]);

static void PnP_TempControlComponent_DeviceTwinCallback(
    DEVICE_TWIN_UPDATE_STATE updateState,
    const unsigned char* payload,
    size_t size,
    void* userContextCallback
)
{
    PnP_ProcessTwinData(
        updateState, payload,
        size, g_modeledComponents,
        g_numModeledComponents,
        PnP_TempControlComponent_ApplicationPropertyCallback,
        userContextCallback);
}

A beágyazott összetevők ikereszköze a következő módon jeleníti meg a kívánt és jelentett szakaszokat:

{
  "desired" : {
    "thermostat1" : {
        "__t" : "c",
        "targetTemperature": 23.2,
    }
    "$version" : 3
  },
  "reported": {
    "thermostat1" : {
        "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
    }
  }
}

Parancsok

Az alapértelmezett összetevő megkapja a parancs nevét, amikor a szolgáltatás meghívta.

A beágyazott összetevők megkapják az összetevő nevével és * az elválasztóval előtaggal ellátott parancsnevet.

void PnP_ParseCommandName(
    const char* deviceMethodName,
    unsigned const char** componentName,
    size_t* componentNameSize,
    const char** pnpCommandName
)
{
    const char* separator;

    if ((separator = strchr(deviceMethodName, "*")) != NULL)
    {
        *componentName = (unsigned const char*)deviceMethodName;
        *componentNameSize = separator - deviceMethodName;
        *pnpCommandName = separator + 1;
    }
    else
    {
        *componentName = NULL;
        *componentNameSize = 0;
        *pnpCommandName = deviceMethodName;
    }
}

static int PnP_TempControlComponent_DeviceMethodCallback(
    const char* methodName,
    const unsigned char* payload,
    size_t size,
    unsigned char** response,
    size_t* responseSize,
    void* userContextCallback)
{
    (void)userContextCallback;

    char* jsonStr = NULL;
    JSON_Value* rootValue = NULL;
    int result;
    unsigned const char *componentName;
    size_t componentNameSize;
    const char *pnpCommandName;

    *response = NULL;
    *responseSize = 0;

    // Parse the methodName into its componentName and CommandName.
    PnP_ParseCommandName(methodName, &componentName, &componentNameSize, &pnpCommandName);

    // Parse the JSON of the payload request.
    jsonStr = PnP_CopyPayloadToString(payload, size));
    rootValue = json_parse_string(jsonStr));
    if (componentName != NULL)
    {
        if (strncmp((const char*)componentName, g_thermostatComponent1Name, g_thermostatComponent1Size) == 0)
        {
            result = PnP_ThermostatComponent_ProcessCommand(g_thermostatHandle1, pnpCommandName, rootValue, response, responseSize);
        }
        else if (strncmp((const char*)componentName, g_thermostatComponent2Name, g_thermostatComponent2Size) == 0)
        {
            result = PnP_ThermostatComponent_ProcessCommand(g_thermostatHandle2, pnpCommandName, rootValue, response, responseSize);
        }
        else
        {
            LogError("PnP component=%.*s is not supported by TemperatureController", (int)componentNameSize, componentName);
            result = PNP_STATUS_NOT_FOUND;
        }
    }
    else
    {
        LogInfo("Received PnP command for TemperatureController component, command=%s", pnpCommandName);
        if (strcmp(pnpCommandName, g_rebootCommand) == 0)
        {
            result = PnP_TempControlComponent_InvokeRebootCommand(rootValue);
        }
        else
        {
            LogError("PnP command=s%s is not supported by TemperatureController", pnpCommandName);
            result = PNP_STATUS_NOT_FOUND;
        }
    }

    if (*response == NULL)
    {
        SetEmptyCommandResponse(response, responseSize, &result);
    }

    json_value_free(rootValue);
    free(jsonStr);

    return result;
}

// ...

PNP_DEVICE_CONFIGURATION g_pnpDeviceConfiguration;
g_pnpDeviceConfiguration.deviceMethodCallback = PnP_TempControlComponent_DeviceMethodCallback;
deviceClient = PnP_CreateDeviceClientLLHandle(&g_pnpDeviceConfiguration);

Hasznos adatok kérése és válasza

A parancsok típusokat használnak a kérések és válaszok hasznos adatainak meghatározásához. Az eszköznek deszerializálnia kell a bejövő bemeneti paramétert, és szerializálnia kell a választ.

Az alábbi példa bemutatja, hogyan implementálhat egy olyan parancsot, amely a hasznos adatokban definiált összetett típusokat tartalmazza:

{
  "@type": "Command",
  "name": "getMaxMinReport",
  "displayName": "Get Max-Min report.",
  "description": "This command returns the max, min and average temperature from the specified time to the current time.",
  "request": {
    "name": "since",
    "displayName": "Since",
    "description": "Period to return the max-min report.",
    "schema": "dateTime"
  },
  "response": {
    "name" : "tempReport",
    "displayName": "Temperature Report",
    "schema": {
      "@type": "Object",
      "fields": [
        {
          "name": "maxTemp",
          "displayName": "Max temperature",
          "schema": "double"
        },
        {
          "name": "minTemp",
          "displayName": "Min temperature",
          "schema": "double"
        },
        {
          "name" : "avgTemp",
          "displayName": "Average Temperature",
          "schema": "double"
        },
        {
          "name" : "startTime",
          "displayName": "Start Time",
          "schema": "dateTime"
        },
        {
          "name" : "endTime",
          "displayName": "End Time",
          "schema": "dateTime"
        }
      ]
    }
  }
}

Az alábbi kódrészletek bemutatják, hogyan valósítja meg az eszköz ezt a parancsdefiníciót, beleértve a szerializálás és a deszerializálás engedélyezéséhez használt típusokat:

static const char g_maxMinCommandResponseFormat[] = "{\"maxTemp\":%.2f,\"minTemp\":%.2f,\"avgTemp\":%.2f,\"startTime\":\"%s\",\"endTime\":\"%s\"}";

// ...

static bool BuildMaxMinCommandResponse(
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent,
    unsigned char** response,
    size_t* responseSize)
{
    int responseBuilderSize = 0;
    unsigned char* responseBuilder = NULL;
    bool result;
    char currentTime[TIME_BUFFER_SIZE];

    BuildUtcTimeFromCurrentTime(currentTime, sizeof(currentTime));
    responseBuilderSize = snprintf(NULL, 0, g_maxMinCommandResponseFormat,
        pnpThermostatComponent->maxTemperature,
        pnpThermostatComponent->minTemperature,
        pnpThermostatComponent->allTemperatures /
        pnpThermostatComponent->numTemperatureUpdates,
        g_programStartTime, currentTime));

    responseBuilder = calloc(1, responseBuilderSize + 1));

    responseBuilderSize = snprintf(
        (char*)responseBuilder, responseBuilderSize + 1, g_maxMinCommandResponseFormat,
        pnpThermostatComponent->maxTemperature,
        pnpThermostatComponent->minTemperature,
        pnpThermostatComponent->allTemperatures / pnpThermostatComponent->numTemperatureUpdates,
        g_programStartTime,
        currentTime));

    *response = responseBuilder;
    *responseSize = (size_t)responseBuilderSize;

    return true;
}

Tipp

A kérés- és válasznevek nem szerepelnek a hálózaton keresztül továbbított szerializált hasznos adatokban.

SDK-k

A cikkben szereplő kódrészletek az Azure RTOS-hez készült Azure IoT Middleware bővítményt használó mintákon alapulnak. A bővítmény egy kötési réteg az Azure RTOS és az Azure SDK for Embedded C között.

A cikkben szereplő kódrészletek a következő mintákon alapulnak:

Modellazonosító bejelentése

A modellazonosító bejelentéséhez az eszköznek szerepelnie kell a kapcsolati adatokban:

#include "nx_azure_iot_hub_client.h"

// ...

#define SAMPLE_PNP_MODEL_ID "dtmi:com:example:Thermostat;1"

// ...

status = nx_azure_iot_hub_client_model_id_set(iothub_client_ptr, (UCHAR *)SAMPLE_PNP_MODEL_ID, sizeof(SAMPLE_PNP_MODEL_ID) - 1);

Tipp

Ez az egyetlen alkalom, amikor egy eszköz beállíthat modellazonosítót, és nem frissíthető az eszköz csatlakoztatása után.

DPS-adattartalom

A Device Provisioning Service -t (DPS) használó eszközök a következő JSON-hasznos adatokkal tartalmazhatják a modelId kiépítési folyamat során használni kívánt eszközöket:

{
    "modelId" : "dtmi:com:example:Thermostat;1"
}

A minta a következő kódot használja a hasznos adatok elküldéséhez:

#include "nx_azure_iot_provisioning_client.h"

// ...

#define SAMPLE_PNP_MODEL_ID "dtmi:com:example:Thermostat;1"
#define SAMPLE_PNP_DPS_PAYLOAD "{\"modelId\":\"" SAMPLE_PNP_MODEL_ID "\"}"

// ...

status = nx_azure_iot_provisioning_client_registration_payload_set(prov_client_ptr, (UCHAR *)SAMPLE_PNP_DPS_PAYLOAD, sizeof(SAMPLE_PNP_DPS_PAYLOAD) - 1);

Összetevők használata

Az IoT-Plug and Play modellek összetevőinek ismertetésében leírtaknak megfelelően az eszközkészítőknek el kell dönteniük, hogy összetevőket szeretnének-e használni az eszközeik leírásához. Összetevők használatakor az eszközöknek az alábbi szakaszokban ismertetett szabályokat kell követnie. Az összetevők IoT Plug and Play konvencióinak egyszerűbb használata érdekében a minták a nx_azure_iot_hub_client.h segédfüggvényeit használják.

Telemetria

Az alapértelmezett összetevőkhöz nem szükséges speciális tulajdonságot hozzáadni a telemetriai üzenethez.

Beágyazott összetevők használatakor az eszközöknek egy üzenettulajdonságot kell beállítaniuk az összetevő nevével. Az alábbi kódrészletben egy olyan összetevő neve szerepel, component_name_ptr mint a thermostat1. A nx_azure_iot_pnp_helpers.h fájlban definiált segédfüggvény nx_azure_iot_pnp_helper_telemetry_message_create hozzáadja az üzenettulajdonságot az összetevő nevével:

#include "nx_azure_iot_pnp_helpers.h"

// ...

static const CHAR telemetry_name[] = "temperature";

// ...

UINT sample_pnp_thermostat_telemetry_send(SAMPLE_PNP_THERMOSTAT_COMPONENT *handle, NX_AZURE_IOT_HUB_CLIENT *iothub_client_ptr)
{
UINT status;
NX_PACKET *packet_ptr;
NX_AZURE_IOT_JSON_WRITER json_writer;
UINT buffer_length;

    // ...

    /* Create a telemetry message packet. */
    if ((status = nx_azure_iot_pnp_helper_telemetry_message_create(iothub_client_ptr, handle -> component_name_ptr,
        handle -> component_name_length,
        &packet_ptr, NX_WAIT_FOREVER)))
    {
        // ...
    }

    // ...

    if ((status = nx_azure_iot_hub_client_telemetry_send(iothub_client_ptr, packet_ptr,
        (UCHAR *)scratch_buffer, buffer_length, NX_WAIT_FOREVER)))
    {
        // ...
    }

    // ...

    return(status);
}

Csak olvasható tulajdonságok

Az alapértelmezett összetevőből származó tulajdonság jelentéséhez nincs szükség speciális szerkezetre:

#include "nx_azure_iot_hub_client.h"
#include "nx_azure_iot_json_writer.h"

// ...

static const CHAR reported_max_temp_since_last_reboot[] = "maxTempSinceLastReboot";

// ...

static UINT sample_build_reported_property(NX_AZURE_IOT_JSON_WRITER *json_builder_ptr, double temp)
{
UINT ret;

    if (nx_azure_iot_json_writer_append_begin_object(json_builder_ptr) ||
        nx_azure_iot_json_writer_append_property_with_double_value(json_builder_ptr,
            (UCHAR *)reported_max_temp_since_last_reboot,
            sizeof(reported_max_temp_since_last_reboot) - 1,
            temp, DOUBLE_DECIMAL_PLACE_DIGITS) ||
        nx_azure_iot_json_writer_append_end_object(json_builder_ptr))
    {
        ret = 1;
        printf("Failed to build reported property\r\n");
    }
    else
    {
        ret = 0;
    }

    return(ret);
}

// ...

if ((status = sample_build_reported_property(&json_builder, device_max_temp)))
{
    // ...
}

reported_properties_length = nx_azure_iot_json_writer_get_bytes_used(&json_builder);
if ((status = nx_azure_iot_hub_client_device_twin_reported_properties_send(&(context -> iothub_client),
    scratch_buffer,
    reported_properties_length,
    &request_id, &response_status,
    &reported_property_version,
    (5 * NX_IP_PERIODIC_RATE))))
{
    // ...
}

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
      "maxTempSinceLastReboot" : 38.7
  }
}

Beágyazott összetevők használatakor a tulajdonságokat az összetevő nevében kell létrehozni, és egy jelölőt kell tartalmaznia, és tartalmaznia kell egy jelölőt. Az alábbi kódrészletben egy olyan összetevő neve szerepel, component_name_ptr mint a thermostat1. A nx_azure_iot_pnp_helpers.h fájlban definiált segédfüggvény nx_azure_iot_pnp_helper_build_reported_property a megfelelő formátumban hozza létre a jelentett tulajdonságot:

#include "nx_azure_iot_pnp_helpers.h"

// ...

static const CHAR reported_max_temp_since_last_reboot[] = "maxTempSinceLastReboot";

UINT sample_pnp_thermostat_report_max_temp_since_last_reboot_property(SAMPLE_PNP_THERMOSTAT_COMPONENT *handle, NX_AZURE_IOT_HUB_CLIENT *iothub_client_ptr)
{
UINT reported_properties_length;
UINT status;
UINT response_status;
UINT request_id;
NX_AZURE_IOT_JSON_WRITER json_builder;
ULONG reported_property_version;

    // ...

    if ((status = nx_azure_iot_pnp_helper_build_reported_property(handle -> component_name_ptr,
        handle -> component_name_length,
        append_max_temp, (VOID *)handle,
        &json_builder)))
    {
        // ...
    }

    reported_properties_length = nx_azure_iot_json_writer_get_bytes_used(&json_builder);
    if ((status = nx_azure_iot_hub_client_device_twin_reported_properties_send(iothub_client_ptr,
        scratch_buffer,
        reported_properties_length,
        &request_id, &response_status,
        &reported_property_version,
        (5 * NX_IP_PERIODIC_RATE))))
    {
        // ...
    }

    // ...
}

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
    "reported": {
        "thermostat1" : {  
            "__t" : "c",  
            "maxTemperature" : 38.7
        } 
    }
}

Írható tulajdonságok

Ezeket a tulajdonságokat az eszköz állíthatja be, vagy a megoldás frissítheti. Az IoT Plug and Play konvenciók követéséhez az eszköznek tájékoztatnia kell a szolgáltatást a tulajdonság sikeres fogadásáról.

Írható tulajdonság jelentése

Amikor egy eszköz írható tulajdonságot jelent, annak tartalmaznia kell a ack konvenciókban meghatározott értékeket.

Írható tulajdonság jelentése az alapértelmezett összetevőből:

#include "nx_azure_iot_hub_client.h"
#include "nx_azure_iot_json_writer.h"

// ...

static const CHAR reported_temp_property_name[] = "targetTemperature";
static const CHAR reported_value_property_name[] = "value";
static const CHAR reported_status_property_name[] = "ac";
static const CHAR reported_version_property_name[] = "av";
static const CHAR reported_description_property_name[] = "ad";

// ...

static VOID sample_send_target_temperature_report(SAMPLE_CONTEXT *context, double current_device_temp_value,
    UINT status, UINT version, UCHAR *description_ptr,
    UINT description_len)
{
NX_AZURE_IOT_JSON_WRITER json_builder;
UINT bytes_copied;
UINT response_status;
UINT request_id;
ULONG reported_property_version;

    // ...

    if (nx_azure_iot_json_writer_append_begin_object(&json_builder) ||
        nx_azure_iot_json_writer_append_property_name(&json_builder,
            (UCHAR *)reported_temp_property_name,
            sizeof(reported_temp_property_name) - 1) ||
        nx_azure_iot_json_writer_append_begin_object(&json_builder) ||
        nx_azure_iot_json_writer_append_property_with_double_value(&json_builder,
            (UCHAR *)reported_value_property_name,
            sizeof(reported_value_property_name) - 1,
            current_device_temp_value, DOUBLE_DECIMAL_PLACE_DIGITS) ||
        nx_azure_iot_json_writer_append_property_with_int32_value(&json_builder,
            (UCHAR *)reported_status_property_name,
            sizeof(reported_status_property_name) - 1,
            (int32_t)status) ||
        nx_azure_iot_json_writer_append_property_with_int32_value(&json_builder,
            (UCHAR *)reported_version_property_name,
            sizeof(reported_version_property_name) - 1,
            (int32_t)version) ||
        nx_azure_iot_json_writer_append_property_with_string_value(&json_builder,
            (UCHAR *)reported_description_property_name,
            sizeof(reported_description_property_name) - 1,
            description_ptr, description_len) ||
        nx_azure_iot_json_writer_append_end_object(&json_builder) ||
        nx_azure_iot_json_writer_append_end_object(&json_builder))
    {
        // ...
    }
    else
    // ...
}

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "success"
      }
  }
}

Ha írható tulajdonságot szeretne jelenteni egy beágyazott összetevőből, az ikerpéldánynak tartalmaznia kell egy jelölőt, és a tulajdonságokat az összetevő nevében kell létrehozni. Az alábbi kódrészletben egy olyan összetevő neve szerepel, component_name_ptr mint a thermostat1. A nx_azure_iot_pnp_helpers.h fájlban definiált segédfüggvény nx_azure_iot_pnp_helper_build_reported_property_with_status létrehozza a jelentett tulajdonság hasznos adatait:

#include "nx_azure_iot_pnp_helpers.h"

// ...

static VOID sample_send_target_temperature_report(SAMPLE_PNP_THERMOSTAT_COMPONENT *handle,
    NX_AZURE_IOT_HUB_CLIENT *iothub_client_ptr, double temp,
    INT status_code, UINT version, const CHAR *description)
{
UINT bytes_copied;
UINT response_status;
UINT request_id;
NX_AZURE_IOT_JSON_WRITER json_writer;
ULONG reported_property_version;

    // ...

    if (nx_azure_iot_pnp_helper_build_reported_property_with_status(handle -> component_name_ptr, handle -> component_name_length,
        (UCHAR *)target_temp_property_name,
        sizeof(target_temp_property_name) - 1,
        append_temp, (VOID *)&temp, status_code,
        (UCHAR *)description,
        strlen(description), version, &json_writer))
    {
        // ...
    }
    else
    {
        // ...
    }

    // ...
}

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "thermostat1": {
      "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "success"
      }
    }
  }
}

Feliratkozás a kívánt tulajdonságfrissítésre

A szolgáltatások frissíthetik a kívánt tulajdonságokat, amelyek értesítést aktiválnak a csatlakoztatott eszközökön. Ez az értesítés tartalmazza a frissített kívánt tulajdonságokat és a frissítést azonosító verziószámot. Az eszközöknek tartalmazniuk kell ezt a verziószámot a ack szolgáltatásnak visszaküldött üzenetben.

Az alapértelmezett összetevő az egyetlen tulajdonságot látja, és a kapott verzióval hozza létre a jelentést ack :

#include "nx_azure_iot_hub_client.h"
#include "nx_azure_iot_json_writer.h"

// ...

static const CHAR temp_response_description[] = "success";

// ...

static UINT sample_parse_desired_temp_property(SAMPLE_CONTEXT *context,
    NX_AZURE_IOT_JSON_READER *json_reader_ptr,
    UINT is_partial)
{
double parsed_value;
UINT version;
NX_AZURE_IOT_JSON_READER copy_json_reader;
UINT status;

    // ...

    copy_json_reader = *json_reader_ptr;
    if (sample_json_child_token_move(&copy_json_reader,
            (UCHAR *)desired_version_property_name,
            sizeof(desired_version_property_name) - 1) ||
        nx_azure_iot_json_reader_token_int32_get(&copy_json_reader, (int32_t *)&version))
    {
        // ...
    }

    // ...

    sample_send_target_temperature_report(context, current_device_temp, 200,
        (UINT)version, (UCHAR *)temp_response_description,
        sizeof(temp_response_description) - 1);

    // ...
}

A beágyazott összetevők megkapják a kívánt tulajdonságokat, amelyek az összetevő nevével vannak burkolva, és létrehoznak egy jelentést ack a kapott verzióval:

#include "nx_azure_iot_pnp_helpers.h"

// ...

static const CHAR target_temp_property_name[] = "targetTemperature";
static const CHAR temp_response_description_success[] = "success";
static const CHAR temp_response_description_failed[] = "failed";

// ...

UINT sample_pnp_thermostat_process_property_update(SAMPLE_PNP_THERMOSTAT_COMPONENT *handle,
    NX_AZURE_IOT_HUB_CLIENT *iothub_client_ptr,
    UCHAR *component_name_ptr, UINT component_name_length,
    UCHAR *property_name_ptr, UINT property_name_length,
    NX_AZURE_IOT_JSON_READER *property_value_reader_ptr, UINT version)
{
double parsed_value = 0;
INT status_code;
const CHAR *description;

    // ...

    if (property_name_length != (sizeof(target_temp_property_name) - 1) ||
        strncmp((CHAR *)property_name_ptr, (CHAR *)target_temp_property_name, property_name_length) != 0)
    {
        // ...
    }
    else if (nx_azure_iot_json_reader_token_double_get(property_value_reader_ptr, &parsed_value))
    {
        status_code = 401;
        description = temp_response_description_failed;
    }
    else
    {
        status_code = 200;
        description = temp_response_description_success;

        // ...
    }

    sample_send_target_temperature_report(handle, iothub_client_ptr, parsed_value,
                                          status_code, version, description);

    // ...
}

A beágyazott összetevők ikereszköze a következő módon jeleníti meg a kívánt és jelentett szakaszokat:

{
  "desired" : {
    "thermostat1" : {
        "__t" : "c",
        "targetTemperature": 23.2,
    }
    "$version" : 3
  },
  "reported": {
    "thermostat1" : {
        "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "success"
      }
    }
  }
}

Parancsok

Az alapértelmezett összetevő megkapja a parancs nevét, amikor a szolgáltatás meghívta.

A beágyazott összetevők megkapják az összetevő nevével és * az elválasztóval előtaggal ellátott parancsnevet. A következő kódrészletben a nx_azure_iot_pnp_helpers.h fájlban definiált segédfüggvény nx_azure_iot_pnp_helper_command_name_parse elemzi az összetevő nevét és a parancs nevét az eszköz által a szolgáltatástól kapott üzenetből:

#include "nx_azure_iot_hub_client.h"
#include "nx_azure_iot_pnp_helpers.h"

// ...

static VOID sample_direct_method_action(SAMPLE_CONTEXT *sample_context_ptr)
{
NX_PACKET *packet_ptr;
UINT status;
USHORT method_name_length;
const UCHAR *method_name_ptr;
USHORT context_length;
VOID *context_ptr;
UINT component_name_length;
const UCHAR *component_name_ptr;
UINT pnp_command_name_length;
const UCHAR *pnp_command_name_ptr;
NX_AZURE_IOT_JSON_WRITER json_writer;
NX_AZURE_IOT_JSON_READER json_reader;
NX_AZURE_IOT_JSON_READER *json_reader_ptr;
UINT status_code;
UINT response_length;

    // ...

    if ((status = nx_azure_iot_hub_client_direct_method_message_receive(&(sample_context_ptr -> iothub_client),
        &method_name_ptr, &method_name_length,
        &context_ptr, &context_length,
        &packet_ptr, NX_WAIT_FOREVER)))
    {
        // ...
    }

    // ...

    if ((status = nx_azure_iot_pnp_helper_command_name_parse(method_name_ptr, method_name_length,
        &component_name_ptr, &component_name_length,
        &pnp_command_name_ptr,
        &pnp_command_name_length)) != NX_AZURE_IOT_SUCCESS)
    {
        // ...
    }
    
    // ...

    else
    {
        // ...

        if ((status = sample_pnp_thermostat_process_command(&sample_thermostat_1, component_name_ptr,
            component_name_length, pnp_command_name_ptr,
            pnp_command_name_length, json_reader_ptr,
            &json_writer, &status_code)) == NX_AZURE_IOT_SUCCESS)
        {
            // ...
        }
        else if ((status = sample_pnp_thermostat_process_command(&sample_thermostat_2, component_name_ptr,
            component_name_length, pnp_command_name_ptr,
            pnp_command_name_length, json_reader_ptr,
            &json_writer, &status_code)) == NX_AZURE_IOT_SUCCESS)
        {
            // ...
        }
        else if((status = sample_pnp_temp_controller_process_command(component_name_ptr, component_name_length,
            pnp_command_name_ptr, pnp_command_name_length,
            json_reader_ptr, &json_writer,
            &status_code)) == NX_AZURE_IOT_SUCCESS)
        {
            // ...
        }
        else
        {
            printf("Failed to find any handler for method %.*s\r\n", method_name_length, method_name_ptr);
            status_code = SAMPLE_COMMAND_NOT_FOUND_STATUS;
            response_length = 0;
        }

        // ...
    }
}

Hasznos adatok kérése és válasza

A parancsok típusokat használnak a kérések és válaszok hasznos adatainak meghatározásához. Az eszköznek deszerializálnia kell a bejövő bemeneti paramétert, és szerializálnia kell a választ.

Az alábbi példa bemutatja, hogyan implementálhat egy olyan parancsot, amely a hasznos adatokban definiált összetett típusokat tartalmazza:

{
  "@type": "Command",
  "name": "getMaxMinReport",
  "displayName": "Get Max-Min report.",
  "description": "This command returns the max, min and average temperature from the specified time to the current time.",
  "request": {
    "name": "since",
    "displayName": "Since",
    "description": "Period to return the max-min report.",
    "schema": "dateTime"
  },
  "response": {
    "name" : "tempReport",
    "displayName": "Temperature Report",
    "schema": {
      "@type": "Object",
      "fields": [
        {
          "name": "maxTemp",
          "displayName": "Max temperature",
          "schema": "double"
        },
        {
          "name": "minTemp",
          "displayName": "Min temperature",
          "schema": "double"
        },
        {
          "name" : "avgTemp",
          "displayName": "Average Temperature",
          "schema": "double"
        },
        {
          "name" : "startTime",
          "displayName": "Start Time",
          "schema": "dateTime"
        },
        {
          "name" : "endTime",
          "displayName": "End Time",
          "schema": "dateTime"
        }
      ]
    }
  }
}

Az alábbi kódrészletek bemutatják, hogyan valósítja meg az eszköz ezt a parancsdefiníciót, beleértve a szerializálás és a deszerializálás engedélyezéséhez használt típusokat:

#include "nx_azure_iot_pnp_helpers.h"

// ...

static const CHAR report_max_temp_name[] = "maxTemp";
static const CHAR report_min_temp_name[] = "minTemp";
static const CHAR report_avg_temp_name[] = "avgTemp";
static const CHAR report_start_time_name[] = "startTime";
static const CHAR report_end_time_name[] = "endTime";
static const CHAR fake_start_report_time[] = "2020-01-10T10:00:00Z";
static const CHAR fake_end_report_time[] = "2023-01-10T10:00:00Z";

// ...

static UINT sample_get_maxmin_report(SAMPLE_PNP_THERMOSTAT_COMPONENT *handle,
    NX_AZURE_IOT_JSON_READER *json_reader_ptr,
    NX_AZURE_IOT_JSON_WRITER *out_json_builder_ptr)
{
UINT status;
UCHAR *start_time = (UCHAR *)fake_start_report_time;
UINT start_time_len = sizeof(fake_start_report_time) - 1;
UCHAR time_buf[32];

    // ...

    /* Build the method response payload */
    if (nx_azure_iot_json_writer_append_begin_object(out_json_builder_ptr) ||
        nx_azure_iot_json_writer_append_property_with_double_value(out_json_builder_ptr,
            (UCHAR *)report_max_temp_name,
            sizeof(report_max_temp_name) - 1,
            handle -> maxTemperature,
            DOUBLE_DECIMAL_PLACE_DIGITS) ||
        nx_azure_iot_json_writer_append_property_with_double_value(out_json_builder_ptr,
            (UCHAR *)report_min_temp_name,
            sizeof(report_min_temp_name) - 1,
            handle -> minTemperature,
            DOUBLE_DECIMAL_PLACE_DIGITS) ||
        nx_azure_iot_json_writer_append_property_with_double_value(out_json_builder_ptr,
            (UCHAR *)report_avg_temp_name,
            sizeof(report_avg_temp_name) - 1,
            handle -> avgTemperature,
            DOUBLE_DECIMAL_PLACE_DIGITS) ||
        nx_azure_iot_json_writer_append_property_with_string_value(out_json_builder_ptr,
            (UCHAR *)report_start_time_name,
            sizeof(report_start_time_name) - 1,
            (UCHAR *)start_time, start_time_len) ||
        nx_azure_iot_json_writer_append_property_with_string_value(out_json_builder_ptr,
            (UCHAR *)report_end_time_name,
            sizeof(report_end_time_name) - 1,
            (UCHAR *)fake_end_report_time,
            sizeof(fake_end_report_time) - 1) ||
        nx_azure_iot_json_writer_append_end_object(out_json_builder_ptr))
    {
        status = NX_NOT_SUCCESSFUL;
    }
    else
    {
        status = NX_AZURE_IOT_SUCCESS;
    }

    return(status);
}

Tipp

A kérés- és válasznevek nem szerepelnek a hálózaton keresztül továbbított szerializált hasznos adatokban.

Modellazonosító bejelentése

A modellazonosító bejelentéséhez az eszköznek szerepelnie kell a kapcsolati adatokban:

DeviceClient.CreateFromConnectionString(
  connectionString,
  TransportType.Mqtt,
  new ClientOptions() { ModelId = modelId })

Az új ClientOptions túlterhelés a kapcsolat inicializálásához használt összes DeviceClient metódusban elérhető.

Tipp

Modulok és IoT Edge helyett használja ModuleClient a DeviceClient.

Tipp

Ez az egyetlen alkalom, amikor egy eszköz beállíthat modellazonosítót, és nem frissíthető az eszköz csatlakoztatása után.

DPS-adattartalom

A Device Provisioning Service -t (DPS) használó eszközök a következő JSON-hasznos adatokkal tartalmazhatják a modelId kiépítési folyamat során használni kívánt eszközöket:

{
    "modelId" : "dtmi:com:example:Thermostat;1"
}

Összetevők használata

Az IoT-Plug and Play modellek összetevőinek ismertetésében leírtaknak megfelelően az eszközkészítőknek el kell dönteniük, hogy összetevőket szeretnének-e használni az eszközeik leírásához. Összetevők használatakor az eszközöknek az alábbi szakaszokban ismertetett szabályokat kell követnie.

Telemetria

Az alapértelmezett összetevőkhöz nem szükséges speciális tulajdonságot hozzáadni a telemetriai üzenethez.

Beágyazott összetevők használatakor az eszközöknek egy üzenettulajdonságot kell beállítaniuk az összetevő nevével:

public async Task SendComponentTelemetryValueAsync(string componentName, string serializedTelemetry)
{
  var message = new Message(Encoding.UTF8.GetBytes(serializedTelemetry));
  message.ComponentName = componentName;
  message.ContentType = "application/json";
  message.ContentEncoding = "utf-8";
  await client.SendEventAsync(message);
}

Csak olvasható tulajdonságok

Az alapértelmezett összetevőből származó tulajdonság jelentéséhez nincs szükség speciális szerkezetre:

TwinCollection reportedProperties = new TwinCollection();
reportedProperties["maxTemperature"] = 38.7;
await client.UpdateReportedPropertiesAsync(reportedProperties);

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
      "maxTemperature" : 38.7
  }
}

Beágyazott összetevők használatakor a tulajdonságokat az összetevő nevében kell létrehozni, és tartalmaznia kell egy jelölőt:

TwinCollection reportedProperties = new TwinCollection();
TwinCollection component = new TwinCollection();
component["maxTemperature"] = 38.7;
component["__t"] = "c"; // marker to identify a component
reportedProperties["thermostat1"] = component;
await client.UpdateReportedPropertiesAsync(reportedProperties);

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "thermostat1" : {  
      "__t" : "c",  
      "maxTemperature" : 38.7
     } 
  }
}

Írható tulajdonságok

Ezeket a tulajdonságokat az eszköz állíthatja be, vagy a megoldás frissítheti. Ha a megoldás frissít egy tulajdonságot, az ügyfél értesítést kap visszahívásként a vagy ModuleClienta DeviceClient . Az IoT Plug and Play konvenciók követéséhez az eszköznek tájékoztatnia kell a szolgáltatást a tulajdonság sikeres fogadásáról.

Ha a tulajdonság típusa, Objecta szolgáltatásnak akkor is teljes objektumot kell küldenie az eszköznek, ha csak az objektum mezőinek egy részét frissíti. Az eszköz által küldött nyugtázásnak teljes objektumnak is kell lennie.

Írható tulajdonság jelentése

Amikor egy eszköz írható tulajdonságot jelent, annak tartalmaznia kell a ack konvenciókban meghatározott értékeket.

Írható tulajdonság jelentése az alapértelmezett összetevőből:

TwinCollection reportedProperties = new TwinCollection();
TwinCollection ackProps = new TwinCollection();
ackProps["value"] = 23.2;
ackProps["ac"] = 200; // using HTTP status codes
ackProps["av"] = 0; // not readed from a desired property
ackProps["ad"] = "reported default value";
reportedProperties["targetTemperature"] = ackProps;
await client.UpdateReportedPropertiesAsync(reportedProperties);

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
  }
}

Ha írható tulajdonságot szeretne jelenteni egy beágyazott összetevőből, az ikerpéldánynak tartalmaznia kell egy jelölőt:

TwinCollection reportedProperties = new TwinCollection();
TwinCollection component = new TwinCollection();
TwinCollection ackProps = new TwinCollection();
component["__t"] = "c"; // marker to identify a component
ackProps["value"] = 23.2;
ackProps["ac"] = 200; // using HTTP status codes
ackProps["av"] = 0; // not read from a desired property
ackProps["ad"] = "reported default value";
component["targetTemperature"] = ackProps;
reportedProperties["thermostat1"] = component;
await client.UpdateReportedPropertiesAsync(reportedProperties);

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "thermostat1": {
      "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
    }
  }
}

Feliratkozás a kívánt tulajdonságfrissítésre

A szolgáltatások frissíthetik a kívánt tulajdonságokat, amelyek értesítést aktiválnak a csatlakoztatott eszközökön. Ez az értesítés tartalmazza a frissített kívánt tulajdonságokat, beleértve a frissítést azonosító verziószámot. Az eszközöknek tartalmazniuk kell ezt a verziószámot a ack szolgáltatásnak visszaküldött üzenetben.

Az alapértelmezett összetevő az egyetlen tulajdonságot látja, és a kapott verzióval hozza létre a jelentést ack :

await client.SetDesiredPropertyUpdateCallbackAsync(async (desired, ctx) => 
{
  JValue targetTempJson = desired["targetTemperature"];
  double targetTemperature = targetTempJson.Value<double>();

  TwinCollection reportedProperties = new TwinCollection();
  TwinCollection ackProps = new TwinCollection();
  ackProps["value"] = targetTemperature;
  ackProps["ac"] = 200;
  ackProps["av"] = desired.Version; 
  ackProps["ad"] = "desired property received";
  reportedProperties["targetTemperature"] = ackProps;

  await client.UpdateReportedPropertiesAsync(reportedProperties);
}, null);

A beágyazott összetevők ikereszköze a következő módon jeleníti meg a kívánt és jelentett szakaszokat:

{
  "desired" : {
    "targetTemperature": 23.2,
    "$version" : 3
  },
  "reported": {
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
  }
}

A beágyazott összetevők megkapják az összetevő nevével burkolt kívánt tulajdonságokat, és a jelentett tulajdonságot jelentik vissza ack :

await client.SetDesiredPropertyUpdateCallbackAsync(async (desired, ctx) =>
{
  JObject thermostatComponent = desired["thermostat1"];
  JToken targetTempProp = thermostatComponent["targetTemperature"];
  double targetTemperature = targetTempProp.Value<double>();

  TwinCollection reportedProperties = new TwinCollection();
  TwinCollection component = new TwinCollection();
  TwinCollection ackProps = new TwinCollection();
  component["__t"] = "c"; // marker to identify a component
  ackProps["value"] = targetTemperature;
  ackProps["ac"] = 200; // using HTTP status codes
  ackProps["av"] = desired.Version; // not readed from a desired property
  ackProps["ad"] = "desired property received";
  component["targetTemperature"] = ackProps;
  reportedProperties["thermostat1"] = component;

  await client.UpdateReportedPropertiesAsync(reportedProperties);
}, null);

A beágyazott összetevők ikereszköze a következő módon jeleníti meg a kívánt és jelentett szakaszokat:

{
  "desired" : {
    "thermostat1" : {
        "__t" : "c",
        "targetTemperature": 23.2,
    }
    "$version" : 3
  },
  "reported": {
    "thermostat1" : {
        "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
    }
  }
}

Parancsok

Az alapértelmezett összetevő megkapja a parancs nevét, amikor a szolgáltatás meghívta.

A beágyazott összetevők megkapják az összetevő nevével és * az elválasztóval előtaggal ellátott parancsnevet.

await client.SetMethodHandlerAsync("themostat*reboot", (MethodRequest req, object ctx) =>
{
  Console.WriteLine("REBOOT");
  return Task.FromResult(new MethodResponse(200));
},
null);

Hasznos adatok kérése és válasza

A parancsok típusokat használnak a kérések és válaszok hasznos adatainak meghatározásához. Az eszköznek deszerializálnia kell a bejövő bemeneti paramétert, és szerializálnia kell a választ. Az alábbi példa bemutatja, hogyan implementálhat egy olyan parancsot, amely a hasznos adatokban definiált összetett típusokat tartalmazza:

{
  "@type": "Command",
  "name": "start",
  "request": {
    "name": "startRequest",
    "schema": {
      "@type": "Object",
      "fields": [
        {
          "name": "startPriority",
          "schema": "integer"
        },
        {
          "name": "startMessage",
          "schema" : "string"
        }
      ]
    }
  },
  "response": {
    "name": "startReponse",
    "schema": {
      "@type": "Object",
      "fields": [
        {
            "name": "startupTime",
            "schema": "integer" 
        },
        {
          "name": "startupMessage",
          "schema": "string"
        }
      ]
    }
  }
}

Az alábbi kódrészletek bemutatják, hogyan valósítja meg az eszköz ezt a parancsdefiníciót, beleértve a szerializálás és a deszerializálás engedélyezéséhez használt típusokat:

class startRequest
{
  public int startPriority { get; set; }
  public string startMessage { get; set; }
}

class startResponse
{
  public int startupTime { get; set; }
  public string startupMessage { get; set; }
}

// ... 

await client.SetMethodHandlerAsync("start", (MethodRequest req, object ctx) =>
{
  var startRequest = JsonConvert.DeserializeObject<startRequest>(req.DataAsJson);
  Console.WriteLine($"Received start command with priority ${startRequest.startPriority} and ${startRequest.startMessage}");

  var startResponse = new startResponse
  {
    startupTime = 123,
    startupMessage = "device started with message " + startRequest.startMessage
  };

  string responsePayload = JsonConvert.SerializeObject(startResponse);
  MethodResponse response = new MethodResponse(Encoding.UTF8.GetBytes(responsePayload), 200);
  return Task.FromResult(response);
},null);

Tipp

A kérés- és válasznevek nem szerepelnek a hálózaton keresztül továbbított szerializált hasznos adatokban.

Modellazonosító bejelentése

A modellazonosító bejelentéséhez az eszköznek szerepelnie kell a kapcsolati adatokban:

ClientOptions options = new ClientOptions();
options.setModelId(MODEL_ID);
deviceClient = new DeviceClient(deviceConnectionString, protocol, options);

A ClientOptions túlterhelés a kapcsolat inicializálásához használt összes DeviceClient metódusban elérhető.

Tipp

Modulok és IoT Edge helyett használja ModuleClient a DeviceClient.

Tipp

Ez az egyetlen alkalom, amikor egy eszköz beállíthat modellazonosítót, és nem frissíthető az eszköz csatlakoztatása után.

DPS-adattartalom

A Device Provisioning Service -t (DPS) használó eszközök a következő JSON-hasznos adatokkal tartalmazhatják a modelId kiépítési folyamat során használni kívánt eszközöket.

{
    "modelId" : "dtmi:com:example:Thermostat;1"
}

Összetevők használata

Az IoT-Plug and Play modellek összetevőinek ismertetésében leírtaknak megfelelően az eszközkészítőknek el kell dönteniük, hogy összetevőket szeretnének-e használni az eszközeik leírásához. Összetevők használatakor az eszközöknek az alábbi szakaszokban ismertetett szabályokat kell követnie.

Telemetria

Az alapértelmezett összetevőkhöz nem szükséges speciális tulajdonságot hozzáadni a telemetriai üzenethez.

Beágyazott összetevők használatakor az eszközöknek egy üzenettulajdonságot kell beállítaniuk az összetevő nevével:

private static void sendTemperatureTelemetry(String componentName) {
  double currentTemperature = temperature.get(componentName);

  Map<String, Object> payload = singletonMap("temperature", currentTemperature);

  Message message = new Message(gson.toJson(payload));
  message.setContentEncoding("utf-8");
  message.setContentTypeFinal("application/json");

  if (componentName != null) {
      message.setProperty("$.sub", componentName);
  }
  deviceClient.sendEventAsync(message, new MessageIotHubEventCallback(), message);
}

Csak olvasható tulajdonságok

Az alapértelmezett összetevőből származó tulajdonság jelentéséhez nincs szükség speciális szerkezetre:

Property reportedProperty = new Property("maxTempSinceLastReboot", 38.7);

deviceClient.sendReportedProperties(Collections.singleton(reportedProperty));

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
      "maxTempSinceLastReboot" : 38.7
  }
}

Beágyazott összetevők használatakor a tulajdonságokat az összetevő nevében kell létrehozni, és tartalmaznia kell egy jelölőt:

Map<String, Object> componentProperty = new HashMap<String, Object>() {{
    put("__t", "c");
    put("maxTemperature", 38.7);
}};

Set<Property> reportedProperty = new Property("thermostat1", componentProperty)

deviceClient.sendReportedProperties(reportedProperty);

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "thermostat1" : {  
      "__t" : "c",  
      "maxTemperature" : 38.7
     }
  }
}

Írható tulajdonságok

Ezeket a tulajdonságokat az eszköz állíthatja be, vagy a megoldás frissítheti. Ha a megoldás frissít egy tulajdonságot, az ügyfél értesítést kap visszahívásként a vagy ModuleClienta DeviceClient . Az IoT Plug and Play konvenciók követéséhez az eszköznek tájékoztatnia kell a szolgáltatást a tulajdonság sikeres fogadásáról.

Ha a tulajdonság típusa, Objecta szolgáltatásnak akkor is teljes objektumot kell küldenie az eszköznek, ha csak az objektum mezőinek egy részét frissíti. Az eszköz által küldött nyugtázásnak teljes objektumnak is kell lennie.

Írható tulajdonság jelentése

Amikor egy eszköz írható tulajdonságot jelent, annak tartalmaznia kell a ack konvenciókban meghatározott értékeket.

Írható tulajdonság jelentése az alapértelmezett összetevőből:

@AllArgsConstructor
private static class EmbeddedPropertyUpdate {
  @NonNull
  @SerializedName("value")
  public Object value;
  @NonNull
  @SerializedName("ac")
  public Integer ackCode;
  @NonNull
  @SerializedName("av")
  public Integer ackVersion;
  @SerializedName("ad")
  public String ackDescription;
}

EmbeddedPropertyUpdate completedUpdate = new EmbeddedPropertyUpdate(23.2, 200, 3, "Successfully updated target temperature");
Property reportedPropertyCompleted = new Property("targetTemperature", completedUpdate);
deviceClient.sendReportedProperties(Collections.singleton(reportedPropertyCompleted));

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "Successfully updated target temperature"
      }
  }
}

Ha írható tulajdonságot szeretne jelenteni egy beágyazott összetevőből, az ikerpéldánynak tartalmaznia kell egy jelölőt:

Map<String, Object> embeddedProperty = new HashMap<String, Object>() {{
    put("value", 23.2);
    put("ac", 200);
    put("av", 3);
    put("ad", "complete");
}};

Map<String, Object> componentProperty = new HashMap<String, Object>() {{
    put("__t", "c");
    put("targetTemperature", embeddedProperty);
}};

Set<Property> reportedProperty = new Property("thermostat1", componentProperty));

deviceClient.sendReportedProperties(reportedProperty);

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "thermostat1": {
      "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
    }
  }
}

Feliratkozás a kívánt tulajdonságfrissítésre

A szolgáltatások frissíthetik a kívánt tulajdonságokat, amelyek értesítést váltanak ki a csatlakoztatott eszközökön. Ez az értesítés tartalmazza a frissített kívánt tulajdonságokat, beleértve a frissítést azonosító verziószámot. Az eszközöknek tartalmazniuk kell ezt a verziószámot a ack szolgáltatásnak visszaküldött üzenetben.

Az alapértelmezett összetevő az egyetlen tulajdonságot látja, és létrehozza a kapott verzióval jelentett értéket ack :

private static class TargetTemperatureUpdateCallback implements TwinPropertyCallBack {

    String propertyName = "targetTemperature";

    @Override
    public void TwinPropertyCallBack(Property property, Object context) {
        double targetTemperature = ((Number)property.getValue()).doubleValue();

        EmbeddedPropertyUpdate completedUpdate = new EmbeddedPropertyUpdate(temperature, 200, property.getVersion(), "Successfully updated target temperature");
        Property reportedPropertyCompleted = new Property(propertyName, completedUpdate);
        deviceClient.sendReportedProperties(Collections.singleton(reportedPropertyCompleted));
    }
}

// ...

deviceClient.startDeviceTwin(new TwinIotHubEventCallback(), null, new TargetTemperatureUpdateCallback(), null);
Map<Property, Pair<TwinPropertyCallBack, Object>> desiredPropertyUpdateCallback =
  Collections.singletonMap(
    new Property("targetTemperature", null),
    new Pair<>(new TargetTemperatureUpdateCallback(), null));
deviceClient.subscribeToTwinDesiredProperties(desiredPropertyUpdateCallback);

A beágyazott összetevők ikereszköze a következő módon jeleníti meg a kívánt és jelentett szakaszokat:

{
  "desired" : {
    "targetTemperature": 23.2,
    "$version" : 3
  },
  "reported": {
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "Successfully updated target temperature"
      }
  }
}

A beágyazott összetevők megkapják az összetevő nevével burkolt kívánt tulajdonságokat, és jelenteni kell a ack jelentett tulajdonságot:

private static final Map<String, Double> temperature = new HashMap<>();

private static class TargetTemperatureUpdateCallback implements TwinPropertyCallBack {

    String propertyName = "targetTemperature";

    @Override
    public void TwinPropertyCallBack(Property property, Object context) {
        String componentName = (String) context;

        if (property.getKey().equalsIgnoreCase(componentName)) {
            double targetTemperature = (double) ((TwinCollection) property.getValue()).get(propertyName);

            Map<String, Object> embeddedProperty = new HashMap<String, Object>() {{
                put("value", temperature.get(componentName));
                put("ac", 200);
                put("av", property.getVersion().longValue());
                put("ad", "Successfully updated target temperature.");
            }};

            Map<String, Object> componentProperty = new HashMap<String, Object>() {{
                put("__t", "c");
                put(propertyName, embeddedProperty);
            }};

            Set<Property> completedPropertyPatch = new Property(componentName, componentProperty));

            deviceClient.sendReportedProperties(completedPropertyPatch);
        } else {
            log.debug("Property: Received an unrecognized property update from service.");
        }
    }
}

// ...

deviceClient.startDeviceTwin(new TwinIotHubEventCallback(), null, new GenericPropertyUpdateCallback(), null);
Map<Property, Pair<TwinPropertyCallBack, Object>> desiredPropertyUpdateCallback = Stream.of(
  new AbstractMap.SimpleEntry<Property, Pair<TwinPropertyCallBack, Object>>(
    new Property("thermostat1", null),
    new Pair<>(new TargetTemperatureUpdateCallback(), "thermostat1")),
  new AbstractMap.SimpleEntry<Property, Pair<TwinPropertyCallBack, Object>>(
    new Property("thermostat2", null),
    new Pair<>(new TargetTemperatureUpdateCallback(), "thermostat2"))
).collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));

deviceClient.subscribeToTwinDesiredProperties(desiredPropertyUpdateCallback);

A beágyazott összetevők ikereszköze a következő módon jeleníti meg a kívánt és jelentett szakaszokat:

{
  "desired" : {
    "thermostat1" : {
        "__t" : "c",
        "targetTemperature": 23.2,
    }
    "$version" : 3
  },
  "reported": {
    "thermostat1" : {
        "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
    }
  }
}

Parancsok

Az alapértelmezett összetevő megkapja a parancs nevét, amikor a szolgáltatás meghívta.

A beágyazott összetevők megkapják az összetevő nevével és az * elválasztóval előtaggal ellátott parancsnevet.

deviceClient.subscribeToDeviceMethod(new MethodCallback(), null, new MethodIotHubEventCallback(), null);

// ...
private static final Map<String, Double> temperature = new HashMap<>();

private static class MethodCallback implements DeviceMethodCallback {
  final String reboot = "reboot";
  final String getMaxMinReport1 = "thermostat1*getMaxMinReport";
  final String getMaxMinReport2 = "thermostat2*getMaxMinReport";

  @Override
  public DeviceMethodData call(String methodName, Object methodData, Object context) {
    String jsonRequest = new String((byte[]) methodData, StandardCharsets.UTF_8);

    switch (methodName) {
      case reboot:
        int delay = gson.fromJson(jsonRequest, Integer.class);

        Thread.sleep(delay * 1000);

        temperature.put("thermostat1", 0.0d);
        temperature.put("thermostat2", 0.0d);

        return new DeviceMethodData(200, null);

      // ...

      default:
        log.debug("Command: command=\"{}\" is not implemented, no action taken.", methodName);
          return new DeviceMethodData(404, null);
    }
  }
}

Hasznos adatok kérése és válasza

A parancsok típusokat használnak a kérések és válaszok hasznos adatainak meghatározásához. Az eszköznek deszerializálnia kell a bejövő bemeneti paramétert, és szerializálnia kell a választ.

Az alábbi példa bemutatja, hogyan implementálhat egy olyan parancsot, amely a hasznos adatokban definiált összetett típusokat tartalmazza:

{
  "@type": "Command",
  "name": "getMaxMinReport",
  "displayName": "Get Max-Min report.",
  "description": "This command returns the max, min and average temperature from the specified time to the current time.",
  "request": {
    "name": "since",
    "displayName": "Since",
    "description": "Period to return the max-min report.",
    "schema": "dateTime"
  },
  "response": {
    "name" : "tempReport",
    "displayName": "Temperature Report",
    "schema": {
      "@type": "Object",
      "fields": [
        {
          "name": "maxTemp",
          "displayName": "Max temperature",
          "schema": "double"
        },
        {
          "name": "minTemp",
          "displayName": "Min temperature",
          "schema": "double"
        },
        {
          "name" : "avgTemp",
          "displayName": "Average Temperature",
          "schema": "double"
        },
        {
          "name" : "startTime",
          "displayName": "Start Time",
          "schema": "dateTime"
        },
        {
          "name" : "endTime",
          "displayName": "End Time",
          "schema": "dateTime"
        }
      ]
    }
  }
}

Az alábbi kódrészletek bemutatják, hogyan valósítja meg az eszköz ezt a parancsdefiníciót, beleértve a szerializálás és a deszerializálás engedélyezéséhez használt típusokat:

deviceClient.subscribeToDeviceMethod(new GetMaxMinReportMethodCallback(), "getMaxMinReport", new MethodIotHubEventCallback(), "getMaxMinReport");

// ...

private static class GetMaxMinReportMethodCallback implements DeviceMethodCallback {
    String commandName = "getMaxMinReport";

    @Override
    public DeviceMethodData call(String methodName, Object methodData, Object context) {

        String jsonRequest = new String((byte[]) methodData, StandardCharsets.UTF_8);
        Date since = gson.fromJson(jsonRequest, Date.class);

        String responsePayload = String.format(
                "{\"maxTemp\": %.1f, \"minTemp\": %.1f, \"avgTemp\": %.1f, \"startTime\": \"%s\", \"endTime\": \"%s\"}",
                maxTemp,
                minTemp,
                avgTemp,
                since,
                endTime);

        return new DeviceMethodData(StatusCode.COMPLETED.value, responsePayload);
    }
}

Tipp

A kérés- és válasznevek nem szerepelnek a vezetéken keresztül továbbított szerializált hasznos adatokban.

Modellazonosító bejelentése

A modellazonosító bejelentéséhez az eszköznek szerepelnie kell a kapcsolati adatokban:

const modelIdObject = { modelId: 'dtmi:com:example:Thermostat;1' };
const client = Client.fromConnectionString(deviceConnectionString, Protocol);
await client.setOptions(modelIdObject);
await client.open();

Tipp

Modulok és IoT Edge esetén használja ModuleClient a következő helyettClient: .

Tipp

Ez az egyetlen alkalom, amikor egy eszköz beállíthat modellazonosítót, és nem frissíthető az eszköz csatlakoztatása után.

DPS-adattartalom

A Device Provisioning Service -t (DPS) használó eszközök az alábbi JSON-hasznos adatokkal tartalmazhatják a modelId kiépítési folyamat során használni kívánt eszközöket.

{
    "modelId" : "dtmi:com:example:Thermostat;1"
}

Összetevők használata

Az IoT-Plug and Play modellek Összetevőinek ismertetése című szakaszban leírtak szerint az eszközépítőknek el kell dönteniük, hogy összetevőket szeretnének-e használni az eszközeik leírásához. Összetevők használatakor az eszközöknek az alábbi szakaszokban leírt szabályokat kell követnie.

Telemetria

Az alapértelmezett összetevőhöz nincs szükség speciális tulajdonság hozzáadására a telemetriai üzenethez.

Beágyazott összetevők használatakor az eszközöknek egy üzenettulajdonságot kell beállítaniuk az összetevő nevével:

async function sendTelemetry(deviceClient, data, index, componentName) {
  const msg = new Message(data);
  if (!!(componentName)) {
    msg.properties.add(messageSubjectProperty, componentName);
  }
  msg.contentType = 'application/json';
  msg.contentEncoding = 'utf-8';
  await deviceClient.sendEvent(msg);
}

Csak olvasható tulajdonságok

Az alapértelmezett összetevő tulajdonságának jelentéséhez nincs szükség speciális szerkezetre:

const createReportPropPatch = (propertiesToReport) => {
  let patch;
  patch = { };
  patch = propertiesToReport;
  return patch;
};

deviceTwin = await client.getTwin();
patchThermostat = createReportPropPatch({
  maxTempSinceLastReboot: 38.7
});

deviceTwin.properties.reported.update(patchThermostat, function (err) {
  if (err) throw err;
});

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
      "maxTempSinceLastReboot" : 38.7
  }
}

Beágyazott összetevők használatakor a tulajdonságokat az összetevő nevében kell létrehozni, és tartalmaznia kell egy jelölőt.

helperCreateReportedPropertiesPatch = (propertiesToReport, componentName) => {
  let patch;
  if (!!(componentName)) {
    patch = { };
    propertiesToReport.__t = 'c';
    patch[componentName] = propertiesToReport;
  } else {
    patch = { };
    patch = propertiesToReport;
  }
  return patch;
};

deviceTwin = await client.getTwin();
patchThermostat1Info = helperCreateReportedPropertiesPatch({
  maxTempSinceLastReboot: 38.7,
}, 'thermostat1');

deviceTwin.properties.reported.update(patchThermostat1Info, function (err) {
  if (err) throw err;
});

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "thermostat1" : {  
      "__t" : "c",  
      "maxTempSinceLastReboot" : 38.7
     } 
  }
}

Írható tulajdonságok

Ezeket a tulajdonságokat az eszköz állíthatja be, vagy a megoldás frissítheti. Ha a megoldás frissít egy tulajdonságot, az ügyfél visszahívásként kap értesítést a vagy ModuleClienta Client . Az IoT Plug and Play konvenciók követéséhez az eszköznek tájékoztatnia kell a szolgáltatást a tulajdonság sikeres fogadásáról.

Ha a tulajdonság típusa az Object, a szolgáltatásnak teljes objektumot kell küldenie az eszköznek, még akkor is, ha csak az objektum mezőinek egy részét frissíti. Az eszköz által küldött nyugtázásnak teljes objektumnak is kell lennie.

Írható tulajdonság jelentése

Amikor egy eszköz írható tulajdonságot jelent, tartalmaznia kell a ack konvenciókban meghatározott értékeket.

Írható tulajdonság jelentése az alapértelmezett összetevőből:

patch = {
  targetTemperature:
    {
      'value': 23.2,
      'ac': 200,  // using HTTP status codes
      'ad': 'reported default value',
      'av': 0  // not read from a desired property
    }
};
deviceTwin.properties.reported.update(patch, function (err) {
  if (err) throw err;
});

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "targetTemperature": {
      "value": 23.2,
      "ac": 200,
      "av": 0,
      "ad": "reported default value"
    }
  }
}

Ha írható tulajdonságot szeretne jelenteni egy beágyazott összetevőből, az ikerpéldánynak tartalmaznia kell egy jelölőt:

patch = {
  thermostat1: {
    '__t' : 'c',
    targetTemperature: {
      'value': 23.2,
      'ac': 200,  // using HTTP status codes
      'ad': 'reported default value',
      'av': 0  // not read from a desired property
    }
  }
};
deviceTwin.properties.reported.update(patch, function (err) {
  if (err) throw err;
});

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "thermostat1": {
      "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 0,
          "ad": "complete"
      }
    }
  }
}

Feliratkozás a kívánt tulajdonságfrissítésre

A szolgáltatások frissíthetik a kívánt tulajdonságokat, amelyek értesítést váltanak ki a csatlakoztatott eszközökön. Ez az értesítés tartalmazza a frissített kívánt tulajdonságokat, beleértve a frissítést azonosító verziószámot. Az eszközöknek tartalmazniuk kell ezt a verziószámot a ack szolgáltatásnak visszaküldött üzenetben.

Az alapértelmezett összetevő az egyetlen tulajdonságot látja, és létrehozza a kapott verzióval jelentett értéket ack :

const propertyUpdateHandler = (deviceTwin, propertyName, reportedValue, desiredValue, version) => {
  const patch = createReportPropPatch(
    { [propertyName]:
      {
        'value': desiredValue,
        'ac': 200,
        'ad': 'Successfully executed patch for ' + propertyName,
        'av': version
      }
    });
  updateComponentReportedProperties(deviceTwin, patch);
};

desiredPropertyPatchHandler = (deviceTwin) => {
  deviceTwin.on('properties.desired', (delta) => {
    const versionProperty = delta.$version;

    Object.entries(delta).forEach(([propertyName, propertyValue]) => {
      if (propertyName !== '$version') {
        propertyUpdateHandler(deviceTwin, propertyName, null, propertyValue, versionProperty);
      }
    });
  });
};

A beágyazott összetevők ikereszköze a következő módon jeleníti meg a kívánt és jelentett szakaszokat:

{
  "desired" : {
    "targetTemperature": 23.2,
    "$version" : 3
  },
  "reported": {
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
  }
}

A beágyazott összetevők megkapják az összetevő nevével burkolt kívánt tulajdonságokat, és jelenteni kell a ack jelentett tulajdonságot:

const desiredPropertyPatchListener = (deviceTwin, componentNames) => {
  deviceTwin.on('properties.desired', (delta) => {
    Object.entries(delta).forEach(([key, values]) => {
      const version = delta.$version;
      if (!!(componentNames) && componentNames.includes(key)) { // then it is a component we are expecting
        const componentName = key;
        const patchForComponents = { [componentName]: {} };
        Object.entries(values).forEach(([propertyName, propertyValue]) => {
          if (propertyName !== '__t' && propertyName !== '$version') {
            const propertyContent = { value: propertyValue };
            propertyContent.ac = 200;
            propertyContent.ad = 'Successfully executed patch';
            propertyContent.av = version;
            patchForComponents[componentName][propertyName] = propertyContent;
          }
        });
        updateComponentReportedProperties(deviceTwin, patchForComponents, componentName);
      }
      else if  (key !== '$version') { // individual property for root
        const patchForRoot = { };
        const propertyContent = { value: values };
        propertyContent.ac = 200;
        propertyContent.ad = 'Successfully executed patch';
        propertyContent.av = version;
        patchForRoot[key] = propertyContent;
        updateComponentReportedProperties(deviceTwin, patchForRoot, null);
      }
    });
  });
};

Az összetevők ikereszköze a következő módon jeleníti meg a kívánt és jelentett szakaszokat:

{
  "desired" : {
    "thermostat1" : {
        "__t" : "c",
        "targetTemperature": 23.2,
    }
    "$version" : 3
  },
  "reported": {
    "thermostat1" : {
        "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
    }
  }
}

Parancsok

Az alapértelmezett összetevő megkapja a parancs nevét, amikor a szolgáltatás meghívta.

A beágyazott összetevők megkapják az összetevő nevével és az * elválasztóval előtaggal ellátott parancsnevet.

const commandHandler = async (request, response) => {
  switch (request.methodName) {
  
  // ...

  case 'thermostat1*reboot': {
    await response.send(200, 'reboot response');
    break;
  }
  default:
    await response.send(404, 'unknown method');
    break;
  }
};

client.onDeviceMethod('thermostat1*reboot', commandHandler);

Hasznos adatok kérése és válasza

A parancsok típusokat használnak a kérések és válaszok hasznos adatainak meghatározásához. Az eszköznek deszerializálnia kell a bejövő bemeneti paramétert, és szerializálnia kell a választ. Az alábbi példa bemutatja, hogyan implementálhat egy olyan parancsot, amely a hasznos adatokban definiált összetett típusokat tartalmazza:

{
  "@type": "Command",
  "name": "getMaxMinReport",
  "displayName": "Get Max-Min report.",
  "description": "This command returns the max, min and average temperature from the specified time to the current time.",
  "request": {
    "name": "since",
    "displayName": "Since",
    "description": "Period to return the max-min report.",
    "schema": "dateTime"
  },
  "response": {
    "name" : "tempReport",
    "displayName": "Temperature Report",
    "schema": {
      "@type": "Object",
      "fields": [
        {
          "name": "maxTemp",
          "displayName": "Max temperature",
          "schema": "double"
        },
        {
          "name": "minTemp",
          "displayName": "Min temperature",
          "schema": "double"
        },
        {
          "name" : "avgTemp",
          "displayName": "Average Temperature",
          "schema": "double"
        },
        {
          "name" : "startTime",
          "displayName": "Start Time",
          "schema": "dateTime"
        },
        {
          "name" : "endTime",
          "displayName": "End Time",
          "schema": "dateTime"
        }
      ]
    }
  }
}

Az alábbi kódrészletek bemutatják, hogyan valósítja meg az eszköz ezt a parancsdefiníciót, beleértve a szerializálás és a deszerializálás engedélyezéséhez használt típusokat:

class TemperatureSensor {

  // ...

  getMaxMinReportObject() {
    return {
      maxTemp: this.maxTemp,
      minTemp: this.minTemp,
      avgTemp: this.cumulativeTemperature / this.numberOfTemperatureReadings,
      endTime: (new Date(Date.now())).toISOString(),
      startTime: this.startTime
    };
  }
}

// ...

const deviceTemperatureSensor = new TemperatureSensor();

const commandHandler = async (request, response) => {
  switch (request.methodName) {
  case commandMaxMinReport: {
    console.log('MaxMinReport ' + request.payload);
    await response.send(200, deviceTemperatureSensor.getMaxMinReportObject());
    break;
  }
  default:
    await response.send(404, 'unknown method');
    break;
  }
};

Tipp

A kérés- és válasznevek nem szerepelnek a vezetéken keresztül továbbított szerializált hasznos adatokban.

Modellazonosító bejelentése

A modellazonosító bejelentéséhez az eszköznek szerepelnie kell a kapcsolati adatokban:

device_client = IoTHubDeviceClient.create_from_symmetric_key(
    symmetric_key=symmetric_key,
    hostname=registration_result.registration_state.assigned_hub,
    device_id=registration_result.registration_state.device_id,
    product_info=model_id,
)

Tipp

Modulok és IoT Edge esetén használja IoTHubModuleClient a következő helyettIoTHubDeviceClient: .

Tipp

Ez az egyetlen alkalom, amikor egy eszköz beállíthat modellazonosítót, és nem frissíthető az eszköz csatlakoztatása után.

DPS-adattartalom

A Device Provisioning Service -t (DPS) használó eszközök az alábbi JSON-hasznos adatokkal tartalmazhatják a modelId kiépítési folyamat során használni kívánt eszközöket.

{
    "modelId" : "dtmi:com:example:Thermostat;1"
}

Összetevők használata

Az IoT-Plug and Play modellek Összetevőinek ismertetése című szakaszban leírtak szerint az eszközépítőknek el kell dönteniük, hogy összetevőket szeretnének-e használni az eszközeik leírásához. Összetevők használatakor az eszközöknek az alábbi szakaszokban leírt szabályokat kell követnie.

Telemetria

Az alapértelmezett összetevőhöz nincs szükség speciális tulajdonság hozzáadására a telemetriai üzenethez.

Beágyazott összetevők használatakor az eszközöknek egy üzenettulajdonságot kell beállítaniuk az összetevő nevével:

async def send_telemetry_from_temp_controller(device_client, telemetry_msg, component_name=None):
    msg = Message(json.dumps(telemetry_msg))
    msg.content_encoding = "utf-8"
    msg.content_type = "application/json"
    if component_name:
        msg.custom_properties["$.sub"] = component_name
    await device_client.send_message(msg)
}

Csak olvasható tulajdonságok

Az alapértelmezett összetevő tulajdonságának jelentéséhez nincs szükség speciális szerkezetre:

await device_client.patch_twin_reported_properties({"maxTempSinceLastReboot": 38.7})

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
      "maxTempSinceLastReboot" : 38.7
  }
}

Beágyazott összetevők használatakor a tulajdonságokat az összetevő nevében kell létrehozni, és tartalmaznia kell egy jelölőt:

inner_dict = {}
inner_dict["targetTemperature"] = 38.7
inner_dict["__t"] = "c"
prop_dict = {}
prop_dict["thermostat1"] = inner_dict

await device_client.patch_twin_reported_properties(prop_dict)

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "thermostat1" : {  
      "__t" : "c",  
      "maxTempSinceLastReboot" : 38.7
     }
  }
}

Írható tulajdonságok

Ezeket a tulajdonságokat az eszköz állíthatja be, vagy a megoldás frissítheti. Ha a megoldás frissít egy tulajdonságot, az ügyfél visszahívásként kap értesítést a vagy IoTHubModuleClienta IoTHubDeviceClient . Az IoT Plug and Play konvenciók követéséhez az eszköznek tájékoztatnia kell a szolgáltatást a tulajdonság sikeres fogadásáról.

Ha a tulajdonság típusa az Object, a szolgáltatásnak teljes objektumot kell küldenie az eszköznek, még akkor is, ha csak az objektum mezőinek egy részét frissíti. Az eszköz által küldött nyugtázásnak teljes objektumnak is kell lennie.

Írható tulajdonság jelentése

Amikor egy eszköz írható tulajdonságot jelent, tartalmaznia kell a ack konvenciókban meghatározott értékeket.

Írható tulajdonság jelentése az alapértelmezett összetevőből:

prop_dict = {}
prop_dict["targetTemperature"] = {
    "ac": 200,
    "ad": "reported default value",
    "av": 0,
    "value": 23.2
}

await device_client.patch_twin_reported_properties(prop_dict)

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "targetTemperature": {
      "value": 23.2,
      "ac": 200,
      "av": 0,
      "ad": "reported default value"
    }
  }
}

Ha írható tulajdonságot szeretne jelenteni egy beágyazott összetevőből, az ikerpéldánynak tartalmaznia kell egy jelölőt:

inner_dict = {}
inner_dict["targetTemperature"] = {
    "ac": 200,
    "ad": "reported default value",
    "av": 0,
    "value": 23.2
}
inner_dict["__t"] = "c"
prop_dict = {}
prop_dict["thermostat1"] = inner_dict

await device_client.patch_twin_reported_properties(prop_dict)

Az ikereszköz a következő jelentett tulajdonsággal frissül:

{
  "reported": {
    "thermostat1": {
      "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 0,
          "ad": "complete"
      }
    }
  }
}

Feliratkozás a kívánt tulajdonságfrissítésre

A szolgáltatások frissíthetik a kívánt tulajdonságokat, amelyek értesítést aktiválnak a csatlakoztatott eszközökön. Ez az értesítés tartalmazza a frissített kívánt tulajdonságokat, beleértve a frissítést azonosító verziószámot. Az eszközöknek tartalmazniuk kell ezt a verziószámot a ack szolgáltatásnak visszaküldött üzenetben.

Az alapértelmezett összetevő az egyetlen tulajdonságot látja, és a kapott verzióval hozza létre a jelentést ack :

async def execute_property_listener(device_client):
    ignore_keys = ["__t", "$version"]
    while True:
        patch = await device_client.receive_twin_desired_properties_patch()  # blocking call

        version = patch["$version"]
        prop_dict = {}

        for prop_name, prop_value in patch.items():
            if prop_name in ignore_keys:
                continue
            else:
                prop_dict[prop_name] = {
                    "ac": 200,
                    "ad": "Successfully executed patch",
                    "av": version,
                    "value": prop_value,
                }

        await device_client.patch_twin_reported_properties(prop_dict)

A beágyazott összetevők ikereszköze a következő módon jeleníti meg a kívánt és jelentett szakaszokat:

{
  "desired" : {
    "targetTemperature": 23.2,
    "$version" : 3
  },
  "reported": {
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
  }
}

A beágyazott összetevők megkapják az összetevő nevével burkolt kívánt tulajdonságokat, és a jelentett tulajdonságot jelentik vissza ack :

def create_reported_properties_from_desired(patch):
    ignore_keys = ["__t", "$version"]
    component_prefix = list(patch.keys())[0]
    values = patch[component_prefix]

    version = patch["$version"]
    inner_dict = {}

    for prop_name, prop_value in values.items():
        if prop_name in ignore_keys:
            continue
        else:
            inner_dict["ac"] = 200
            inner_dict["ad"] = "Successfully executed patch"
            inner_dict["av"] = version
            inner_dict["value"] = prop_value
            values[prop_name] = inner_dict

    properties_dict = dict()
    if component_prefix:
        properties_dict[component_prefix] = values
    else:
        properties_dict = values

    return properties_dict

async def execute_property_listener(device_client):
    while True:
        patch = await device_client.receive_twin_desired_properties_patch()  # blocking call
        properties_dict = create_reported_properties_from_desired(patch)

        await device_client.patch_twin_reported_properties(properties_dict)

Az összetevők ikereszköze a következő módon jeleníti meg a kívánt és jelentett szakaszokat:

{
  "desired" : {
    "thermostat1" : {
        "__t" : "c",
        "targetTemperature": 23.2,
    }
    "$version" : 3
  },
  "reported": {
    "thermostat1" : {
        "__t" : "c",
      "targetTemperature": {
          "value": 23.2,
          "ac": 200,
          "av": 3,
          "ad": "complete"
      }
    }
  }
}

Parancsok

Az alapértelmezett összetevő megkapja a parancs nevét, amikor a szolgáltatás meghívta.

A beágyazott összetevők megkapják az összetevő nevével és * az elválasztóval előtaggal ellátott parancsnevet.

command_request = await device_client.receive_method_request("thermostat1*reboot")

Hasznos adatok kérése és válasza

A parancsok típusokat használnak a kérések és válaszok hasznos adatainak meghatározásához. Az eszköznek deszerializálnia kell a bejövő bemeneti paramétert, és szerializálnia kell a választ. Az alábbi példa bemutatja, hogyan implementálhat egy olyan parancsot, amely a hasznos adatokban definiált összetett típusokat tartalmazza:

{
  "@type": "Command",
  "name": "getMaxMinReport",
  "displayName": "Get Max-Min report.",
  "description": "This command returns the max, min and average temperature from the specified time to the current time.",
  "request": {
    "name": "since",
    "displayName": "Since",
    "description": "Period to return the max-min report.",
    "schema": "dateTime"
  },
  "response": {
    "name" : "tempReport",
    "displayName": "Temperature Report",
    "schema": {
      "@type": "Object",
      "fields": [
        {
          "name": "maxTemp",
          "displayName": "Max temperature",
          "schema": "double"
        },
        {
          "name": "minTemp",
          "displayName": "Min temperature",
          "schema": "double"
        },
        {
          "name" : "avgTemp",
          "displayName": "Average Temperature",
          "schema": "double"
        },
        {
          "name" : "startTime",
          "displayName": "Start Time",
          "schema": "dateTime"
        },
        {
          "name" : "endTime",
          "displayName": "End Time",
          "schema": "dateTime"
        }
      ]
    }
  }
}

Az alábbi kódrészletek bemutatják, hogyan valósítja meg az eszköz ezt a parancsdefiníciót, beleértve a szerializálás és a deszerializálás engedélyezéséhez használt típusokat:

def create_max_min_report_response(values):
    response_dict = {
        "maxTemp": max_temp,
        "minTemp": min_temp,
        "avgTemp": sum(avg_temp_list) / moving_window_size,
        "startTime": (datetime.now() - timedelta(0, moving_window_size * 8)).isoformat(),
        "endTime": datetime.now().isoformat(),
    }
    # serialize response dictionary into a JSON formatted str
    response_payload = json.dumps(response_dict, default=lambda o: o.__dict__, sort_keys=True)
    return response_payload

Tipp

A kérés- és válasznevek nem szerepelnek a hálózaton keresztül továbbított szerializált hasznos adatokban.

Következő lépések

Most, hogy megismerkedett az IoT Plug and Play eszközfejlesztéssel, íme néhány további forrás: