IoT Tak Çalıştır cihaz geliştirici kılavuzu

IoT Tak Çalıştır, özelliklerini Azure IoT uygulamalarına tanıtan IoT cihazları oluşturmanıza olanak tanır. IoT Tak Çalıştır cihazlar, müşteri tarafından IoT Central gibi IoT Tak Çalıştır etkin uygulamalara bağlandığında el ile yapılandırma gerektirmez.

IoT cihazını doğrudan modülleri veya IoT Edge modüllerini kullanarak uygulayabilirsiniz.

Bu kılavuzda, IoT Tak Çalıştır kurallarını izleyen bir cihaz, modül veya IoT Edge modülü oluşturmak için gereken temel adımlar açıklanmaktadır.

IoT Tak Çalıştır cihazı, modülü veya IoT Edge modülünü oluşturmak için şu adımları izleyin:

  1. Cihazınızın Azure IoT Hub'a bağlanmak için WebSockets üzerinden MQTT veya MQTT protokollerini kullandığından emin olun.
  2. Cihazınızı açıklamak için bir Digital Twins Tanım Dili (DTDL) modeli oluşturun. Daha fazla bilgi edinmek için bkz. IoT Tak Çalıştır modellerdeki bileşenleri anlama.
  3. Cihaz bağlantısının bir parçası olarak öğesini duyurmak model-id için cihazınızı veya modülünüzü güncelleştirin.
  4. IoT Tak Çalıştır kurallarını izleyen telemetri, özellikler ve komutlar uygulama

Cihazınız veya modül uygulamanız hazır olduktan sonra, cihazın IoT Tak Çalıştır kurallarına uyduğunu doğrulamak için Azure IoT gezginini kullanın.

Model Kimliği duyurusu

Model kimliğini duyurmak için cihazın bağlantı bilgilerine dahil etmesi gerekir:

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);

İpucu

Modüller ve IoT Edge için yerine IoTHubDeviceClient_LLkullanınIoTHubModuleClient_LL.

İpucu

Bir cihazın model kimliğini ayarlayabildiği tek zamandır, cihaz bağlandıktan sonra güncelleştirilemez.

DPS yükü

Cihaz Sağlama Hizmeti'ni (DPS) kullanan cihazlar, aşağıdaki JSON yükünü kullanarak sağlama işlemi sırasında kullanılacak öğesini içerebilir modelId :

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

Bileşenleri kullanma

IoT Tak Çalıştır modellerindeki bileşenleri anlama bölümünde açıklandığı gibi, cihazlarınızı açıklamak için bileşenleri kullanmak isteyip istemediğinize karar vermelisiniz. Bileşenleri kullandığınızda, cihazların aşağıdaki bölümlerde açıklanan kurallara uyması gerekir:

Telemetri

Varsayılan bileşen telemetri iletisine özel bir özellik eklenmesini gerektirmez.

İç içe bileşenleri kullandığınızda, cihazların bileşen adıyla bir ileti özelliği ayarlaması gerekir:

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);

Salt okunur özellikler

Varsayılan bileşenden bir özelliği raporlamak için özel bir yapı gerekmez:

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));

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe bileşenleri kullandığınızda, bileşen adında özellikler oluşturun ve bir işaretçi ekleyin:

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);

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

Yazılabilir özellikler

Bu özellikler cihaz tarafından ayarlanabilir veya arka uç uygulaması tarafından güncelleştirilebilir. Arka uç uygulaması bir özelliği güncelleştirirse, istemci veya ModuleClientiçinde DeviceClient geri çağırma olarak bir bildirim alır. IoT Tak Çalıştır kurallarını izlemek için cihazın hizmete özelliğin başarıyla alındığını bildirmesi gerekir.

Özellik türü ise Object, nesne alanlarının yalnızca bir alt kümesini güncelleştirse bile hizmetin cihaza tam bir nesne göndermesi gerekir. Cihazın gönderdiği bildirim tam bir nesne de olabilir.

Yazılabilir özelliği bildirme

Bir cihaz yazılabilir bir özellik bildirdiğinde, kurallarda tanımlanan değerleri içermelidir ack .

Varsayılan bileşenden yazılabilir bir özellik bildirmek için:

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);

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe yerleştirilmiş bir bileşenden yazılabilir bir özelliği bildirmek için ikizin bir işaretçi içermesi gerekir:

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);

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İstenen özellik güncelleştirmelerine abone olma

Hizmetler, bağlı cihazlarda bildirim tetikleyen istenen özellikleri güncelleştirebilir. Bu bildirim, güncelleştirmeyi tanımlayan sürüm numarası da dahil olmak üzere güncelleştirilmiş istenen özellikleri içerir. Cihazlar hizmete geri gönderilen iletiye ack bu sürüm numarasını içermelidir.

Varsayılan bileşen tek özelliği görür ve alınan sürümle bildirileni ack oluşturur:

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))

İç içe yerleştirilmiş bir bileşenin cihaz ikizi istenen ve bildirilen bölümleri aşağıdaki gibi gösterir:

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

İç içe yerleştirilmiş bir bileşen, bileşen adıyla sarmalanan istenen özellikleri alır ve bildirilen özelliği geri ack raporlamalıdır:

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);
}

İç içe yerleştirilmiş bir bileşenin cihaz ikizi istenen ve bildirilen bölümleri aşağıdaki gibi gösterir:

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

Komutlar

Varsayılan bileşen, hizmet tarafından çağrıldığında komut adını alır.

İç içe yerleştirilmiş bir bileşen, bileşen adı ve * ayırıcı ile ön ekli komut adını alır.

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);

İstek ve yanıt yükleri

Komutlar, istek ve yanıt yüklerini tanımlamak için türleri kullanır. Bir cihazın gelen giriş parametresinin seri durumdan çıkarılması ve yanıtı seri hale getirmesi gerekir.

Aşağıdaki örnekte, yüklerde tanımlanan karmaşık türlerle bir komutun nasıl uygulandığı gösterilmektedir:

{
  "@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"
        }
      ]
    }
  }
}

Aşağıdaki kod parçacıkları, serileştirmeyi ve seri durumdan çıkarmayı etkinleştirmek için kullanılan türler de dahil olmak üzere bir cihazın bu komut tanımını nasıl uyguladığını gösterir:

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;
}

İpucu

İstek ve yanıt adları, kablo üzerinden iletilen serileştirilmiş yüklerde mevcut değildir.

SDK

Bu makaledeki kod parçacıkları, Eclipse ThreadX için Azure IoT Ara Yazılım eklentisini kullanan örnekleri temel alır. Eklenti, Eclipse ThreadX ile Embedded C için Azure SDK arasında bir bağlama katmanıdır.

Bu makaledeki kod parçacıkları aşağıdaki örnekleri temel alır:

Model Kimliği duyurusu

Model kimliğini duyurmak için cihazın bağlantı bilgilerine dahil etmesi gerekir:

#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);

İpucu

Bir cihazın model kimliğini ayarlayabildiği tek zamandır, cihaz bağlandıktan sonra güncelleştirilemez.

DPS yükü

Cihaz Sağlama Hizmeti'ni (DPS) kullanan cihazlar, aşağıdaki JSON yükünü kullanarak sağlama işlemi sırasında kullanılacak öğesini içerebilir modelId :

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

Örnek, bu yükü göndermek için aşağıdaki kodu kullanır:

#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);

Bileşenleri kullanma

IoT Tak Çalıştır modellerindeki bileşenleri anlama bölümünde açıklandığı gibi, cihazlarınızı açıklamak için bileşenleri kullanmak isteyip istemediğinize karar vermeniz gerekir. Bileşenleri kullandığınızda, cihazların aşağıdaki bölümlerde açıklanan kurallara uyması gerekir. Bileşenler için IoT Tak Çalıştır kurallarıyla çalışmayı basitleştirmek için örnekler nx_azure_iot_hub_client.h dosyasındaki yardımcı işlevleri kullanır.

Telemetri

Varsayılan bileşen telemetri iletisine özel bir özellik eklenmesini gerektirmez.

İç içe bileşenleri kullandığınızda, cihazların bileşen adıyla bir ileti özelliği ayarlaması gerekir. Aşağıdaki kod parçacığında component_name_ptr , gibi thermostat1bir bileşenin adıdır. nx_azure_iot_pnp_helpers.h dosyasında tanımlanan yardımcı işlevinx_azure_iot_pnp_helper_telemetry_message_create, ileti özelliğini bileşen adıyla ekler:

#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);
}

Salt okunur özellikler

Varsayılan bileşenden bir özelliği raporlamak için özel bir yapı gerekmez:

#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))))
{
    // ...
}

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe bileşenleri kullandığınızda, özelliklerin bileşen adı içinde oluşturulması ve bir işaretçi içermesi gerekir. Aşağıdaki kod parçacığında component_name_ptr , gibi thermostat1bir bileşenin adıdır. nx_azure_iot_pnp_helpers.h dosyasında tanımlanan yardımcı işlevi nx_azure_iot_pnp_helper_build_reported_property bildirilen özelliği doğru biçimde oluşturur:

#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))))
    {
        // ...
    }

    // ...
}

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

Yazılabilir özellikler

Bu özellikler cihaz tarafından ayarlanabilir veya arka uç uygulaması tarafından güncelleştirilebilir. IoT Tak Çalıştır kurallarını izlemek için cihazın hizmete özelliğin başarıyla alındığını bildirmesi gerekir.

Yazılabilir özelliği bildirme

Bir cihaz yazılabilir bir özellik bildirdiğinde, kurallarda tanımlanan değerleri içermelidir ack .

Varsayılan bileşenden yazılabilir bir özellik bildirmek için:

#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
    // ...
}

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe yerleştirilmiş bir bileşenden yazılabilir bir özelliği raporlamak için ikizin bir işaretçi içermesi ve özelliklerin bileşen adı içinde oluşturulması gerekir. Aşağıdaki kod parçacığında component_name_ptr , gibi thermostat1bir bileşenin adıdır. nx_azure_iot_pnp_helpers.h dosyasında tanımlanan yardımcı işlevi nx_azure_iot_pnp_helper_build_reported_property_with_status bildirilen özellik yükünü oluşturur:

#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
    {
        // ...
    }

    // ...
}

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İstenen özellik güncelleştirmelerine abone olma

Hizmetler, bağlı cihazlarda bildirim tetikleyen istenen özellikleri güncelleştirebilir. Bu bildirim, güncelleştirilmiş istenen özellikleri ve güncelleştirmeyi tanımlayan sürüm numarasını içerir. Cihazlar hizmete geri gönderilen iletiye ack bu sürüm numarasını içermelidir.

Varsayılan bileşen tek özelliği görür ve alınan sürümle bildirileni ack oluşturur:

#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);

    // ...
}

İç içe yerleştirilmiş bir bileşen, bileşen adıyla sarmalanan istenen özellikleri alır ve alınan sürümle bildirileni ack oluşturur:

#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);

    // ...
}

İç içe yerleştirilmiş bir bileşenin cihaz ikizi istenen ve bildirilen bölümleri aşağıdaki gibi gösterir:

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

Komutlar

Varsayılan bileşen, hizmet tarafından çağrıldığında komut adını alır.

İç içe yerleştirilmiş bir bileşen, bileşen adı ve * ayırıcı ile ön ekli komut adını alır. Aşağıdaki kod parçacığında, nx_azure_iot_pnp_helpers.h dosyasında tanımlanan yardımcı işlevinx_azure_iot_pnp_helper_command_name_parse, cihazın hizmetten aldığı iletiden bileşen adını ve komut adını ayrıştırmaktadır:

#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;
        }

        // ...
    }
}

İstek ve yanıt yükleri

Komutlar, istek ve yanıt yüklerini tanımlamak için türleri kullanır. Bir cihazın gelen giriş parametresinin seri durumdan çıkarılması ve yanıtı seri hale getirmesi gerekir.

Aşağıdaki örnekte, yüklerde tanımlanan karmaşık türlerle bir komutun nasıl uygulandığı gösterilmektedir:

{
  "@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"
        }
      ]
    }
  }
}

Aşağıdaki kod parçacıkları, serileştirmeyi ve seri durumdan çıkarmayı etkinleştirmek için kullanılan türler de dahil olmak üzere bir cihazın bu komut tanımını nasıl uyguladığını gösterir:

#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);
}

İpucu

İstek ve yanıt adları, kablo üzerinden iletilen serileştirilmiş yüklerde mevcut değildir.

Model Kimliği duyurusu

Model kimliğini duyurmak için cihazın bağlantı bilgilerine dahil etmesi gerekir:

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

Yeni ClientOptions aşırı yükleme, bağlantı başlatmak için kullanılan tüm DeviceClient yöntemlerde kullanılabilir.

İpucu

Modüller ve IoT Edge için yerine DeviceClientkullanınModuleClient.

İpucu

Bir cihazın model kimliğini ayarlayabildiği tek zamandır, cihaz bağlandıktan sonra güncelleştirilemez.

DPS yükü

Cihaz Sağlama Hizmeti'ni (DPS) kullanan cihazlar, aşağıdaki JSON yükünü kullanarak sağlama işlemi sırasında kullanılacak öğesini içerebilir modelId :

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

Bileşenleri kullanma

IoT Tak Çalıştır modellerindeki bileşenleri anlama bölümünde açıklandığı gibi, cihazlarınızı açıklamak için bileşenleri kullanmak isteyip istemediğinize karar vermeniz gerekir. Bileşenleri kullandığınızda, cihazların aşağıdaki bölümlerde açıklanan kurallara uyması gerekir.

Telemetri

Varsayılan bileşen telemetri iletisine özel bir özellik eklenmesini gerektirmez.

İç içe bileşenleri kullandığınızda, cihazların bileşen adıyla bir ileti özelliği ayarlaması gerekir:

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);
}

Salt okunur özellikler

Varsayılan bileşenden bir özelliği raporlamak için özel bir yapı gerekmez:

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

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe bileşenleri kullandığınızda, bileşen adında özellikler oluşturun ve bir işaretçi ekleyin:

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);

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

Yazılabilir özellikler

Bu özellikler cihaz tarafından ayarlanabilir veya arka uç uygulaması tarafından güncelleştirilebilir. Arka uç uygulaması bir özelliği güncelleştirirse, istemci veya ModuleClientiçinde DeviceClient geri çağırma olarak bir bildirim alır. IoT Tak Çalıştır kurallarını izlemek için cihazın hizmete özelliğin başarıyla alındığını bildirmesi gerekir.

Özellik türü ise Object, nesne alanlarının yalnızca bir alt kümesini güncelleştirse bile hizmetin cihaza tam bir nesne göndermesi gerekir. Cihazın gönderdiği onay da tam bir nesne olmalıdır.

Yazılabilir özelliği bildirme

Bir cihaz yazılabilir bir özellik bildirdiğinde, kurallarda tanımlanan değerleri içermelidir ack .

Varsayılan bileşenden yazılabilir bir özellik bildirmek için:

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);

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe yerleştirilmiş bir bileşenden yazılabilir bir özelliği bildirmek için ikizin bir işaretçi içermesi gerekir:

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);

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İstenen özellik güncelleştirmelerine abone olma

Hizmetler, bağlı cihazlarda bildirim tetikleyen istenen özellikleri güncelleştirebilir. Bu bildirim, güncelleştirmeyi tanımlayan sürüm numarası da dahil olmak üzere güncelleştirilmiş istenen özellikleri içerir. Cihazlar hizmete geri gönderilen iletiye ack bu sürüm numarasını içermelidir.

Varsayılan bileşen tek özelliği görür ve alınan sürümle bildirileni ack oluşturur:

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);

İç içe yerleştirilmiş bir bileşenin cihaz ikizi istenen ve bildirilen bölümleri aşağıdaki gibi gösterir:

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

İç içe yerleştirilmiş bir bileşen, bileşen adıyla sarmalanan istenen özellikleri alır ve bildirilen özelliği geri ack raporlamalıdır:

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);

İç içe yerleştirilmiş bir bileşenin cihaz ikizi istenen ve bildirilen bölümleri aşağıdaki gibi gösterir:

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

Komutlar

Varsayılan bileşen, hizmet tarafından çağrıldığında komut adını alır.

İç içe yerleştirilmiş bir bileşen, bileşen adı ve * ayırıcı ile ön ekli komut adını alır.

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

İstek ve yanıt yükleri

Komutlar, istek ve yanıt yüklerini tanımlamak için türleri kullanır. Bir cihazın gelen giriş parametresinin seri durumdan çıkarılması ve yanıtı seri hale getirmesi gerekir.

Aşağıdaki örnekte, yüklerde tanımlanan karmaşık türlerle bir komutun nasıl uygulandığı gösterilmektedir:

{
  "@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"
        }
      ]
    }
  }
}

Aşağıdaki kod parçacıkları, serileştirmeyi ve seri durumdan çıkarmayı etkinleştirmek için kullanılan türler de dahil olmak üzere bir cihazın bu komut tanımını nasıl uyguladığını gösterir:

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);

İpucu

İstek ve yanıt adları, kablo üzerinden iletilen serileştirilmiş yüklerde mevcut değildir.

Model Kimliği duyurusu

Model kimliğini duyurmak için cihazın bağlantı bilgilerine dahil etmesi gerekir:

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

Aşırı ClientOptions yükleme, bağlantı başlatmak için kullanılan tüm DeviceClient yöntemlerde kullanılabilir.

İpucu

Modüller ve IoT Edge için yerine DeviceClientkullanınModuleClient.

İpucu

Bir cihazın model kimliğini ayarlayabildiği tek zamandır, cihaz bağlandıktan sonra güncelleştirilemez.

DPS yükü

Cihaz Sağlama Hizmeti'ni (DPS) kullanan cihazlar, aşağıdaki JSON yükünü kullanarak sağlama işlemi sırasında kullanılacak öğesini içerebilir modelId .

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

Bileşenleri kullanma

IoT Tak Çalıştır modellerindeki bileşenleri anlama bölümünde açıklandığı gibi, cihazlarınızı açıklamak için bileşenleri kullanmak isteyip istemediğinize karar vermelisiniz. Bileşenleri kullandığınızda, cihazların aşağıdaki bölümlerde açıklanan kurallara uyması gerekir.

Telemetri

Varsayılan bileşen telemetri iletisine özel bir özellik eklenmesini gerektirmez.

İç içe bileşenleri kullandığınızda, cihazın bileşen adıyla bir ileti özelliği ayarlaması gerekir:

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);
}

Salt okunur özellikler

Varsayılan bileşenden bir özelliği raporlamak için özel bir yapı gerekmez:

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

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

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe bileşenleri kullandığınızda, bileşen adında özellikler oluşturun ve bir işaretçi ekleyin:

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);

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

Yazılabilir özellikler

Bu özellikler cihaz tarafından ayarlanabilir veya arka uç uygulaması tarafından güncelleştirilebilir. Arka uç uygulaması bir özelliği güncelleştirirse, istemci veya ModuleClientiçinde DeviceClient geri çağırma olarak bir bildirim alır. IoT Tak Çalıştır kurallarını izlemek için cihazın hizmete özelliğin başarıyla alındığını bildirmesi gerekir.

Özellik türü ise Object, nesne alanlarının yalnızca bir alt kümesini güncelleştirse bile hizmetin cihaza tam bir nesne göndermesi gerekir. Cihazın gönderdiği onay da tam bir nesne olmalıdır.

Yazılabilir özelliği bildirme

Bir cihaz yazılabilir bir özellik bildirdiğinde, kurallarda tanımlanan değerleri içermelidir ack .

Varsayılan bileşenden yazılabilir bir özellik bildirmek için:

@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));

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe yerleştirilmiş bir bileşenden yazılabilir bir özelliği bildirmek için ikizin bir işaretçi içermesi gerekir:

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);

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İstenen özellik güncelleştirmelerine abone olma

Hizmetler, bağlı cihazlarda bildirim tetikleyen istenen özellikleri güncelleştirebilir. Bu bildirim, güncelleştirmeyi tanımlayan sürüm numarası da dahil olmak üzere güncelleştirilmiş istenen özellikleri içerir. Cihazlar hizmete geri gönderilen iletiye ack bu sürüm numarasını içermelidir.

Varsayılan bileşen tek özelliği görür ve alınan sürümle bildirileni ack oluşturur:

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);

İç içe yerleştirilmiş bir bileşenin cihaz ikizi istenen ve bildirilen bölümleri aşağıdaki gibi gösterir:

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

İç içe yerleştirilmiş bir bileşen, bileşen adıyla sarmalanan istenen özellikleri alır ve bildirilen özelliği geri ack raporlamalıdır:

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);

İç içe yerleştirilmiş bir bileşenin cihaz ikizi istenen ve bildirilen bölümleri aşağıdaki gibi gösterir:

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

Komutlar

Varsayılan bileşen, hizmet tarafından çağrıldığında komut adını alır.

İç içe yerleştirilmiş bir bileşen, bileşen adı ve * ayırıcı ile ön ekli komut adını alır.

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);
    }
  }
}

İstek ve yanıt yükleri

Komutlar, istek ve yanıt yüklerini tanımlamak için türleri kullanır. Bir cihazın gelen giriş parametresinin seri durumdan çıkarılması ve yanıtı seri hale getirmesi gerekir.

Aşağıdaki örnekte, yüklerde tanımlanan karmaşık türlerle bir komutun nasıl uygulandığı gösterilmektedir:

{
  "@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"
        }
      ]
    }
  }
}

Aşağıdaki kod parçacıkları, serileştirmeyi ve seri durumdan çıkarmayı etkinleştirmek için kullanılan türler de dahil olmak üzere bir cihazın bu komut tanımını nasıl uyguladığını gösterir:

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);
    }
}

İpucu

İstek ve yanıt adları, kablo üzerinden iletilen serileştirilmiş yüklerde mevcut değildir.

Model Kimliği duyurusu

Model kimliğini duyurmak için cihazın bağlantı bilgilerine dahil etmesi gerekir:

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

İpucu

Modüller ve IoT Edge için yerine ClientkullanınModuleClient.

İpucu

Bir cihazın model kimliğini ayarlayabildiği tek zamandır, cihaz bağlandıktan sonra güncelleştirilemez.

DPS yükü

Cihaz Sağlama Hizmeti'ni (DPS) kullanan cihazlar, aşağıdaki JSON yükünü kullanarak sağlama işlemi sırasında kullanılacak öğesini içerebilir modelId .

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

Bileşenleri kullanma

IoT Tak Çalıştır modellerindeki bileşenleri anlama bölümünde açıklandığı gibi, cihazlarınızı açıklamak için bileşenleri kullanmak isteyip istemediğinize karar vermeniz gerekir. Bileşenleri kullandığınızda, cihazların aşağıdaki bölümlerde açıklanan kurallara uyması gerekir.

Telemetri

Varsayılan bileşen telemetri iletisine özel bir özellik eklenmesini gerektirmez.

İç içe bileşenleri kullandığınızda, cihazların bileşen adıyla bir ileti özelliği ayarlaması gerekir:

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);
}

Salt okunur özellikler

Varsayılan bileşenden bir özelliği raporlamak için özel bir yapı gerekmez:

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;
});

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe bileşenleri kullandığınızda, özelliklerin bileşen adı içinde oluşturulması ve bir işaretçi içermesi gerekir:

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;
});

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

Yazılabilir özellikler

Bu özellikler cihaz tarafından ayarlanabilir veya arka uç uygulaması tarafından güncelleştirilebilir. Arka uç uygulaması bir özelliği güncelleştirirse, istemci veya ModuleClientiçinde Client geri çağırma olarak bir bildirim alır. IoT Tak Çalıştır kurallarını izlemek için cihazın hizmete özelliğin başarıyla alındığını bildirmesi gerekir.

Özellik türü ise Object, nesne alanlarının yalnızca bir alt kümesini güncelleştirse bile hizmetin cihaza tam bir nesne göndermesi gerekir. Cihazın gönderdiği onay da tam bir nesne olmalıdır.

Yazılabilir özelliği bildirme

Bir cihaz yazılabilir bir özellik bildirdiğinde, kurallarda tanımlanan değerleri içermelidir ack .

Varsayılan bileşenden yazılabilir bir özellik bildirmek için:

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;
});

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe yerleştirilmiş bir bileşenden yazılabilir bir özelliği bildirmek için ikizin bir işaretçi içermesi gerekir:

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;
});

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İstenen özellik güncelleştirmelerine abone olma

Hizmetler, bağlı cihazlarda bildirim tetikleyen istenen özellikleri güncelleştirebilir. Bu bildirim, güncelleştirmeyi tanımlayan sürüm numarası da dahil olmak üzere güncelleştirilmiş istenen özellikleri içerir. Cihazlar hizmete geri gönderilen iletiye ack bu sürüm numarasını içermelidir.

Varsayılan bileşen tek özelliği görür ve alınan sürümle bildirileni ack oluşturur:

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);
      }
    });
  });
};

İç içe yerleştirilmiş bir bileşenin cihaz ikizi istenen ve bildirilen bölümleri aşağıdaki gibi gösterir:

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

İç içe yerleştirilmiş bir bileşen, bileşen adıyla sarmalanan istenen özellikleri alır ve bildirilen özelliği geri ack raporlamalıdır:

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);
      }
    });
  });
};

Bileşenler için cihaz ikizi istenen ve bildirilen bölümleri aşağıdaki gibi gösterir:

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

Komutlar

Varsayılan bileşen, hizmet tarafından çağrıldığında komut adını alır.

İç içe yerleştirilmiş bir bileşen, bileşen adı ve * ayırıcı ile ön ekli komut adını alır.

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);

İstek ve yanıt yükleri

Komutlar, istek ve yanıt yüklerini tanımlamak için türleri kullanır. Bir cihazın gelen giriş parametresinin seri durumdan çıkarılması ve yanıtı seri hale getirmesi gerekir.

Aşağıdaki örnekte, yüklerde tanımlanan karmaşık türlerle bir komutun nasıl uygulandığı gösterilmektedir:

{
  "@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"
        }
      ]
    }
  }
}

Aşağıdaki kod parçacıkları, serileştirmeyi ve seri durumdan çıkarmayı etkinleştirmek için kullanılan türler de dahil olmak üzere bir cihazın bu komut tanımını nasıl uyguladığını gösterir:

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;
  }
};

İpucu

İstek ve yanıt adları, kablo üzerinden iletilen serileştirilmiş yüklerde mevcut değildir.

Model Kimliği duyurusu

Model kimliğini duyurmak için cihazın bağlantı bilgilerine dahil etmesi gerekir:

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,
)

İpucu

Modüller ve IoT Edge için yerine IoTHubDeviceClientkullanınIoTHubModuleClient.

İpucu

Bir cihazın model kimliğini ayarlayabildiği tek zamandır, cihaz bağlandıktan sonra güncelleştirilemez.

DPS yükü

Cihaz Sağlama Hizmeti'ni (DPS) kullanan cihazlar, aşağıdaki JSON yükünü kullanarak sağlama işlemi sırasında kullanılacak öğesini içerebilir modelId .

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

Bileşenleri kullanma

IoT Tak Çalıştır modellerindeki bileşenleri anlama bölümünde açıklandığı gibi, cihazlarınızı açıklamak için bileşenleri kullanmak isteyip istemediğinize karar vermeniz gerekir. Bileşenleri kullandığınızda, cihazların aşağıdaki bölümlerde açıklanan kurallara uyması gerekir.

Telemetri

Varsayılan bileşen telemetri iletisine özel bir özellik eklenmesini gerektirmez.

İç içe bileşenleri kullandığınızda, cihazların bileşen adıyla bir ileti özelliği ayarlaması gerekir:

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)

Salt okunur özellikler

Varsayılan bileşenden bir özelliği raporlamak için özel bir yapı gerekmez:

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

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe bileşenler kullanılırken, özelliklerin bileşen adı içinde oluşturulması ve bir işaretçi içermesi gerekir:

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)

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

Yazılabilir özellikler

Bu özellikler cihaz tarafından ayarlanabilir veya arka uç uygulaması tarafından güncelleştirilebilir. Arka uç uygulaması bir özelliği güncelleştirirse, istemci veya IoTHubModuleClientiçinde IoTHubDeviceClient geri çağırma olarak bir bildirim alır. IoT Tak Çalıştır kurallarını izlemek için cihazın hizmete özelliğin başarıyla alındığını bildirmesi gerekir.

Özellik türü ise Object, nesne alanlarının yalnızca bir alt kümesini güncelleştirse bile hizmetin cihaza tam bir nesne göndermesi gerekir. Cihazın gönderdiği onay da tam bir nesne olmalıdır.

Yazılabilir özelliği bildirme

Bir cihaz yazılabilir bir özellik bildirdiğinde, kurallarda tanımlanan değerleri içermelidir ack .

Varsayılan bileşenden yazılabilir bir özellik bildirmek için:

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)

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İç içe yerleştirilmiş bir bileşenden yazılabilir bir özelliği bildirmek için ikizin bir işaretçi içermesi gerekir:

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)

Cihaz ikizi aşağıdaki bildirilen özellik ile güncelleştirilir:

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

İstenen özellik güncelleştirmelerine abone olma

Hizmetler, bağlı cihazlarda bildirim tetikleyen istenen özellikleri güncelleştirebilir. Bu bildirim, güncelleştirmeyi tanımlayan sürüm numarası da dahil olmak üzere güncelleştirilmiş istenen özellikleri içerir. Cihazlar hizmete geri gönderilen iletiye ack bu sürüm numarasını içermelidir.

Varsayılan bileşen tek özelliği görür ve alınan sürümle bildirileni ack oluşturur:

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)

İç içe yerleştirilmiş bir bileşenin cihaz ikizi istenen ve bildirilen bölümleri aşağıdaki gibi gösterir:

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

İç içe yerleştirilmiş bir bileşen, bileşen adıyla sarmalanan istenen özellikleri alır ve bildirilen özelliği geri ack raporlamalıdır:

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)

Bileşenler için cihaz ikizi istenen ve bildirilen bölümleri aşağıdaki gibi gösterir:

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

Komutlar

Varsayılan bileşen, hizmet tarafından çağrıldığında komut adını alır.

İç içe yerleştirilmiş bir bileşen, bileşen adı ve * ayırıcı ile ön ekli komut adını alır.

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

İstek ve yanıt yükleri

Komutlar, istek ve yanıt yüklerini tanımlamak için türleri kullanır. Bir cihazın gelen giriş parametresinin seri durumdan çıkarılması ve yanıtı seri hale getirmesi gerekir.

Aşağıdaki örnekte, yüklerde tanımlanan karmaşık türlerle bir komutun nasıl uygulandığı gösterilmektedir:

{
  "@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"
        }
      ]
    }
  }
}

Aşağıdaki kod parçacıkları, serileştirmeyi ve seri durumdan çıkarmayı etkinleştirmek için kullanılan türler de dahil olmak üzere bir cihazın bu komut tanımını nasıl uyguladığını gösterir:

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

İpucu

İstek ve yanıt adları, kablo üzerinden iletilen serileştirilmiş yüklerde mevcut değildir.

Sonraki adımlar

Artık IoT Tak Çalıştır cihaz geliştirme hakkında bilgi edindiğinize göre, diğer bazı kaynaklar şunlardır: