Tutorial: Criar e conectar um aplicativo cliente ao seu aplicativo do Azure IoT Central

Este tutorial mostra como conectar um aplicativo cliente ao seu aplicativo do Azure IoT Central. O aplicativo simula o comportamento de um dispositivo controlador de temperatura. Quando o aplicativo se conecta ao IoT Central, ele envia a ID de modelo do dispositivo controlador de temperatura. O IoT Central usa a ID do modelo para recuperar o modelo de dispositivo e criar um modelo de dispositivo para você. Adicione exibições ao modelo de dispositivo para permitir que um operador interaja com um dispositivo.

Neste tutorial, você aprenderá como:

  • Criar e executar o código do dispositivo e ver se ele se conecta ao seu aplicativo do IoT Central.
  • Veja a telemetria simulada enviada do dispositivo.
  • Adicionar exibições personalizadas a um modelo de dispositivo.
  • Publicar o modelo de dispositivo.
  • Usar uma exibição para gerenciar as propriedades do dispositivo.
  • Chamar um comando para controlar o dispositivo.

Browse code

Pré-requisitos

Para concluir as etapas deste tutorial, você precisará do seguinte:

Você pode executar este tutorial no Linux ou no Windows. Os comandos do shell neste tutorial seguem a convenção do Linux para os separadores de caminho '/', se você está acompanhando no Windows, não deixe de trocar esses separadores por '\'.

Os pré-requisitos diferem por sistema operacional:

Linux

Este tutorial pressupõe que você esteja usando o Ubuntu Linux. As etapas deste tutorial foram testadas com o Ubuntu 18.04.

Para concluir este tutorial no Linux, instale o seguinte software em um ambiente Linux local:

Instale o GCC, o Git, o CMake e todas as dependências exigidas usando o comando apt-get:

sudo apt-get update
sudo apt-get install -y git cmake build-essential curl libcurl4-openssl-dev libssl-dev uuid-dev

Verifique se a versão do cmake é superior a 2.8.12 e a versão do GCC é superior a 4.4.7.

cmake --version
gcc --version

Windows

Para realizar este tutorial no Windows, instale o seguinte software em um ambiente Windows local:

Baixar o código

Neste tutorial, você vai preparar um ambiente de desenvolvimento que pode ser usado para clonar e compilar o SDK do C do dispositivo do Hub IoT do Azure.

Abra um prompt de comando no diretório de sua escolha. Execute o seguinte comando para clonar o repositório GitHub dos SDKs de C e Bibliotecas do IoT do Azure nesta localização:

git clone https://github.com/Azure/azure-iot-sdk-c.git
cd azure-iot-sdk-c
git submodule update --init

Essa operação deve demorar alguns minutos.

Examine o código

Na cópia do SDK do Microsoft Azure IoT para C baixado anteriormente, abra os arquivos azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_temperature_controller.c e azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_thermostat_component.c em um editor de texto.

O exemplo implementa o modelo de Linguagem de Definição de Gêmeo Digital do Controlador de Temperatura de vários componentes.

Quando você executa o exemplo para se conectar ao IoT Central, ele usa o DPS (Serviço de Provisionamento de Dispositivos) para registrar o dispositivo e gerar uma cadeia de conexão. O exemplo recupera as informações de conexão do DPS necessárias por meio do ambiente de linha de comando.

Em pnp_temperature_controller.c, a função main primeiro chama CreateDeviceClientAndAllocateComponents para:

  • Defina a ID do modelo dtmi:com:example:Thermostat;1. O IoT Central usa a ID do modelo para identificar ou gerar o modelo de dispositivo para este dispositivo. Para saber mais, confira Associar um dispositivo a um modelo de dispositivo.
  • Use o DPS para provisionar e registrar o dispositivo.
  • Crie um identificador de cliente de dispositivo e conecte-se ao seu aplicativo do IoT Central.
  • Cria um manipulador para comandos no componente controlador de temperatura.
  • Cria um manipulador para atualizações de propriedade no componente controlador de temperatura.
  • Cria os dois componentes termostatos.

A função main em seguida:

  • Relata alguns valores iniciais de propriedade para todos os componentes.
  • Inicia um loop para enviar telemetria de todos os componentes.

Depois, a função main inicia um thread para enviar a telemetria periodicamente.

int main(void)
{
    IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient = NULL;

    g_pnpDeviceConfiguration.modelId = g_temperatureControllerModelId;
    g_pnpDeviceConfiguration.enableTracing = g_hubClientTraceEnabled;

    // First determine the IoT Hub / credentials / device to use.
    if (GetConnectionSettingsFromEnvironment(&g_pnpDeviceConfiguration) == false)
    {
        LogError("Cannot read required environment variable(s)");
    }
    // Creates the thermostat subcomponents defined by this model.  Since everything
    // is simulated, this setup stage just creates simulated objects in memory.
    else if (AllocateThermostatComponents() == false)
    {
        LogError("Failure allocating thermostat components");
    }
    // Create a handle to device client handle.  Note that this call may block
    // for extended periods of time when using DPS.
    else if ((deviceClient = CreateAndConfigureDeviceClientHandleForPnP()) == NULL)
    {
        LogError("Failure creating Iot Hub device client");
        PnP_ThermostatComponent_Destroy(g_thermostatHandle1);
        PnP_ThermostatComponent_Destroy(g_thermostatHandle2);
    }
    else
    {
        LogInfo("Successfully created device client.  Hit Control-C to exit program\n");

        int numberOfIterations = 0;

        // During startup, send what DTDLv2 calls "read-only properties" to indicate initial device state.
        PnP_TempControlComponent_ReportSerialNumber_Property(deviceClient);
        PnP_DeviceInfoComponent_Report_All_Properties(g_deviceInfoComponentName, deviceClient);
        PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(g_thermostatHandle1, deviceClient);
        PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(g_thermostatHandle2, deviceClient);

        while (true)
        {
            // Wake up periodically to poll.  Even if we do not plan on sending telemetry, we still need to poll periodically in order to process
            // incoming requests from the server and to do connection keep alives.
            if ((numberOfIterations % g_sendTelemetryPollInterval) == 0)
            {
                PnP_TempControlComponent_SendWorkingSet(deviceClient);
                PnP_ThermostatComponent_SendCurrentTemperature(g_thermostatHandle1, deviceClient);
                PnP_ThermostatComponent_SendCurrentTemperature(g_thermostatHandle2, deviceClient);
            }

            IoTHubDeviceClient_LL_DoWork(deviceClient);
            ThreadAPI_Sleep(g_sleepBetweenPollsMs);
            numberOfIterations++;
        }

        // The remainder of the code is used for cleaning up our allocated resources. It won't be executed in this 
        // sample (because the loop above is infinite and is only broken out of by Control-C of the program), but 
        // it is included for reference.

        // Free the memory allocated to track simulated thermostat.
        PnP_ThermostatComponent_Destroy(g_thermostatHandle1);
        PnP_ThermostatComponent_Destroy(g_thermostatHandle2);

        // Clean up the IoT Hub SDK handle.
        IoTHubDeviceClient_LL_Destroy(deviceClient);
        // Free all IoT Hub subsystem.
        IoTHub_Deinit();
    }

    return 0;
}

Em pnp_thermostat_component.c, a função PnP_ThermostatComponent_SendCurrentTemperature mostra como o dispositivo envia a telemetria de temperatura de um componente para o IoT Central:

void PnP_ThermostatComponent_SendCurrentTemperature(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;
    IOTHUB_MESSAGE_HANDLE messageHandle = NULL;
    IOTHUB_MESSAGE_RESULT messageResult;
    IOTHUB_CLIENT_RESULT iothubClientResult;

    char temperatureStringBuffer[CURRENT_TEMPERATURE_BUFFER_SIZE];

    // Create the telemetry message body to send.
    if (snprintf(temperatureStringBuffer, sizeof(temperatureStringBuffer), g_temperatureTelemetryBodyFormat, pnpThermostatComponent->currentTemperature) < 0)
    {
        LogError("snprintf of current temperature telemetry failed");
    }
    // Create the message handle and specify its metadata.
    else if ((messageHandle = IoTHubMessage_CreateFromString(temperatureStringBuffer)) == NULL)
    {
        LogError("IoTHubMessage_PnP_CreateFromString failed");
    }
    else if ((messageResult = IoTHubMessage_SetContentTypeSystemProperty(messageHandle, g_jsonContentType)) != IOTHUB_MESSAGE_OK)
    {
        LogError("IoTHubMessage_SetContentTypeSystemProperty failed, error=%d", messageResult);
    }
    else if ((messageResult = IoTHubMessage_SetContentEncodingSystemProperty(messageHandle, g_utf8EncodingType)) != IOTHUB_MESSAGE_OK)
    {
        LogError("IoTHubMessage_SetContentEncodingSystemProperty failed, error=%d", messageResult);
    }
    else if ((messageResult = IoTHubMessage_SetComponentName(messageHandle, pnpThermostatComponent->componentName)) != IOTHUB_MESSAGE_OK)
    {
        LogError("IoTHubMessage_SetContentEncodingSystemProperty failed, error=%d", messageResult);
    }
    // Send the telemetry message.
    else if ((iothubClientResult = IoTHubDeviceClient_LL_SendTelemetryAsync(deviceClient, messageHandle, NULL, NULL)) != IOTHUB_CLIENT_OK)
    {
        LogError("Unable to send telemetry message, error=%d", iothubClientResult);
    }

    IoTHubMessage_Destroy(messageHandle);
}

Em pnp_thermostat_component.c, a função PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property envia uma atualização de propriedade maxTempSinceLastReboot do componente para o IoT Central:

void PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;
    char maximumTemperatureAsString[MAX_TEMPERATURE_SINCE_REBOOT_BUFFER_SIZE];
    IOTHUB_CLIENT_RESULT iothubClientResult;

    if (snprintf(maximumTemperatureAsString, sizeof(maximumTemperatureAsString), g_maxTempSinceLastRebootPropertyFormat, pnpThermostatComponent->maxTemperature) < 0)
    {
        LogError("Unable to create max temp since last reboot string for reporting result");
    }
    else
    {
        IOTHUB_CLIENT_PROPERTY_REPORTED maxTempProperty;
        maxTempProperty.structVersion = IOTHUB_CLIENT_PROPERTY_REPORTED_STRUCT_VERSION_1;
        maxTempProperty.name = g_maxTempSinceLastRebootPropertyName;
        maxTempProperty.value =  maximumTemperatureAsString;

        unsigned char* propertySerialized = NULL;
        size_t propertySerializedLength;

        // The first step of reporting properties is to serialize IOTHUB_CLIENT_PROPERTY_WRITABLE_RESPONSE into JSON for sending.
        if ((iothubClientResult = IoTHubClient_Properties_Serializer_CreateReported(&maxTempProperty, 1, pnpThermostatComponent->componentName, &propertySerialized, &propertySerializedLength)) != IOTHUB_CLIENT_OK)
        {
            LogError("Unable to serialize reported state, error=%d", iothubClientResult);
        }
        // The output of IoTHubClient_Properties_Serializer_CreateReported is sent to IoTHubDeviceClient_LL_SendPropertiesAsync to perform network I/O.
        else if ((iothubClientResult = IoTHubDeviceClient_LL_SendPropertiesAsync(deviceClient, propertySerialized, propertySerializedLength,  NULL, NULL)) != IOTHUB_CLIENT_OK)
        {
            LogError("Unable to send reported state, error=%d", iothubClientResult);
        }
        else
        {
            LogInfo("Sending %s property to IoTHub for component %s", g_maxTempSinceLastRebootPropertyName, pnpThermostatComponent->componentName);
        }
        IoTHubClient_Properties_Serializer_Destroy(propertySerialized);
    }
}

Em pnp_thermostat_component.c, a função PnP_ThermostatComponent_ProcessPropertyUpdate gerencia as atualizações da propriedade gravável por meio do IoT Central:

void PnP_ThermostatComponent_ProcessPropertyUpdate(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient, const char* propertyName, const char* propertyValue, int version)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;

    if (strcmp(propertyName, g_targetTemperaturePropertyName) != 0)
    {
        LogError("Property %s was requested to be changed but is not part of the thermostat interface definition", propertyName);
    }
    else
    {
        char* next;
        double targetTemperature = strtod(propertyValue, &next);
        if ((propertyValue == next) || (targetTemperature == HUGE_VAL) || (targetTemperature == (-1*HUGE_VAL)))
        {
            LogError("Property %s is not a valid number", propertyValue);
            SendTargetTemperatureResponse(pnpThermostatComponent, deviceClient, propertyValue, PNP_STATUS_BAD_FORMAT, version, g_temperaturePropertyResponseDescriptionNotInt);
        }
        else
        {
            LogInfo("Received targetTemperature %f for component %s", targetTemperature, pnpThermostatComponent->componentName);
            
            bool maxTempUpdated = false;
            UpdateTemperatureAndStatistics(pnpThermostatComponent, targetTemperature, &maxTempUpdated);

            // The device needs to let the service know that it has received the targetTemperature desired property.
            SendTargetTemperatureResponse(pnpThermostatComponent, deviceClient, propertyValue, PNP_STATUS_SUCCESS, version, NULL);
            
            if (maxTempUpdated)
            {
                // If the maximum temperature has been updated, we also report this as a property.
                PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(pnpThermostatComponent, deviceClient);
            }
        }
    }
}

Em pnp_thermostat_component.c, a função PnP_ThermostatComponent_ProcessCommand gerencia os comandos chamados por meio do IoT Central:

void PnP_ThermostatComponent_ProcessCommand(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, const char *pnpCommandName, JSON_Value* commandJsonValue, IOTHUB_CLIENT_COMMAND_RESPONSE* commandResponse)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;
    const char* sinceStr;

    if (strcmp(pnpCommandName, g_getMaxMinReportCommandName) != 0)
    {
        LogError("Command %s is not supported on thermostat component", pnpCommandName);
        commandResponse->statusCode = PNP_STATUS_NOT_FOUND;
    }
    // See caveats section in ../readme.md; we don't actually respect this sinceStr to keep the sample simple,
    // but want to demonstrate how to parse out in any case.
    else if ((sinceStr = json_value_get_string(commandJsonValue)) == NULL)
    {
        LogError("Cannot retrieve JSON string for command");
        commandResponse->statusCode = PNP_STATUS_BAD_FORMAT;
    }
    else if (BuildMaxMinCommandResponse(pnpThermostatComponent, commandResponse) == false)
    {
        LogError("Unable to build response for component %s", pnpThermostatComponent->componentName);
        commandResponse->statusCode = PNP_STATUS_INTERNAL_ERROR;
    }
    else
    {
        LogInfo("Returning success from command request for component %s", pnpThermostatComponent->componentName);
        commandResponse->statusCode = PNP_STATUS_SUCCESS;
    }
}

Compilar o código

Use o SDK do dispositivo para criar o código de exemplo incluído:

  1. Crie um subdiretório cmake na pasta raiz do SDK do dispositivo e navegue até essa pasta:

    cd azure-iot-sdk-c
    mkdir cmake
    cd cmake
    
  2. Execute os seguintes comandos para criar o SDK e os exemplos:

    cmake -Duse_prov_client=ON -Dhsm_type_symm_key=ON -Drun_e2e_tests=OFF ..
    cmake --build .
    

Obter informações de conexão

Quando você executar o aplicativo de dispositivo de exemplo mais adiante neste tutorial, precisará dos seguintes valores de configuração:

  • Escopo da ID: no aplicativo do IoT Central, acesse Permissões > Grupos de conexão de dispositivo. Anote o valor de Escopo da ID.
  • Chave primária do grupo: No aplicativo do IoT Central, acesse Permissões > Grupos de conexão de dispositivo > Dispositivos IoT de SAS. Anote o valor da Chave primária da Assinatura de Acesso Compartilhado.

Use o Azure Cloud Shell para gerar uma chave de dispositivo com base na chave primária de grupo que você recuperou:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Anote a chave de dispositivo gerada, pois você a usará mais adiante neste tutorial.

Observação

Para executar este exemplo, você não precisa registrar o dispositivo com antecedência no seu aplicativo do IoT Central. O exemplo usa a funcionalidade do IoT Central para registrar automaticamente os dispositivos quando eles se conectam pela primeira vez.

Executar o código

Para executar o aplicativo de exemplo, abra um ambiente de linha de comando e acesse a pasta azure-iot-sdk-c\cmake.

Defina as variáveis de ambiente para configurar o exemplo. O snippet a seguir mostra como definir as variáveis de ambiente no prompt de comando do Windows. Se estiver usando um shell do Bash, substitua os comandos set pelos comandos export:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Para executar o exemplo:

# Bash
cd iothub_client/samples/pnp/pnp_temperature_controller/
./pnp_temperature_controller
REM Windows
cd iothub_client\samples\pnp\pnp_temperature_controller\Debug
.\pnp_temperature_controller.exe

A saída a seguir mostra o dispositivo se registrando e se conectando ao IoT Central. O exemplo começa a enviar a telemetria:

Info: Initiating DPS client to retrieve IoT Hub connection information
-> 09:43:27 CONNECT | VER: 4 | KEEPALIVE: 0 | FLAGS: 194 | USERNAME: 0ne0026656D/registrations/sample-device-01/api-version=2019-03-31&ClientVersion=1.6.0 | PWD: XXXX | CLEAN: 1
<- 09:43:28 CONNACK | SESSION_PRESENT: false | RETURN_CODE: 0x0
-> 09:43:29 SUBSCRIBE | PACKET_ID: 1 | TOPIC_NAME: $dps/registrations/res/# | QOS: 1
<- 09:43:30 SUBACK | PACKET_ID: 1 | RETURN_CODE: 1
-> 09:43:30 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $dps/registrations/PUT/iotdps-register/?$rid=1 | PAYLOAD_LEN: 102
<- 09:43:31 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: $dps/registrations/res/202/?$rid=1&retry-after=3 | PACKET_ID: 2 | PAYLOAD_LEN: 94
-> 09:43:31 PUBACK | PACKET_ID: 2
-> 09:43:33 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $dps/registrations/GET/iotdps-get-operationstatus/?$rid=2&operationId=4.2f792ade0a5c3e68.baf0e879-d88a-4153-afef-71aff51fd847 | PAYLOAD_LEN: 102
<- 09:43:34 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: $dps/registrations/res/202/?$rid=2&retry-after=3 | PACKET_ID: 2 | PAYLOAD_LEN: 173
-> 09:43:34 PUBACK | PACKET_ID: 2
-> 09:43:36 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $dps/registrations/GET/iotdps-get-operationstatus/?$rid=3&operationId=4.2f792ade0a5c3e68.baf0e879-d88a-4153-afef-71aff51fd847 | PAYLOAD_LEN: 102
<- 09:43:37 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: $dps/registrations/res/200/?$rid=3 | PACKET_ID: 2 | PAYLOAD_LEN: 478
-> 09:43:37 PUBACK | PACKET_ID: 2
Info: Provisioning callback indicates success.  iothubUri=iotc-60a....azure-devices.net, deviceId=sample-device-01
-> 09:43:37 DISCONNECT
Info: DPS successfully registered.  Continuing on to creation of IoTHub device client handle.
Info: Successfully created device client.  Hit Control-C to exit program

Info: Sending serialNumber property to IoTHub
Info: Sending device information property to IoTHub.  propertyName=swVersion, propertyValue="1.0.0.0"
Info: Sending device information property to IoTHub.  propertyName=manufacturer, propertyValue="Sample-Manufacturer"
Info: Sending device information property to IoTHub.  propertyName=model, propertyValue="sample-Model-123"
Info: Sending device information property to IoTHub.  propertyName=osName, propertyValue="sample-OperatingSystem-name"
Info: Sending device information property to IoTHub.  propertyName=processorArchitecture, propertyValue="Contoso-Arch-64bit"
Info: Sending device information property to IoTHub.  propertyName=processorManufacturer, propertyValue="Processor Manufacturer(TM)"
Info: Sending device information property to IoTHub.  propertyName=totalStorage, propertyValue=10000
Info: Sending device information property to IoTHub.  propertyName=totalMemory, propertyValue=200
Info: Sending maximumTemperatureSinceLastReboot property to IoTHub for component=thermostat1
Info: Sending maximumTemperatureSinceLastReboot property to IoTHub for component=thermostat2
-> 09:43:44 CONNECT | VER: 4 | KEEPALIVE: 240 | FLAGS: 192 | USERNAME: iotc-60a576a2-eec7-48e2-9306-9e7089a79995.azure-devices.net/sample-device-01/?api-version=2020-09-30&DeviceClientType=iothubclient%2f1.6.0%20(native%3b%20Linux%3b%20x86_64)&model-id=dtmi%3acom%3aexample%3aTemperatureController%3b1 | PWD: XXXX | CLEAN: 0
<- 09:43:44 CONNACK | SESSION_PRESENT: false | RETURN_CODE: 0x0
-> 09:43:44 SUBSCRIBE | PACKET_ID: 2 | TOPIC_NAME: $iothub/twin/res/# | QOS: 0 | TOPIC_NAME: $iothub/methods/POST/# | QOS: 0
-> 09:43:44 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: devices/sample-device-01/messages/events/ | PACKET_ID: 3 | PAYLOAD_LEN: 19
-> 09:43:44 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: devices/sample-device-01/messages/events/%24.sub=thermostat1 | PACKET_ID: 4 | PAYLOAD_LEN: 21
-> 09:43:44 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: devices/sample-device-01/messages/events/%24.sub=thermostat2 | PACKET_ID: 5 | PAYLOAD_LEN: 21

Como operador no seu aplicativo do Azure IoT Central, você pode:

  • Exibir a telemetria enviada pelos dois componentes termostatos na página Visão geral:

    Screenshot that shows the device overview page.

  • Exibir as propriedades do dispositivo na página Sobre. Esta página mostra as propriedades do componente de informações do dispositivo e dos dois componentes termostatos:

    Screenshot that shows the device properties view.

Personalizar o modelo de dispositivo

Como desenvolvedor de soluções, você pode personalizar o modelo de dispositivo criado automaticamente pelo IoT Central quando o dispositivo controlador de temperatura foi conectado.

Para adicionar uma propriedade de nuvem para armazenar o nome do cliente associado ao dispositivo:

  1. No aplicativo do IoT Central, acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivos.

  2. No modelo Controlador de Temperatura, selecione +Adicionar funcionalidade.

  3. Insira Nome do cliente como o Nome de exibição, selecione Propriedade de nuvem como o tipo de capacidade, expanda a entrada e escolha Cadeia de caracteres como Esquema. Depois, selecione Salvar.

Para personalizar como os comandos Obter relatório de Máx.-Mín. são exibidos no aplicativo do IoT Central:

  1. Acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivo.

  2. Para getMaxMinReport (thermostat1), substitua Obter relatório de Máx.-Mín. por Obter relatório de status de termostato1.

  3. Para getMaxMinReport (thermostat2), substitua Obter relatório de Máx.-Mín. por Obter relatório de status de termostato2.

  4. Clique em Salvar.

Para personalizar como as propriedades graváveis da temperatura de destino são exibidas no aplicativo do IoT Central:

  1. Acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivo.

  2. Para targetTemperature (thermostat1) , substitua a Temperatura de destino pela Temperatura de destino (1) .

  3. Para targetTemperature (thermostat2) , substitua a Temperatura de destino pela Temperatura de destino (2) .

  4. Clique em Salvar.

Os componentes do termostato no modelo de Controlador de Temperatura incluem a propriedade gravável Temperatura de Destino e o modelo de dispositivo inclui a propriedade de nuvem Nome do Cliente. Crie uma exibição que um operador possa usar para editar estas propriedades:

  1. Selecione Exibições e, em seguida, o bloco Editando dados do dispositivo e da nuvem.

  2. Insira Propriedades como o nome do formulário.

  3. Selecione as propriedades Temperatura de destino (1), Temperatura de destino (2) e Nome do cliente. Em seguida, selecione Adicionar seção.

  4. Salve suas alterações.

Screenshot that shows a view for updating property values.

Publicar o modelo de dispositivo

Para que um operador possa ver e usar as personalizações feitas por você, publique o modelo de dispositivo.

No modelo de dispositivo de Termostato, selecione Publicar. No painel Publicar este modelo de dispositivo no aplicativo, selecione Publicar.

Agora, um operador poderá usar a exibição Propriedades para atualizar os valores da propriedade e chamar os comandos Obter relatório de status do thermostat1 e Obter relatório de status do thermostat2 na página de comandos do dispositivo:

  • Atualize os valores de propriedades graváveis na página Propriedades:

    Screenshot that shows updating the device properties.

  • Chame os comandos na página Comandos. Se você executar o comando de relatório de status, selecione uma data e uma hora para o parâmetro Desde antes de executá-lo:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Você pode ver como o dispositivo responde a comandos e a atualizações de propriedade:

<- 09:49:03 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $iothub/methods/POST/thermostat1*getMaxMinReport/?$rid=1 | PAYLOAD_LEN: 26
Info: Received PnP command for component=thermostat1, command=getMaxMinReport
Info: Returning success from command request for component=thermostat1
-> 09:49:03 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $iothub/methods/res/200/?$rid=1 | PAYLOAD_LEN: 117

...

<- 09:50:04 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $iothub/twin/PATCH/properties/desired/?$version=2 | PAYLOAD_LEN: 63
Info: Received targetTemperature=67.000000 for component=thermostat2
Info: Sending acknowledgement of property to IoTHub for component=thermostat2

Browse code

Pré-requisitos

Para concluir as etapas neste artigo, você precisa dos seguintes recursos:

Examine o código

Na cópia do repositório SDK do Microsoft Azure IoT para C#, baixado anteriormente, abra o arquivo de solução azure-iot-sdk-csharp-main\azureiot.sln no Visual Studio. Em Gerenciador de Soluções, expanda a pasta PnpDeviceSamples > TemperatureController e abra os arquivos Program.cs e TemperatureControllerSample.cs para exibir o código deste exemplo.

O exemplo implementa o modelo de Linguagem de Definição de Gêmeo Digital do Controlador de Temperatura de vários componentes.

Quando você executa o exemplo para se conectar ao IoT Central, ele usa o DPS (Serviço de Provisionamento de Dispositivos) para registrar o dispositivo e gerar uma cadeia de conexão. O exemplo recupera as informações de conexão do DPS necessárias do ambiente.

Em Program.cs, o método Main chama SetupDeviceClientAsync para:

  • Use a ID do modelo dtmi:com:example:TemperatureController;2 quando ela provisionar o dispositivo com o DPS. O IoT Central usa a ID do modelo para identificar ou gerar o modelo de dispositivo para este dispositivo. Para saber mais, confira Associar um dispositivo a um modelo de dispositivo.
  • Crie uma instância de DeviceClient para se conectar ao IoT Central.
private static async Task<DeviceClient> SetupDeviceClientAsync(Parameters parameters, CancellationToken cancellationToken)
{
  DeviceClient deviceClient;
  switch (parameters.DeviceSecurityType.ToLowerInvariant())
  {
    case "dps":
      DeviceRegistrationResult dpsRegistrationResult = await ProvisionDeviceAsync(parameters, cancellationToken);
      var authMethod = new DeviceAuthenticationWithRegistrySymmetricKey(dpsRegistrationResult.DeviceId, parameters.DeviceSymmetricKey);
      deviceClient = InitializeDeviceClient(dpsRegistrationResult.AssignedHub, authMethod);
      break;

    case "connectionstring":
      // ...

    default:
      // ...
  }
  return deviceClient;
}

Em seguida, o método principal cria uma instância do TemperatureControllerSample e chama o método PerformOperationsAsync para gerenciar as interações com o IoT Central.

Em TemperatureControllerSample.cs, o método PerformOperationsAsync:

  • Define um manipulador para o comando reboot no componente padrão.
  • Define manipuladores para os comandos getMaxMinReport nos dois componentes termostatos.
  • Define manipuladores para receber atualizações da propriedade da temperatura de destino nos dois componentes termostatos.
  • Envia atualizações iniciais da propriedade de informações de dispositivo.
  • Envia periodicamente a telemetria de temperatura dos dois componentes termostatos.
  • Envia periodicamente a telemetria do conjunto de trabalho do componente padrão.
  • Envia a temperatura máxima desde a última reinicialização, sempre que uma nova temperatura máxima é atingida nos dois componentes termostatos.
public async Task PerformOperationsAsync(CancellationToken cancellationToken)
{
  await _deviceClient.SetMethodHandlerAsync("reboot", HandleRebootCommandAsync, _deviceClient, cancellationToken);

  // For a component-level command, the command name is in the format "<component-name>*<command-name>".
  await _deviceClient.SetMethodHandlerAsync("thermostat1*getMaxMinReport", HandleMaxMinReportCommand, Thermostat1, cancellationToken);
  await _deviceClient.SetMethodHandlerAsync("thermostat2*getMaxMinReport", HandleMaxMinReportCommand, Thermostat2, cancellationToken);

  await _deviceClient.SetDesiredPropertyUpdateCallbackAsync(SetDesiredPropertyUpdateCallback, null, cancellationToken);
  _desiredPropertyUpdateCallbacks.Add(Thermostat1, TargetTemperatureUpdateCallbackAsync);
  _desiredPropertyUpdateCallbacks.Add(Thermostat2, TargetTemperatureUpdateCallbackAsync);

  await UpdateDeviceInformationAsync(cancellationToken);
  await SendDeviceSerialNumberAsync(cancellationToken);

  bool temperatureReset = true;
  _maxTemp[Thermostat1] = 0d;
  _maxTemp[Thermostat2] = 0d;

  while (!cancellationToken.IsCancellationRequested)
  {
    if (temperatureReset)
    {
      // Generate a random value between 5.0°C and 45.0°C for the current temperature reading for each "Thermostat" component.
      _temperature[Thermostat1] = Math.Round(s_random.NextDouble() * 40.0 + 5.0, 1);
      _temperature[Thermostat2] = Math.Round(s_random.NextDouble() * 40.0 + 5.0, 1);
    }

    await SendTemperatureAsync(Thermostat1, cancellationToken);
    await SendTemperatureAsync(Thermostat2, cancellationToken);
    await SendDeviceMemoryAsync(cancellationToken);

    temperatureReset = _temperature[Thermostat1] == 0 && _temperature[Thermostat2] == 0;
    await Task.Delay(5 * 1000);
  }
}

O método SendTemperatureAsync mostra como o dispositivo envia a telemetria de temperatura de um componente ao IoT Central. O método SendTemperatureTelemetryAsync usa a classe PnpConvention para criar a mensagem:

private async Task SendTemperatureAsync(string componentName, CancellationToken cancellationToken)
{
  await SendTemperatureTelemetryAsync(componentName, cancellationToken);

  double maxTemp = _temperatureReadingsDateTimeOffset[componentName].Values.Max<double>();
  if (maxTemp > _maxTemp[componentName])
  {
    _maxTemp[componentName] = maxTemp;
    await UpdateMaxTemperatureSinceLastRebootAsync(componentName, cancellationToken);
  }
}

private async Task SendTemperatureTelemetryAsync(string componentName, CancellationToken cancellationToken)
{
  const string telemetryName = "temperature";
  double currentTemperature = _temperature[componentName];
  using Message msg = PnpConvention.CreateMessage(telemetryName, currentTemperature, componentName);

  await _deviceClient.SendEventAsync(msg, cancellationToken);

  if (_temperatureReadingsDateTimeOffset.ContainsKey(componentName))
  {
    _temperatureReadingsDateTimeOffset[componentName].TryAdd(DateTimeOffset.UtcNow, currentTemperature);
  }
  else
  {
    _temperatureReadingsDateTimeOffset.TryAdd(
      componentName,
      new Dictionary<DateTimeOffset, double>
      {
        { DateTimeOffset.UtcNow, currentTemperature },
      });
  }
}

O método UpdateMaxTemperatureSinceLastRebootAsync envia uma atualização de propriedade maxTempSinceLastReboot ao IoT Central. Esse método usa a classe PnpConvention para criar o patch:

private async Task UpdateMaxTemperatureSinceLastRebootAsync(string componentName, CancellationToken cancellationToken)
{
  const string propertyName = "maxTempSinceLastReboot";
  double maxTemp = _maxTemp[componentName];
  TwinCollection reportedProperties = PnpConvention.CreateComponentPropertyPatch(componentName, propertyName, maxTemp);

  await _deviceClient.UpdateReportedPropertiesAsync(reportedProperties, cancellationToken);
}

O método TargetTemperatureUpdateCallbackAsync gerencia a atualização da propriedade gravável de temperatura de destino por meio do IoT Central. Esse método usa a classe PnpConvention para ler a mensagem de atualização da propriedade e construir a resposta:

private async Task TargetTemperatureUpdateCallbackAsync(TwinCollection desiredProperties, object userContext)
{
  const string propertyName = "targetTemperature";
  string componentName = (string)userContext;

  bool targetTempUpdateReceived = PnpConvention.TryGetPropertyFromTwin(
    desiredProperties,
    propertyName,
    out double targetTemperature,
    componentName);
  if (!targetTempUpdateReceived)
  {
      return;
  }

  TwinCollection pendingReportedProperty = PnpConvention.CreateComponentWritablePropertyResponse(
      componentName,
      propertyName,
      targetTemperature,
      (int)StatusCode.InProgress,
      desiredProperties.Version);

  await _deviceClient.UpdateReportedPropertiesAsync(pendingReportedProperty);

  // Update Temperature in 2 steps
  double step = (targetTemperature - _temperature[componentName]) / 2d;
  for (int i = 1; i <= 2; i++)
  {
      _temperature[componentName] = Math.Round(_temperature[componentName] + step, 1);
      await Task.Delay(6 * 1000);
  }

  TwinCollection completedReportedProperty = PnpConvention.CreateComponentWritablePropertyResponse(
      componentName,
      propertyName,
      _temperature[componentName],
      (int)StatusCode.Completed,
      desiredProperties.Version,
      "Successfully updated target temperature");

  await _deviceClient.UpdateReportedPropertiesAsync(completedReportedProperty);
}

O método HandleMaxMinReportCommand gerencia os comandos para os componentes chamados por meio do IoT Central:

private Task<MethodResponse> HandleMaxMinReportCommand(MethodRequest request, object userContext)
{
    try
    {
        string componentName = (string)userContext;
        DateTime sinceInUtc = JsonConvert.DeserializeObject<DateTime>(request.DataAsJson);
        var sinceInDateTimeOffset = new DateTimeOffset(sinceInUtc);

        if (_temperatureReadingsDateTimeOffset.ContainsKey(componentName))
        {

            Dictionary<DateTimeOffset, double> allReadings = _temperatureReadingsDateTimeOffset[componentName];
            Dictionary<DateTimeOffset, double> filteredReadings = allReadings.Where(i => i.Key > sinceInDateTimeOffset)
                .ToDictionary(i => i.Key, i => i.Value);

            if (filteredReadings != null && filteredReadings.Any())
            {
                var report = new
                {
                    maxTemp = filteredReadings.Values.Max<double>(),
                    minTemp = filteredReadings.Values.Min<double>(),
                    avgTemp = filteredReadings.Values.Average(),
                    startTime = filteredReadings.Keys.Min(),
                    endTime = filteredReadings.Keys.Max(),
                };

                byte[] responsePayload = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(report));
                return Task.FromResult(new MethodResponse(responsePayload, (int)StatusCode.Completed));
            }

            return Task.FromResult(new MethodResponse((int)StatusCode.NotFound));
        }

        return Task.FromResult(new MethodResponse((int)StatusCode.NotFound));
    }
    catch (JsonReaderException ex)
    {
        // ...
    }
}

Obter informações de conexão

Quando você executar o aplicativo de dispositivo de exemplo mais adiante neste tutorial, precisará dos seguintes valores de configuração:

  • Escopo da ID: no aplicativo do IoT Central, acesse Permissões > Grupos de conexão de dispositivo. Anote o valor de Escopo da ID.
  • Chave primária do grupo: No aplicativo do IoT Central, acesse Permissões > Grupos de conexão de dispositivo > Dispositivos IoT de SAS. Anote o valor da Chave primária da Assinatura de Acesso Compartilhado.

Use o Azure Cloud Shell para gerar uma chave de dispositivo com base na chave primária de grupo que você recuperou:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Anote a chave de dispositivo gerada, pois você a usará mais adiante neste tutorial.

Observação

Para executar este exemplo, você não precisa registrar o dispositivo com antecedência no seu aplicativo do IoT Central. O exemplo usa a funcionalidade do IoT Central para registrar automaticamente os dispositivos quando eles se conectam pela primeira vez.

Executar o código

Observação

Configure o TemperatureController como um projeto de inicialização antes de executar o código.

Para executar o aplicativo de exemplo no Visual Studio:

  1. Em Gerenciador de Soluções, selecione o arquivo de projeto PnpDeviceSamples > TemperatureController.

  2. Acesse Projeto > Propriedades do TemperatureController > Depurar. Em seguida, adicione as seguintes variáveis de ambiente ao projeto:

    Nome Valor
    IOTHUB_DEVICE_SECURITY_TYPE DPS
    IOTHUB_DEVICE_DPS_ENDPOINT global.azure-devices-provisioning.net
    IOTHUB_DEVICE_DPS_ID_SCOPE O valor de escopo da ID anotado anteriormente.
    IOTHUB_DEVICE_DPS_DEVICE_ID sample-device-01
    IOTHUB_DEVICE_DPS_DEVICE_KEY O valor de chave do dispositivo gerado anotado anteriormente.

Agora você pode executar e depurar o exemplo no Visual Studio.

A saída a seguir mostra o dispositivo se registrando e se conectando ao IoT Central. O exemplo começa a enviar a telemetria:

[03/31/2021 14:43:17]info: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Press Control+C to quit the sample.
[03/31/2021 14:43:17]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set up the device client.
[03/31/2021 14:43:18]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Initializing via DPS
[03/31/2021 14:43:27]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set handler for 'reboot' command.
[03/31/2021 14:43:27]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Connection status change registered - status=Connected, reason=Connection_Ok.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set handler for "getMaxMinReport" command.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set handler to receive 'targetTemperature' updates.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component = 'deviceInformation', properties update is complete.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - { "serialNumber": "SR-123456" } is complete.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - component="thermostat1", { "temperature": 34.2 } in °C.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat1", { "maxTempSinceLastReboot": 34.2 } in °C is complete.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - component="thermostat2", { "temperature": 25.1 } in °C.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat2", { "maxTempSinceLastReboot": 25.1 } in °C is complete.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - {"workingSet":31412} in KB.

Como operador no seu aplicativo do Azure IoT Central, você pode:

  • Exibir a telemetria enviada pelos dois componentes termostatos na página Visão geral:

    Screenshot that shows the device overview page.

  • Exibir as propriedades do dispositivo na página Sobre. Esta página mostra as propriedades do componente de informações do dispositivo e dos dois componentes termostatos:

    Screenshot that shows the device properties view.

Personalizar o modelo de dispositivo

Como desenvolvedor de soluções, você pode personalizar o modelo de dispositivo criado automaticamente pelo IoT Central quando o dispositivo controlador de temperatura foi conectado.

Para adicionar uma propriedade de nuvem para armazenar o nome do cliente associado ao dispositivo:

  1. No aplicativo do IoT Central, acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivos.

  2. No modelo Controlador de Temperatura, selecione +Adicionar funcionalidade.

  3. Insira Nome do cliente como o Nome de exibição, selecione Propriedade de nuvem como o tipo de capacidade, expanda a entrada e escolha Cadeia de caracteres como Esquema. Depois, selecione Salvar.

Para personalizar como os comandos Obter relatório de Máx.-Mín. são exibidos no aplicativo do IoT Central:

  1. Acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivo.

  2. Para getMaxMinReport (thermostat1), substitua Obter relatório de Máx.-Mín. por Obter relatório de status de termostato1.

  3. Para getMaxMinReport (thermostat2), substitua Obter relatório de Máx.-Mín. por Obter relatório de status de termostato2.

  4. Clique em Salvar.

Para personalizar como as propriedades graváveis da temperatura de destino são exibidas no aplicativo do IoT Central:

  1. Acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivo.

  2. Para targetTemperature (thermostat1) , substitua a Temperatura de destino pela Temperatura de destino (1) .

  3. Para targetTemperature (thermostat2) , substitua a Temperatura de destino pela Temperatura de destino (2) .

  4. Clique em Salvar.

Os componentes do termostato no modelo de Controlador de Temperatura incluem a propriedade gravável Temperatura de Destino e o modelo de dispositivo inclui a propriedade de nuvem Nome do Cliente. Crie uma exibição que um operador possa usar para editar estas propriedades:

  1. Selecione Exibições e, em seguida, o bloco Editando dados do dispositivo e da nuvem.

  2. Insira Propriedades como o nome do formulário.

  3. Selecione as propriedades Temperatura de destino (1), Temperatura de destino (2) e Nome do cliente. Em seguida, selecione Adicionar seção.

  4. Salve suas alterações.

Screenshot that shows a view for updating property values.

Publicar o modelo de dispositivo

Para que um operador possa ver e usar as personalizações feitas por você, publique o modelo de dispositivo.

No modelo de dispositivo de Termostato, selecione Publicar. No painel Publicar este modelo de dispositivo no aplicativo, selecione Publicar.

Agora, um operador poderá usar a exibição Propriedades para atualizar os valores da propriedade e chamar os comandos Obter relatório de status do thermostat1 e Obter relatório de status do thermostat2 na página de comandos do dispositivo:

  • Atualize os valores de propriedades graváveis na página Propriedades:

    Screenshot that shows updating the device properties.

  • Chame os comandos na página Comandos. Se você executar o comando de relatório de status, selecione uma data e uma hora para o parâmetro Desde antes de executá-lo:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Você pode ver como o dispositivo responde a comandos e a atualizações de propriedade:

[03/31/2021 14:47:00]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Command: Received - component="thermostat2", generating max, min and avg temperature report since 31/03/2021 06:00:00.
[03/31/2021 14:47:00]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Command: component="thermostat2", MaxMinReport since 31/03/2021 06:00:00: maxTemp=36.4, minTemp=36.4, avgTemp=36.4, startTime=31/03/2021 14:46:33, endTime=31/03/2021 14:46:55

...

[03/31/2021 14:46:36]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Received - component="thermostat1", { "targetTemperature": 67°C }.
[03/31/2021 14:46:36]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat1", {"targetTemperature": 67 } in °C is InProgress.
[03/31/2021 14:46:49]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat1", {"targetTemperature": 67 } in °C is Completed
[03/31/2021 14:46:49]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - component="thermostat1", { "temperature": 67 } in °C.

Browse code

Pré-requisitos

Para concluir as etapas neste artigo, você precisa dos seguintes recursos:

  • Um computador de desenvolvimento com o Java SE Development Kit 8 ou mais recente. Para obter mais informações, confira Instalar o JDK.

  • Apache Maven 3.

  • Uma cópia local do repositório GitHub do SDK do Microsoft Azure IoT para Java que contém o código de exemplo. Use este link para baixar uma cópia do repositório: Baixar ZIP. Em seguida, descompacte o arquivo em uma localização adequada no computador local.

Examine o código

Na cópia do SDK da Internet das Coisas do Microsoft Azure para Java baixado anteriormente, abra o arquivo azure-iot-sdk-java/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/device/TemperatureController.java em um editor de texto.

O exemplo implementa o modelo de Linguagem de Definição de Gêmeo Digital do Controlador de Temperatura de vários componentes.

Quando você executa o exemplo para se conectar ao IoT Central, ele usa o DPS (Serviço de Provisionamento de Dispositivos) para registrar o dispositivo e gerar uma cadeia de conexão. O exemplo recupera as informações de conexão do DPS necessárias por meio do ambiente de linha de comando.

O método main:

  • Chama initializeAndProvisionDevice para definir a ID do modelo dtmi:com:example:TemperatureController;2, usar o DPS para provisionar e registrar o dispositivo, criar uma instância de initializeAndProvisionDevice e conectar-se ao aplicativo do IoT Central. O IoT Central usa a ID do modelo para identificar ou gerar o modelo de dispositivo para este dispositivo. Para saber mais, confira Associar um dispositivo a um modelo de dispositivo.
  • Cria manipuladores de comando para os comandos getMaxMinReport e reboot.
  • Cria manipuladores de atualização de propriedade para as propriedades targetTemperature graváveis.
  • Envia valores iniciais para as propriedades na interface de Informações do dispositivo e as propriedades de Memória do dispositivo e Número de série.
  • Inicia um thread para enviar a telemetria de temperatura dos dois termostatos e atualizar a propriedade maxTempSinceLastReboot a cada cinco segundos.
public static void main(String[] args) throws Exception {

  // ...
  
  switch (deviceSecurityType.toLowerCase())
  {
    case "dps":
    {
      if (validateArgsForDpsFlow())
      {
        initializeAndProvisionDevice();
        break;
      }
      throw new IllegalArgumentException("Required environment variables are not set for DPS flow, please recheck your environment.");
    }
    case "connectionstring":
    {
      // ...
    }
    default:
    {
      // ...
    }
  }
  
  deviceClient.subscribeToMethods(new MethodCallback(), null);
  
  deviceClient.subscribeToDesiredPropertiesAsync(
  {
  (twin, context) ->
      TwinCollection desiredProperties = twin.getDesiredProperties();
      for (String desiredPropertyKey : desiredProperties.keySet())
      {
          TargetTemperatureUpdateCallback.onPropertyChanged(new Property(desiredPropertyKey, desiredProperties.get(desiredPropertyKey)), null);
      }
  },
  null,
  (exception, context) ->
  {
      if (exception == null)
      {
          log.info("Successfully subscribed to desired properties. Getting initial state");
          deviceClient.getTwinAsync(
              (twin, getTwinException, getTwinContext) ->
              {
                  log.info("Initial twin state received");
                  log.info(twin.toString());
              },
              null);
      }
      else
      {
          log.info("Failed to subscribe to desired properties. Error code {}", exception.getStatusCode());
          System.exit(-1);
      }
  },
  null);

  updateDeviceInformation();
  sendDeviceMemory();
  sendDeviceSerialNumber();
  
  final AtomicBoolean temperatureReset = new AtomicBoolean(true);
  maxTemperature.put(THERMOSTAT_1, 0.0d);
  maxTemperature.put(THERMOSTAT_2, 0.0d);
  
  new Thread(new Runnable() {
    @SneakyThrows({InterruptedException.class, IOException.class})
    @Override
    public void run() {
      while (true) {
        if (temperatureReset.get()) {
          // Generate a random value between 5.0°C and 45.0°C for the current temperature reading for each "Thermostat" component.
          temperature.put(THERMOSTAT_1, BigDecimal.valueOf(random.nextDouble() * 40 + 5).setScale(1, RoundingMode.HALF_UP).doubleValue());
          temperature.put(THERMOSTAT_2, BigDecimal.valueOf(random.nextDouble() * 40 + 5).setScale(1, RoundingMode.HALF_UP).doubleValue());
        }

        sendTemperatureReading(THERMOSTAT_1);
        sendTemperatureReading(THERMOSTAT_2);

        temperatureReset.set(temperature.get(THERMOSTAT_1) == 0 && temperature.get(THERMOSTAT_2) == 0);
        Thread.sleep(5 * 1000);
      }
    }
  }).start();
}

O método initializeAndProvisionDevice mostra como o dispositivo usa o DPS para se registrar no IoT Central e se conectar a ele. O conteúdo inclui a ID do modelo que o IoT Central usa para atribuir um dispositivo a um modelo de dispositivo:

private static void initializeAndProvisionDevice() throws Exception {
  SecurityProviderSymmetricKey securityClientSymmetricKey = new SecurityProviderSymmetricKey(deviceSymmetricKey.getBytes(), registrationId);
  ProvisioningDeviceClient provisioningDeviceClient;
  ProvisioningStatus provisioningStatus = new ProvisioningStatus();

  provisioningDeviceClient = ProvisioningDeviceClient.create(globalEndpoint, scopeId, provisioningProtocol, securityClientSymmetricKey);

  AdditionalData additionalData = new AdditionalData();
  additionalData.setProvisioningPayload(com.microsoft.azure.sdk.iot.provisioning.device.plugandplay.PnpHelper.createDpsPayload(MODEL_ID));

  ProvisioningDeviceClientRegistrationResult registrationResult = provisioningDeviceClient.registerDeviceSync(additionalData);

    ClientOptions options = ClientOptions.builder().modelId(MODEL_ID).build();
    if (registrationResult.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED) {
        System.out.println("IotHUb Uri : " + registrationResult.getIothubUri());
        System.out.println("Device ID : " + registrationResult.getDeviceId());
        String iotHubUri = registrationResult.getIothubUri();
        String deviceId = registrationResult.getDeviceId();
        log.debug("Opening the device client.");
        deviceClient = new DeviceClient(iotHubUri, deviceId, securityClientSymmetricKey, IotHubClientProtocol.MQTT, options);
        deviceClient.open(true);
    }
}

O método sendTemperatureTelemetry mostra como o dispositivo envia a telemetria de temperatura de um componente ao IoT Central. Esse método usa a classe PnpConvention para criar a mensagem:

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

    Message message = PnpConvention.createIotHubMessageUtf8(telemetryName, currentTemperature, componentName);
    deviceClient.sendEventAsync(message, new MessageIotHubEventCallback(), message);

    // Add the current temperature entry to the list of temperature readings.
    Map<Date, Double> currentReadings;
    if (temperatureReadings.containsKey(componentName)) {
      currentReadings = temperatureReadings.get(componentName);
    } else {
      currentReadings = new HashMap<>();
    }
    currentReadings.put(new Date(), currentTemperature);
    temperatureReadings.put(componentName, currentReadings);
  }

O método updateMaxTemperatureSinceLastReboot envia uma atualização de propriedade maxTempSinceLastReboot de um componente ao IoT Central. Esse método usa a classe PnpConvention para criar o patch:

private static void updateMaxTemperatureSinceLastReboot(String componentName) throws IOException {
  String propertyName = "maxTempSinceLastReboot";
  double maxTemp = maxTemperature.get(componentName);

  TwinCollection reportedProperty = PnpConvention.createComponentPropertyPatch(propertyName, maxTemp, componentName);
  deviceClient.updateReportedPropertiesAsync(reportedProperty, sendReportedPropertiesResponseCallback, null);
  log.debug("Property: Update - {\"{}\": {}°C} is {}.", propertyName, maxTemp, StatusCode.COMPLETED);
}

A classe TargetTemperatureUpdateCallback contém o método onPropertyChanged para gerenciar as atualizações da propriedade gravável de um componente por meio do IoT Central. Esse método usa a classe PnpConvention para criar a resposta:

private static class TargetTemperatureUpdateCallback
{
    final static String propertyName = "targetTemperature";
    @SneakyThrows(InterruptedException.class)
    public static void onPropertyChanged(Property property, Object context) {
        String componentName = (String) context;
        if (property.getKey().equalsIgnoreCase(componentName)) {
            double targetTemperature = (double) ((TwinCollection) property.getValue()).get(propertyName);
            log.debug("Property: Received - component=\"{}\", {\"{}\": {}°C}.", componentName, propertyName, targetTemperature);
            TwinCollection pendingPropertyPatch = PnpConvention.createComponentWritablePropertyResponse(
                    propertyName,
                    targetTemperature,
                    componentName,
                    StatusCode.IN_PROGRESS.value,
                    property.getVersion().longValue(),
                    null);
            deviceClient.updateReportedPropertiesAsync(pendingPropertyPatch, sendReportedPropertiesResponseCallback, null);
            log.debug("Property: Update - component=\"{}\", {\"{}\": {}°C} is {}", componentName, propertyName, targetTemperature, StatusCode.IN_PROGRESS);
            // Update temperature in 2 steps
            double step = (targetTemperature - temperature.get(componentName)) / 2;
            for (int i = 1; i <=2; i++) {
                temperature.put(componentName, BigDecimal.valueOf(temperature.get(componentName) + step).setScale(1, RoundingMode.HALF_UP).doubleValue());
                Thread.sleep(5 * 1000);
            }
            TwinCollection completedPropertyPatch = PnpConvention.createComponentWritablePropertyResponse(
                    propertyName,
                    temperature.get(componentName),
                    componentName,
                    StatusCode.COMPLETED.value,
                    property.getVersion().longValue(),
                    "Successfully updated target temperature.");
            deviceClient.updateReportedPropertiesAsync(completedPropertyPatch, sendReportedPropertiesResponseCallback, null);
            log.debug("Property: Update - {\"{}\": {}°C} is {}", propertyName, temperature.get(componentName), StatusCode.COMPLETED);
        } else {
            log.debug("Property: Received an unrecognized property update from service.");
        }
    }
}

A classe MethodCallback contém o método onMethodInvoked para gerenciar os comandos do componente chamados por meio do IoT Central:

private static class MethodCallback implements com.microsoft.azure.sdk.iot.device.twin.MethodCallback
{
    final String reboot = "reboot";
    final String getMaxMinReport1 = "thermostat1*getMaxMinReport";
    final String getMaxMinReport2 = "thermostat2*getMaxMinReport";
    @SneakyThrows(InterruptedException.class)
    @Override
    public DirectMethodResponse onMethodInvoked(String methodName, DirectMethodPayload methodData, Object context) {
        String jsonRequest = methodData.getPayload(String.class);
        switch (methodName) {
            case reboot:
                int delay = getCommandRequestValue(jsonRequest, Integer.class);
                log.debug("Command: Received - Rebooting thermostat (resetting temperature reading to 0°C after {} seconds).", delay);
                Thread.sleep(delay * 1000L);
                temperature.put(THERMOSTAT_1, 0.0d);
                temperature.put(THERMOSTAT_2, 0.0d);
                maxTemperature.put(THERMOSTAT_1, 0.0d);
                maxTemperature.put(THERMOSTAT_2, 0.0d);
                temperatureReadings.clear();
                return new DirectMethodResponse(StatusCode.COMPLETED.value, null);
            case getMaxMinReport1:
            case getMaxMinReport2:
                String[] words = methodName.split("\\*");
                String componentName = words[0];
                if (temperatureReadings.containsKey(componentName)) {
                    Date since = getCommandRequestValue(jsonRequest, Date.class);
                    log.debug("Command: Received - component=\"{}\", generating min, max, avg temperature report since {}", componentName, since);
                    Map<Date, Double> allReadings = temperatureReadings.get(componentName);
                    Map<Date, Double> filteredReadings = allReadings.entrySet().stream()
                            .filter(map -> map.getKey().after(since))
                            .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
                    if (!filteredReadings.isEmpty()) {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                        double maxTemp = Collections.max(filteredReadings.values());
                        double minTemp = Collections.min(filteredReadings.values());
                        double avgTemp = filteredReadings.values().stream().mapToDouble(Double::doubleValue).average().orElse(Double.NaN);
                        String startTime =  sdf.format(Collections.min(filteredReadings.keySet()));
                        String endTime =  sdf.format(Collections.max(filteredReadings.keySet()));
                        String responsePayload = String.format(
                                "{\"maxTemp\": %.1f, \"minTemp\": %.1f, \"avgTemp\": %.1f, \"startTime\": \"%s\", \"endTime\": \"%s\"}",
                                maxTemp,
                                minTemp,
                                avgTemp,
                                startTime,
                                endTime);
                        log.debug("Command: MaxMinReport since {}: \"maxTemp\": {}°C, \"minTemp\": {}°C, \"avgTemp\": {}°C, \"startTime\": {}, \"endTime\": {}",
                                since,
                                maxTemp,
                                minTemp,
                                avgTemp,
                                startTime,
                                endTime);
                        return new DirectMethodResponse(StatusCode.COMPLETED.value, responsePayload);
                    }
                    log.debug("Command: component=\"{}\", no relevant readings found since {}, cannot generate any report.", componentName, since);
                    return new DirectMethodResponse(StatusCode.NOT_FOUND.value, null);
                }
                log.debug("Command: component=\"{}\", no temperature readings sent yet, cannot generate any report.", componentName);
                return new DirectMethodResponse(StatusCode.NOT_FOUND.value, null);
            default:
                log.debug("Command: command=\"{}\" is not implemented, no action taken.", methodName);
                return new DirectMethodResponse(StatusCode.NOT_FOUND.value, null);
        }
    }
}

Obter informações de conexão

Quando você executar o aplicativo de dispositivo de exemplo mais adiante neste tutorial, precisará dos seguintes valores de configuração:

  • Escopo da ID: no aplicativo do IoT Central, acesse Permissões > Grupos de conexão de dispositivo. Anote o valor de Escopo da ID.
  • Chave primária do grupo: No aplicativo do IoT Central, acesse Permissões > Grupos de conexão de dispositivo > Dispositivos IoT de SAS. Anote o valor da Chave primária da Assinatura de Acesso Compartilhado.

Use o Azure Cloud Shell para gerar uma chave de dispositivo com base na chave primária de grupo que você recuperou:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Anote a chave de dispositivo gerada, pois você a usará mais adiante neste tutorial.

Observação

Para executar este exemplo, você não precisa registrar o dispositivo com antecedência no seu aplicativo do IoT Central. O exemplo usa a funcionalidade do IoT Central para registrar automaticamente os dispositivos quando eles se conectam pela primeira vez.

No Windows, acesse a pasta raiz do SDK do Azure IoT para o repositório do Java que você baixou.

Execute o seguinte comando para compilar o aplicativo de exemplo:

mvn install -T 2C -DskipTests

Executar o código

Para executar o aplicativo de exemplo, abra um ambiente de linha de comando e acesse a pasta azure-iot-sdk-java/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample que contém a pasta src com o arquivo de exemplo TemperatureController.java.

Defina as variáveis de ambiente para configurar o exemplo. O snippet a seguir mostra como definir as variáveis de ambiente no prompt de comando do Windows. Se estiver usando um shell do Bash, substitua os comandos set pelos comandos export:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Execute o exemplo:

mvn exec:java -Dexec.mainClass="samples.com.microsoft.azure.sdk.iot.device.TemperatureController"

A saída a seguir mostra o dispositivo se registrando e se conectando ao IoT Central. O exemplo começa a enviar a telemetria:

2021-03-30 15:33:25.138 DEBUG TemperatureController:123 - Initialize the device client.
Waiting for Provisioning Service to register
Waiting for Provisioning Service to register
IotHUb Uri : iotc-60a.....azure-devices.net
Device ID : sample-device-01
2021-03-30 15:33:38.294 DEBUG TemperatureController:247 - Opening the device client.
2021-03-30 15:33:38.307 INFO  ExponentialBackoffWithJitter:98 - NOTE: A new instance of ExponentialBackoffWithJitter has been created with the following properties. Retry Count: 2147483647, Min Backoff Interval: 100, Max Backoff Interval: 10000, Max Time Between Retries: 100, Fast Retry Enabled: true
2021-03-30 15:33:38.321 INFO  ExponentialBackoffWithJitter:98 - NOTE: A new instance of ExponentialBackoffWithJitter has been created with the following properties. Retry Count: 2147483647, Min Backoff Interval: 100, Max Backoff Interval: 10000, Max Time Between Retries: 100, Fast Retry Enabled: true
2021-03-30 15:33:38.427 DEBUG MqttIotHubConnection:274 - Opening MQTT connection...
2021-03-30 15:33:38.427 DEBUG Mqtt:123 - Sending MQTT CONNECT packet...
2021-03-30 15:33:44.628 DEBUG Mqtt:126 - Sent MQTT CONNECT packet was acknowledged
2021-03-30 15:33:44.630 DEBUG Mqtt:256 - Sending MQTT SUBSCRIBE packet for topic devices/sample-device-01/messages/devicebound/#
2021-03-30 15:33:44.731 DEBUG Mqtt:261 - Sent MQTT SUBSCRIBE packet for topic devices/sample-device-01/messages/devicebound/# was acknowledged
2021-03-30 15:33:44.733 DEBUG MqttIotHubConnection:279 - MQTT connection opened successfully
2021-03-30 15:33:44.733 DEBUG IotHubTransport:302 - The connection to the IoT Hub has been established
2021-03-30 15:33:44.734 INFO  IotHubTransport:1429 - Updating transport status to new status CONNECTED with reason CONNECTION_OK
2021-03-30 15:33:44.735 DEBUG IotHubTransport:1439 - Invoking connection status callbacks with new status details
2021-03-30 15:33:44.739 DEBUG IotHubTransport:394 - Client connection opened successfully
2021-03-30 15:33:44.740 INFO  DeviceClient:438 - Device client opened successfully
2021-03-30 15:33:44.740 DEBUG TemperatureController:152 - Set handler for "reboot" command.
2021-03-30 15:33:44.742 DEBUG TemperatureController:153 - Set handler for "getMaxMinReport" command.
2021-03-30 15:33:44.774 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [029d30d4-acbd-462d-b155-82d53ce7786c] Message Id [1b2adf93-ba81-41e4-b8c7-7c90c8b0d6a1] Device Operation Type [DEVICE_OPERATION_METHOD_SUBSCRIBE_REQUEST] )
2021-03-30 15:33:44.774 DEBUG TemperatureController:156 - Set handler to receive "targetTemperature" updates.
2021-03-30 15:33:44.775 INFO  IotHubTransport:1344 - Sending message ( Message details: Correlation Id [029d30d4-acbd-462d-b155-82d53ce7786c] Message Id [1b2adf93-ba81-41e4-b8c7-7c90c8b0d6a1] Device Operation Type [DEVICE_OPERATION_METHOD_SUBSCRIBE_REQUEST] )
2021-03-30 15:33:44.779 DEBUG Mqtt:256 - Sending MQTT SUBSCRIBE packet for topic $iothub/methods/POST/#
2021-03-30 15:33:44.793 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [f2f9ed95-9778-44f2-b9ec-f60c84061251] Message Id [0d5abdb2-6460-414c-a10e-786ee24cacff] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.794 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [417d659a-7324-43fa-84eb-8a3f3d07963c] Message Id [55532cad-8a5a-489f-9aa8-8f0e5bc21541] Request Id [0] Device Operation Type [DEVICE_OPERATION_TWIN_GET_REQUEST] )
2021-03-30 15:33:44.819 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [d46a0d8a-8a18-4014-abeb-768bd9b17ad2] Message Id [780abc81-ce42-4e5f-aa80-e4785883604e] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.881 DEBUG Mqtt:261 - Sent MQTT SUBSCRIBE packet for topic $iothub/methods/POST/# was acknowledged
2021-03-30 15:33:44.882 INFO  IotHubTransport:1344 - Sending message ( Message details: Correlation Id [f2f9ed95-9778-44f2-b9ec-f60c84061251] Message Id [0d5abdb2-6460-414c-a10e-786ee24cacff] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.882 DEBUG Mqtt:256 - Sending MQTT SUBSCRIBE packet for topic $iothub/twin/res/#
2021-03-30 15:33:44.893 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [a77b1c02-f043-4477-b610-e31a774772c0] Message Id [2e2f6bee-c480-42cf-ac31-194118930846] Request Id [1] Device Operation Type [DEVICE_OPERATION_TWIN_UPDATE_REPORTED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.904 DEBUG TemperatureController:423 - Property: Update - component = "deviceInformation" is COMPLETED.
2021-03-30 15:33:44.915 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [bbb7e3cf-3550-4fdf-90f9-0787740f028a] Message Id [e06ac385-ae0d-46dd-857a-d9725707527a] )
2021-03-30 15:33:44.915 DEBUG TemperatureController:434 - Telemetry: Sent - {"workingSet": 1024.0KiB }
2021-03-30 15:33:44.915 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [6dbef765-cc9a-4e72-980a-2fe5b0cd77e1] Message Id [49bbad33-09bf-417a-9d6e-299ba7b7c562] Request Id [2] Device Operation Type [DEVICE_OPERATION_TWIN_UPDATE_REPORTED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.916 DEBUG TemperatureController:442 - Property: Update - {"serialNumber": SR-123456} is COMPLETED
2021-03-30 15:33:44.927 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [86787c32-87a5-4c49-9083-c7f2b17446a7] Message Id [0a45fa0c-a467-499d-b214-9bb5995772ba] )
2021-03-30 15:33:44.927 DEBUG TemperatureController:461 - Telemetry: Sent - {"temperature": 5.8°C} with message Id 0a45fa0c-a467-499d-b214-9bb5995772ba.

Como operador no seu aplicativo do Azure IoT Central, você pode:

  • Exibir a telemetria enviada pelos dois componentes termostatos na página Visão geral:

    Screenshot that shows the device overview page.

  • Exibir as propriedades do dispositivo na página Sobre. Esta página mostra as propriedades do componente de informações do dispositivo e dos dois componentes termostatos:

    Screenshot that shows the device properties view.

Personalizar o modelo de dispositivo

Como desenvolvedor de soluções, você pode personalizar o modelo de dispositivo criado automaticamente pelo IoT Central quando o dispositivo controlador de temperatura foi conectado.

Para adicionar uma propriedade de nuvem para armazenar o nome do cliente associado ao dispositivo:

  1. No aplicativo do IoT Central, acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivos.

  2. No modelo Controlador de Temperatura, selecione +Adicionar funcionalidade.

  3. Insira Nome do cliente como o Nome de exibição, selecione Propriedade de nuvem como o tipo de capacidade, expanda a entrada e escolha Cadeia de caracteres como Esquema. Depois, selecione Salvar.

Para personalizar como os comandos Obter relatório de Máx.-Mín. são exibidos no aplicativo do IoT Central:

  1. Acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivo.

  2. Para getMaxMinReport (thermostat1), substitua Obter relatório de Máx.-Mín. por Obter relatório de status de termostato1.

  3. Para getMaxMinReport (thermostat2), substitua Obter relatório de Máx.-Mín. por Obter relatório de status de termostato2.

  4. Clique em Salvar.

Para personalizar como as propriedades graváveis da temperatura de destino são exibidas no aplicativo do IoT Central:

  1. Acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivo.

  2. Para targetTemperature (thermostat1) , substitua a Temperatura de destino pela Temperatura de destino (1) .

  3. Para targetTemperature (thermostat2) , substitua a Temperatura de destino pela Temperatura de destino (2) .

  4. Clique em Salvar.

Os componentes do termostato no modelo de Controlador de Temperatura incluem a propriedade gravável Temperatura de Destino e o modelo de dispositivo inclui a propriedade de nuvem Nome do Cliente. Crie uma exibição que um operador possa usar para editar estas propriedades:

  1. Selecione Exibições e, em seguida, o bloco Editando dados do dispositivo e da nuvem.

  2. Insira Propriedades como o nome do formulário.

  3. Selecione as propriedades Temperatura de destino (1), Temperatura de destino (2) e Nome do cliente. Em seguida, selecione Adicionar seção.

  4. Salve suas alterações.

Screenshot that shows a view for updating property values.

Publicar o modelo de dispositivo

Para que um operador possa ver e usar as personalizações feitas por você, publique o modelo de dispositivo.

No modelo de dispositivo de Termostato, selecione Publicar. No painel Publicar este modelo de dispositivo no aplicativo, selecione Publicar.

Agora, um operador poderá usar a exibição Propriedades para atualizar os valores da propriedade e chamar os comandos Obter relatório de status do thermostat1 e Obter relatório de status do thermostat2 na página de comandos do dispositivo:

  • Atualize os valores de propriedades graváveis na página Propriedades:

    Screenshot that shows updating the device properties.

  • Chame os comandos na página Comandos. Se você executar o comando de relatório de status, selecione uma data e uma hora para o parâmetro Desde antes de executá-lo:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Você pode ver como o dispositivo responde a comandos e a atualizações de propriedade:

2021-03-30 15:43:57.133 DEBUG TemperatureController:309 - Command: Received - component="thermostat1", generating min, max, avg temperature report since Tue Mar 30 06:00:00 BST 2021
2021-03-30 15:43:57.153 DEBUG TemperatureController:332 - Command: MaxMinReport since Tue Mar 30 06:00:00 BST 2021: "maxTemp": 35.6°C, "minTemp": 35.6°C, "avgTemp": 35.6°C, "startTime": 2021-03-30T15:43:41Z, "endTime": 2021-03-30T15:43:56Z
2021-03-30 15:43:57.394 DEBUG TemperatureController:502 - Command - Response from IoT Hub: command name=null, status=OK_EMPTY


...

2021-03-30 15:48:47.808 DEBUG TemperatureController:372 - Property: Received - component="thermostat2", {"targetTemperature": 67.0°C}.
2021-03-30 15:48:47.837 DEBUG TemperatureController:382 - Property: Update - component="thermostat2", {"targetTemperature": 67.0°C} is IN_PROGRESS

Browse code

Pré-requisitos

Para concluir as etapas neste artigo, você precisa dos seguintes recursos:

  • Um computador de desenvolvimento com o Node.js versão 6 ou posterior instalado. É possível executar node --version na linha de comando para verificar a versão. As instruções deste tutorial pressupõem que você esteja executando o comando node no prompt de comando do Windows. Contudo, você pode usar o Node.js em muitos outros sistemas operacionais.

  • Uma cópia local do repositório GitHub do SDK do Microsoft Azure IoT para Node.js que contém o código de exemplo. Use este link para baixar uma cópia do repositório: Baixar ZIP. Em seguida, descompacte o arquivo em uma localização adequada no computador local.

Examine o código

Na cópia do SDK da Internet das Coisas do Microsoft Azure para Node.js baixado anteriormente, abra o arquivo azure-iot-sdk-node/device/samples/javascript/pnp_temperature_controller.js em um editor de texto.

O exemplo implementa o modelo de Linguagem de Definição de Gêmeo Digital do Controlador de Temperatura de vários componentes.

Quando você executa o exemplo para se conectar ao IoT Central, ele usa o DPS (Serviço de Provisionamento de Dispositivos) para registrar o dispositivo e gerar uma cadeia de conexão. O exemplo recupera as informações de conexão do DPS necessárias por meio do ambiente de linha de comando.

O método main:

  • Cria um objeto client e define a ID do modelo dtmi:com:example:TemperatureController;2 antes de abrir a conexão. O IoT Central usa a ID do modelo para identificar ou gerar o modelo de dispositivo para este dispositivo. Para saber mais, confira Associar um dispositivo a um modelo de dispositivo.
  • Cria manipuladores de comando para três comandos.
  • Inicia um loop para cada componente termostato a fim de enviar telemetria de temperatura a cada 5 segundos.
  • Inicia um loop para o componente padrão enviar telemetria de tamanho de conjunto de trabalho a cada 6 segundos.
  • Envia a propriedade maxTempSinceLastReboot para cada componente termostato.
  • Envia as propriedades das informações do dispositivo.
  • Cria manipuladores de propriedades graváveis para os três componentes.
async function main() {
  // ...

  // fromConnectionString must specify a transport, coming from any transport package.
  const client = Client.fromConnectionString(deviceConnectionString, Protocol);
  console.log('Connecting using connection string: ' + deviceConnectionString);
  let resultTwin;

  try {
    // Add the modelId here
    await client.setOptions(modelIdObject);
    await client.open();
    console.log('Enabling the commands on the client');
    client.onDeviceMethod(commandNameGetMaxMinReport1, commandHandler);
    client.onDeviceMethod(commandNameGetMaxMinReport2, commandHandler);
    client.onDeviceMethod(commandNameReboot, commandHandler);

    // Send Telemetry after some interval
    let index1 = 0;
    let index2 = 0;
    let index3 = 0;
    intervalToken1 = setInterval(() => {
      const data = JSON.stringify(thermostat1.updateSensor().getCurrentTemperatureObject());
      sendTelemetry(client, data, index1, thermostat1ComponentName).catch((err) => console.log('error ', err.toString()));
      index1 += 1;
    }, 5000);

    intervalToken2 = setInterval(() => {
      const data = JSON.stringify(thermostat2.updateSensor().getCurrentTemperatureObject());
      sendTelemetry(client, data, index2, thermostat2ComponentName).catch((err) => console.log('error ', err.toString()));
      index2 += 1;
    }, 5500);


    intervalToken3 = setInterval(() => {
      const data = JSON.stringify({ workingset: 1 + (Math.random() * 90) });
      sendTelemetry(client, data, index3, null).catch((err) => console.log('error ', err.toString()));
      index3 += 1;
    }, 6000);

    // attach a standard input exit listener
    exitListener(client);

    try {
      resultTwin = await client.getTwin();
      // Only report readable properties
      const patchRoot = helperCreateReportedPropertiesPatch({ serialNumber: serialNumber }, null);
      const patchThermostat1Info = helperCreateReportedPropertiesPatch({
        maxTempSinceLastReboot: thermostat1.getMaxTemperatureValue(),
      }, thermostat1ComponentName);

      const patchThermostat2Info = helperCreateReportedPropertiesPatch({
        maxTempSinceLastReboot: thermostat2.getMaxTemperatureValue(),
      }, thermostat2ComponentName);

      const patchDeviceInfo = helperCreateReportedPropertiesPatch({
        manufacturer: 'Contoso Device Corporation',
        model: 'Contoso 47-turbo',
        swVersion: '10.89',
        osName: 'Contoso_OS',
        processorArchitecture: 'Contoso_x86',
        processorManufacturer: 'Contoso Industries',
        totalStorage: 65000,
        totalMemory: 640,
      }, deviceInfoComponentName);

      // the below things can only happen once the twin is there
      updateComponentReportedProperties(resultTwin, patchRoot, null);
      updateComponentReportedProperties(resultTwin, patchThermostat1Info, thermostat1ComponentName);
      updateComponentReportedProperties(resultTwin, patchThermostat2Info, thermostat2ComponentName);
      updateComponentReportedProperties(resultTwin, patchDeviceInfo, deviceInfoComponentName);
      desiredPropertyPatchListener(resultTwin, [thermostat1ComponentName, thermostat2ComponentName, deviceInfoComponentName]);
    } catch (err) {
      console.error('could not retrieve twin or report twin properties\n' + err.toString());
    }
  } catch (err) {
    console.error('could not connect Plug and Play client or could not attach interval function for telemetry\n' + err.toString());
  }
}

A função provisionDevice mostra como o dispositivo usa o DPS para se registrar no IoT Central e se conectar a ele. O conteúdo inclui a ID do modelo que o IoT Central usa para Atribuir um dispositivo a um modelo de dispositivo:

async function provisionDevice(payload) {
  var provSecurityClient = new SymmetricKeySecurityClient(registrationId, symmetricKey);
  var provisioningClient = ProvisioningDeviceClient.create(provisioningHost, idScope, new ProvProtocol(), provSecurityClient);

  if (payload) {
    provisioningClient.setProvisioningPayload(payload);
  }

  try {
    let result = await provisioningClient.register();
    deviceConnectionString = 'HostName=' + result.assignedHub + ';DeviceId=' + result.deviceId + ';SharedAccessKey=' + symmetricKey;
    console.log('registration succeeded');
    console.log('assigned hub=' + result.assignedHub);
    console.log('deviceId=' + result.deviceId);
    console.log('payload=' + JSON.stringify(result.payload));
  } catch (err) {
    console.error("error registering device: " + err.toString());
  }
}

A função sendTelemetry mostra como o dispositivo envia a telemetria de temperatura para o IoT Central. Para a telemetria dos componentes, ele adiciona uma propriedade chamada $.sub com o nome do componente:

async function sendTelemetry(deviceClient, data, index, componentName) {
  if componentName) {
    console.log('Sending telemetry message %d from component: %s ', index, componentName);
  } else {
    console.log('Sending telemetry message %d from root interface', index);
  }
  const msg = new Message(data);
  if (componentName) {
    msg.properties.add(messageSubjectProperty, componentName);
  }
  msg.contentType = 'application/json';
  msg.contentEncoding = 'utf-8';
  await deviceClient.sendEvent(msg);
}

O método main usa um método auxiliar chamado helperCreateReportedPropertiesPatch para criar mensagens de atualização de propriedade. Esse método usa um parâmetro opcional para especificar o componente que está enviando a propriedade:

const helperCreateReportedPropertiesPatch = (propertiesToReport, componentName) => {
  let patch;
  if (!!(componentName)) {
    patch = { };
    propertiesToReport.__t = 'c';
    patch[componentName] = propertiesToReport;
  } else {
    patch = { };
    patch = propertiesToReport;
  }
  if (!!(componentName)) {
    console.log('The following properties will be updated for component: ' + componentName);
  } else {
    console.log('The following properties will be updated for root interface.');
  }
  console.log(patch);
  return patch;
};

O método main usa os dois métodos a seguir para gerenciar as atualizações de propriedade graváveis por meio do IoT Central. Observe como o método cria a resposta com a versão e o código de status:

const desiredPropertyPatchListener = (deviceTwin, componentNames) => {
  deviceTwin.on('properties.desired', (delta) => {
    console.log('Received an update for device with value: ' + JSON.stringify(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') {
            console.log('Will update property: ' + propertyName + ' to value: ' + propertyValue + ' of component: ' + componentName);
            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 = { };
        console.log('Will update property: ' + key + ' to value: ' + values + ' for root');
        const propertyContent = { value: values };
        propertyContent.ac = 200;
        propertyContent.ad = 'Successfully executed patch';
        propertyContent.av = version;
        patchForRoot[key] = propertyContent;
        updateComponentReportedProperties(deviceTwin, patchForRoot, null);
      }
    });
  });
};

O método main usa os métodos seguintes para gerenciar comandos do IoT Central:

const commandHandler = async (request, response) => {
  helperLogCommandRequest(request);
  switch (request.methodName) {
  case commandNameGetMaxMinReport1: {
    await sendCommandResponse(request, response, 200, thermostat1.getMaxMinReportObject());
    break;
  }
  case commandNameGetMaxMinReport2: {
    await sendCommandResponse(request, response, 200, thermostat2.getMaxMinReportObject());
    break;
  }
  case commandNameReboot: {
    await sendCommandResponse(request, response, 200, 'reboot response');
    break;
  }
  default:
    await sendCommandResponse(request, response, 404, 'unknown method');
    break;
  }
};

const sendCommandResponse = async (request, response, status, payload) => {
  try {
    await response.send(status, payload);
    console.log('Response to method: ' + request.methodName + ' sent successfully.' );
  } catch (err) {
    console.error('An error occurred when sending a method response:\n' + err.toString());
  }
};

Obter informações de conexão

Quando você executar o aplicativo de dispositivo de exemplo mais adiante neste tutorial, precisará dos seguintes valores de configuração:

  • Escopo da ID: no aplicativo do IoT Central, acesse Permissões > Grupos de conexão de dispositivo. Anote o valor de Escopo da ID.
  • Chave primária do grupo: No aplicativo do IoT Central, acesse Permissões > Grupos de conexão de dispositivo > Dispositivos IoT de SAS. Anote o valor da Chave primária da Assinatura de Acesso Compartilhado.

Use o Azure Cloud Shell para gerar uma chave de dispositivo com base na chave primária de grupo que você recuperou:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Anote a chave de dispositivo gerada, pois você a usará mais adiante neste tutorial.

Observação

Para executar este exemplo, você não precisa registrar o dispositivo com antecedência no seu aplicativo do IoT Central. O exemplo usa a funcionalidade do IoT Central para registrar automaticamente os dispositivos quando eles se conectam pela primeira vez.

Executar o código

Para executar o aplicativo de exemplo, abra um ambiente de linha de comando e navegue até a pasta azure-iot-sdk-node/device/samples/javascript que contém o arquivo de exemplo pnp_temperature_controller.js.

Defina as variáveis de ambiente para configurar o exemplo. O snippet a seguir mostra como definir as variáveis de ambiente no prompt de comando do Windows. Se estiver usando um shell do Bash, substitua os comandos set pelos comandos export:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Instale os pacotes necessários:

npm install

Execute o exemplo:

node pnp_temperature_controller.js

A saída a seguir mostra o dispositivo se registrando e se conectando ao IoT Central. Em seguida, o exemplo envia a propriedade maxTempSinceLastReboot dos dois componentes termostatos antes de começar a enviar a telemetria:

registration succeeded
assigned hub=iotc-....azure-devices.net
deviceId=sample-device-01
payload=undefined
Connecting using connection string: HostName=iotc-....azure-devices.net;DeviceId=sample-device-01;SharedAccessKey=qdv...IpAo=
Enabling the commands on the client
Please enter q or Q to exit sample.
The following properties will be updated for root interface.
{ serialNumber: 'alwinexlepaho8329' }
The following properties will be updated for component: thermostat1
{ thermostat1: { maxTempSinceLastReboot: 1.5902294191855972, __t: 'c' } }
The following properties will be updated for component: thermostat2
{ thermostat2: { maxTempSinceLastReboot: 16.181771928614545, __t: 'c' } }
The following properties will be updated for component: deviceInformation
{ deviceInformation:
   { manufacturer: 'Contoso Device Corporation',
     model: 'Contoso 47-turbo',
     swVersion: '10.89',
     osName: 'Contoso_OS',
     processorArchitecture: 'Contoso_x86',
     processorManufacturer: 'Contoso Industries',
     totalStorage: 65000,
     totalMemory: 640,
     __t: 'c' } }
executed sample
Received an update for device with value: {"$version":1}
Properties have been reported for component: thermostat1
Properties have been reported for component: thermostat2
Properties have been reported for component: deviceInformation
Properties have been reported for root interface.
Sending telemetry message 0 from component: thermostat1 
Sending telemetry message 0 from component: thermostat2 
Sending telemetry message 0 from root interface

Como operador no seu aplicativo do Azure IoT Central, você pode:

  • Exibir a telemetria enviada pelos dois componentes termostatos na página Visão geral:

    Screenshot that shows the device overview page.

  • Exibir as propriedades do dispositivo na página Sobre. Esta página mostra as propriedades do componente de informações do dispositivo e dos dois componentes termostatos:

    Screenshot that shows the device properties view.

Personalizar o modelo de dispositivo

Como desenvolvedor de soluções, você pode personalizar o modelo de dispositivo criado automaticamente pelo IoT Central quando o dispositivo controlador de temperatura foi conectado.

Para adicionar uma propriedade de nuvem para armazenar o nome do cliente associado ao dispositivo:

  1. No aplicativo do IoT Central, acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivos.

  2. No modelo Controlador de Temperatura, selecione +Adicionar funcionalidade.

  3. Insira Nome do cliente como o Nome de exibição, selecione Propriedade de nuvem como o tipo de capacidade, expanda a entrada e escolha Cadeia de caracteres como Esquema. Depois, selecione Salvar.

Para personalizar como os comandos Obter relatório de Máx.-Mín. são exibidos no aplicativo do IoT Central:

  1. Acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivo.

  2. Para getMaxMinReport (thermostat1), substitua Obter relatório de Máx.-Mín. por Obter relatório de status de termostato1.

  3. Para getMaxMinReport (thermostat2), substitua Obter relatório de Máx.-Mín. por Obter relatório de status de termostato2.

  4. Clique em Salvar.

Para personalizar como as propriedades graváveis da temperatura de destino são exibidas no aplicativo do IoT Central:

  1. Acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivo.

  2. Para targetTemperature (thermostat1) , substitua a Temperatura de destino pela Temperatura de destino (1) .

  3. Para targetTemperature (thermostat2) , substitua a Temperatura de destino pela Temperatura de destino (2) .

  4. Clique em Salvar.

Os componentes do termostato no modelo de Controlador de Temperatura incluem a propriedade gravável Temperatura de Destino e o modelo de dispositivo inclui a propriedade de nuvem Nome do Cliente. Crie uma exibição que um operador possa usar para editar estas propriedades:

  1. Selecione Exibições e, em seguida, o bloco Editando dados do dispositivo e da nuvem.

  2. Insira Propriedades como o nome do formulário.

  3. Selecione as propriedades Temperatura de destino (1), Temperatura de destino (2) e Nome do cliente. Em seguida, selecione Adicionar seção.

  4. Salve suas alterações.

Screenshot that shows a view for updating property values.

Publicar o modelo de dispositivo

Para que um operador possa ver e usar as personalizações feitas por você, publique o modelo de dispositivo.

No modelo de dispositivo de Termostato, selecione Publicar. No painel Publicar este modelo de dispositivo no aplicativo, selecione Publicar.

Agora, um operador poderá usar a exibição Propriedades para atualizar os valores da propriedade e chamar os comandos Obter relatório de status do thermostat1 e Obter relatório de status do thermostat2 na página de comandos do dispositivo:

  • Atualize os valores de propriedades graváveis na página Propriedades:

    Screenshot that shows updating the device properties.

  • Chame os comandos na página Comandos. Se você executar o comando de relatório de status, selecione uma data e uma hora para o parâmetro Desde antes de executá-lo:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Você pode ver como o dispositivo responde a comandos e a atualizações de propriedade. O comando getMaxMinReport está no componente thermostat2, o comando reboot está no componente padrão. A propriedade gravável targetTemperature foi definida para o componente thermostat2:

Received command request for command name: thermostat2*getMaxMinReport
The command request payload is:
2021-03-26T06:00:00.000Z
Response to method: thermostat2*getMaxMinReport sent successfully.

...

Received command request for command name: reboot
The command request payload is:
10
Response to method: reboot sent successfully.

...

Received an update for device with value: {"thermostat2":{"targetTemperature":76,"__t":"c"},"$version":2}
Will update property: targetTemperature to value: 76 of component: thermostat2
Properties have been reported for component: thermostat2

Browse code

Pré-requisitos

Para concluir as etapas neste artigo, você precisa dos seguintes recursos:

  • Um computador de desenvolvimento com o Python instalado. Verifique o SDK do Python da Internet das Coisas do Azure para os requisitos atuais da versão do Python. É possível executar python --version na linha de comando para verificar a versão. O Python está disponível para uma ampla variedade de sistemas operacionais. As instruções deste tutorial pressupõem que você esteja executando o comando python no prompt de comando do Windows.

  • Uma cópia local do repositório GitHub do SDK do Microsoft Azure IoT para Python que contém o código de exemplo. Use este link para baixar uma cópia do repositório: Baixar ZIP. Em seguida, descompacte o arquivo em uma localização adequada no computador local.

Examine o código

Na cópia do SDK do Microsoft Azure IoT para Python baixado anteriormente, abra o arquivo azure-iot-sdk-python/samples/pnp/temp_controller_with_thermostats.py em um editor de texto.

O exemplo implementa o modelo de Linguagem de Definição de Gêmeo Digital do Controlador de Temperatura de vários componentes.

Quando você executa o exemplo para se conectar ao IoT Central, ele usa o DPS (Serviço de Provisionamento de Dispositivos) para registrar o dispositivo e gerar uma cadeia de conexão. O exemplo recupera as informações de conexão do DPS necessárias por meio do ambiente de linha de comando.

A função main:

  • Usa o DPS para provisionar o dispositivo. As informações de provisionamento incluem a ID do modelo. O IoT Central usa a ID do modelo para identificar ou gerar o modelo de dispositivo para este dispositivo. Para saber mais, confira Associar um dispositivo a um modelo de dispositivo.
  • Cria um objeto Device_client e define a ID do modelo dtmi:com:example:TemperatureController;2 antes de abrir a conexão.
  • Envia os valores da propriedade iniciais ao IoT Central. Ele usa o pnp_helper para criar os patches.
  • Cria ouvintes para os comandos getMaxMinReport e reboot. Cada componente termostato tem seu próprio comando getMaxMinReport.
  • Cria o ouvinte de propriedade para escutar as atualizações de propriedade gravável.
  • Inicia um loop para enviar a telemetria de temperatura dos dois componentes termostatos e a telemetria do conjunto de trabalho do componente padrão a cada 8 segundos.
async def main():
    switch = os.getenv("IOTHUB_DEVICE_SECURITY_TYPE")
    if switch == "DPS":
        provisioning_host = (
            os.getenv("IOTHUB_DEVICE_DPS_ENDPOINT")
            if os.getenv("IOTHUB_DEVICE_DPS_ENDPOINT")
            else "global.azure-devices-provisioning.net"
        )
        id_scope = os.getenv("IOTHUB_DEVICE_DPS_ID_SCOPE")
        registration_id = os.getenv("IOTHUB_DEVICE_DPS_DEVICE_ID")
        symmetric_key = os.getenv("IOTHUB_DEVICE_DPS_DEVICE_KEY")

        registration_result = await provision_device(
            provisioning_host, id_scope, registration_id, symmetric_key, model_id
        )

        if registration_result.status == "assigned":
            print("Device was assigned")
            print(registration_result.registration_state.assigned_hub)
            print(registration_result.registration_state.device_id)
            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,
            )
        else:
            raise RuntimeError(
                "Could not provision device. Aborting Plug and Play device connection."
            )

    elif switch == "connectionString":
        # ...

    # Connect the client.
    await device_client.connect()

    ################################################
    # Update readable properties from various components

    properties_root = pnp_helper.create_reported_properties(serialNumber=serial_number)
    properties_thermostat1 = pnp_helper.create_reported_properties(
        thermostat_1_component_name, maxTempSinceLastReboot=98.34
    )
    properties_thermostat2 = pnp_helper.create_reported_properties(
        thermostat_2_component_name, maxTempSinceLastReboot=48.92
    )
    properties_device_info = pnp_helper.create_reported_properties(
        device_information_component_name,
        swVersion="5.5",
        manufacturer="Contoso Device Corporation",
        model="Contoso 4762B-turbo",
        osName="Mac Os",
        processorArchitecture="x86-64",
        processorManufacturer="Intel",
        totalStorage=1024,
        totalMemory=32,
    )

    property_updates = asyncio.gather(
        device_client.patch_twin_reported_properties(properties_root),
        device_client.patch_twin_reported_properties(properties_thermostat1),
        device_client.patch_twin_reported_properties(properties_thermostat2),
        device_client.patch_twin_reported_properties(properties_device_info),
    )

    ################################################
    # Get all the listeners running
    print("Listening for command requests and property updates")

    global THERMOSTAT_1
    global THERMOSTAT_2
    THERMOSTAT_1 = Thermostat(thermostat_1_component_name, 10)
    THERMOSTAT_2 = Thermostat(thermostat_2_component_name, 10)

    listeners = asyncio.gather(
        execute_command_listener(
            device_client, method_name="reboot", user_command_handler=reboot_handler
        ),
        execute_command_listener(
            device_client,
            thermostat_1_component_name,
            method_name="getMaxMinReport",
            user_command_handler=max_min_handler,
            create_user_response_handler=create_max_min_report_response,
        ),
        execute_command_listener(
            device_client,
            thermostat_2_component_name,
            method_name="getMaxMinReport",
            user_command_handler=max_min_handler,
            create_user_response_handler=create_max_min_report_response,
        ),
        execute_property_listener(device_client),
    )

    ################################################
    # Function to send telemetry every 8 seconds

    async def send_telemetry():
        print("Sending telemetry from various components")

        while True:
            curr_temp_ext = random.randrange(10, 50)
            THERMOSTAT_1.record(curr_temp_ext)

            temperature_msg1 = {"temperature": curr_temp_ext}
            await send_telemetry_from_temp_controller(
                device_client, temperature_msg1, thermostat_1_component_name
            )

            curr_temp_int = random.randrange(10, 50)  # Current temperature in Celsius
            THERMOSTAT_2.record(curr_temp_int)

            temperature_msg2 = {"temperature": curr_temp_int}

            await send_telemetry_from_temp_controller(
                device_client, temperature_msg2, thermostat_2_component_name
            )

            workingset_msg3 = {"workingSet": random.randrange(1, 100)}
            await send_telemetry_from_temp_controller(device_client, workingset_msg3)

    send_telemetry_task = asyncio.ensure_future(send_telemetry())

    # ...

A função provision_device usa o DPS para provisionar o dispositivo e registrá-lo no IoT Central. A função inclui a ID do modelo do dispositivo, que o IoT Central usa para atribuir um dispositivo a um modelo de dispositivo, no conteúdo de provisionamento:

async def provision_device(provisioning_host, id_scope, registration_id, symmetric_key, model_id):
    provisioning_device_client = ProvisioningDeviceClient.create_from_symmetric_key(
        provisioning_host=provisioning_host,
        registration_id=registration_id,
        id_scope=id_scope,
        symmetric_key=symmetric_key,
    )

    provisioning_device_client.provisioning_payload = {"modelId": model_id}
    return await provisioning_device_client.register()

A função execute_command_listener gerencia solicitações de comando, executa a função max_min_handler quando o dispositivo recebe o comando getMaxMinReport para os componentes termostatos e a função reboot_handler quando o dispositivo recebe o comando reboot. Ele usa o módulo pnp_helper para criar a resposta:

async def execute_command_listener(
    device_client,
    component_name=None,
    method_name=None,
    user_command_handler=None,
    create_user_response_handler=None,
):
    while True:
        if component_name and method_name:
            command_name = component_name + "*" + method_name
        elif method_name:
            command_name = method_name
        else:
            command_name = None

        command_request = await device_client.receive_method_request(command_name)
        print("Command request received with payload")
        values = command_request.payload
        print(values)

        if user_command_handler:
            await user_command_handler(values)
        else:
            print("No handler provided to execute")

        (response_status, response_payload) = pnp_helper.create_response_payload_with_status(
            command_request, method_name, create_user_response=create_user_response_handler
        )

        command_response = MethodResponse.create_from_method_request(
            command_request, response_status, response_payload
        )

        try:
            await device_client.send_method_response(command_response)
        except Exception:
            print("responding to the {command} command failed".format(command=method_name))

A função async def execute_property_listener gerencia as atualizações da propriedade gravável, como targetTemperature para os componentes termostatos, e gera a resposta JSON. Ele usa o módulo pnp_helper para criar a resposta:

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

        await device_client.patch_twin_reported_properties(properties_dict)

A função send_telemetry_from_temp_controller envia as mensagens de telemetria dos componentes termostatos para o IoT Central. Ele usa o módulo pnp_helper para criar as mensagens:

async def send_telemetry_from_temp_controller(device_client, telemetry_msg, component_name=None):
    msg = pnp_helper.create_telemetry(telemetry_msg, component_name)
    await device_client.send_message(msg)
    print("Sent message")
    print(msg)
    await asyncio.sleep(5)

Obter informações de conexão

Quando você executar o aplicativo de dispositivo de exemplo mais adiante neste tutorial, precisará dos seguintes valores de configuração:

  • Escopo da ID: no aplicativo do IoT Central, acesse Permissões > Grupos de conexão de dispositivo. Anote o valor de Escopo da ID.
  • Chave primária do grupo: No aplicativo do IoT Central, acesse Permissões > Grupos de conexão de dispositivo > Dispositivos IoT de SAS. Anote o valor da Chave primária da Assinatura de Acesso Compartilhado.

Use o Azure Cloud Shell para gerar uma chave de dispositivo com base na chave primária de grupo que você recuperou:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Anote a chave de dispositivo gerada, pois você a usará mais adiante neste tutorial.

Observação

Para executar este exemplo, você não precisa registrar o dispositivo com antecedência no seu aplicativo do IoT Central. O exemplo usa a funcionalidade do IoT Central para registrar automaticamente os dispositivos quando eles se conectam pela primeira vez.

Executar o código

Para executar o aplicativo de amostra, abra um ambiente de linha de comando e acesse a pasta azure-iot-sdk-python-2/samples/pnp que contém o arquivo de amostra temp_controller_with_thermostats.py.

Defina as variáveis de ambiente para configurar o exemplo. O snippet a seguir mostra como definir as variáveis de ambiente no prompt de comando do Windows. Se estiver usando um shell do Bash, substitua os comandos set pelos comandos export:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Instale os pacotes necessários:

pip install azure-iot-device

Execute o exemplo:

python temp_controller_with_thermostats.py

A saída a seguir mostra o dispositivo se registrando e se conectando ao IoT Central. O exemplo envia as propriedades maxTempSinceLastReboot dos dois componentes termostatos antes de começar a enviar a telemetria:

Device was assigned
iotc-60a.....azure-devices.net
sample-device-01
Updating pnp properties for root interface
{'serialNumber': 'alohomora'}
Updating pnp properties for thermostat1
{'thermostat1': {'maxTempSinceLastReboot': 98.34, '__t': 'c'}}
Updating pnp properties for thermostat2
{'thermostat2': {'maxTempSinceLastReboot': 48.92, '__t': 'c'}}
Updating pnp properties for deviceInformation
{'deviceInformation': {'swVersion': '5.5', 'manufacturer': 'Contoso Device Corporation', 'model': 'Contoso 4762B-turbo', 'osName': 'Mac Os', 'processorArchitecture': 'x86-64', 'processorManufacturer': 'Intel', 'totalStorage': 1024, 'totalMemory': 32, '__t': 'c'}}
Listening for command requests and property updates
Press Q to quit
Sending telemetry from various components
Sent message
{"temperature": 27}
Sent message
{"temperature": 17}
Sent message
{"workingSet": 13}

Como operador no seu aplicativo do Azure IoT Central, você pode:

  • Exibir a telemetria enviada pelos dois componentes termostatos na página Visão geral:

    Screenshot that shows the device overview page.

  • Exibir as propriedades do dispositivo na página Sobre. Esta página mostra as propriedades do componente de informações do dispositivo e dos dois componentes termostatos:

    Screenshot that shows the device properties view.

Personalizar o modelo de dispositivo

Como desenvolvedor de soluções, você pode personalizar o modelo de dispositivo criado automaticamente pelo IoT Central quando o dispositivo controlador de temperatura foi conectado.

Para adicionar uma propriedade de nuvem para armazenar o nome do cliente associado ao dispositivo:

  1. No aplicativo do IoT Central, acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivos.

  2. No modelo Controlador de Temperatura, selecione +Adicionar funcionalidade.

  3. Insira Nome do cliente como o Nome de exibição, selecione Propriedade de nuvem como o tipo de capacidade, expanda a entrada e escolha Cadeia de caracteres como Esquema. Depois, selecione Salvar.

Para personalizar como os comandos Obter relatório de Máx.-Mín. são exibidos no aplicativo do IoT Central:

  1. Acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivo.

  2. Para getMaxMinReport (thermostat1), substitua Obter relatório de Máx.-Mín. por Obter relatório de status de termostato1.

  3. Para getMaxMinReport (thermostat2), substitua Obter relatório de Máx.-Mín. por Obter relatório de status de termostato2.

  4. Clique em Salvar.

Para personalizar como as propriedades graváveis da temperatura de destino são exibidas no aplicativo do IoT Central:

  1. Acesse o modelo de dispositivo do Controlador de temperatura na página Modelos de dispositivo.

  2. Para targetTemperature (thermostat1) , substitua a Temperatura de destino pela Temperatura de destino (1) .

  3. Para targetTemperature (thermostat2) , substitua a Temperatura de destino pela Temperatura de destino (2) .

  4. Clique em Salvar.

Os componentes do termostato no modelo de Controlador de Temperatura incluem a propriedade gravável Temperatura de Destino e o modelo de dispositivo inclui a propriedade de nuvem Nome do Cliente. Crie uma exibição que um operador possa usar para editar estas propriedades:

  1. Selecione Exibições e, em seguida, o bloco Editando dados do dispositivo e da nuvem.

  2. Insira Propriedades como o nome do formulário.

  3. Selecione as propriedades Temperatura de destino (1), Temperatura de destino (2) e Nome do cliente. Em seguida, selecione Adicionar seção.

  4. Salve suas alterações.

Screenshot that shows a view for updating property values.

Publicar o modelo de dispositivo

Para que um operador possa ver e usar as personalizações feitas por você, publique o modelo de dispositivo.

No modelo de dispositivo de Termostato, selecione Publicar. No painel Publicar este modelo de dispositivo no aplicativo, selecione Publicar.

Agora, um operador poderá usar a exibição Propriedades para atualizar os valores da propriedade e chamar os comandos Obter relatório de status do thermostat1 e Obter relatório de status do thermostat2 na página de comandos do dispositivo:

  • Atualize os valores de propriedades graváveis na página Propriedades:

    Screenshot that shows updating the device properties.

  • Chame os comandos na página Comandos. Se você executar o comando de relatório de status, selecione uma data e uma hora para o parâmetro Desde antes de executá-lo:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Você pode ver como o dispositivo responde a comandos e a atualizações de propriedade:

{'thermostat1': {'targetTemperature': 67, '__t': 'c'}, '$version': 2}
the data in the desired properties patch was: {'thermostat1': {'targetTemperature': 67, '__t': 'c'}, '$version': 2}
Values received are :-
{'targetTemperature': 67, '__t': 'c'}
Sent message

...

Command request received with payload
2021-03-31T05:00:00.000Z
Will return the max, min and average temperature from the specified time 2021-03-31T05:00:00.000Z to the current time
Done generating
{"avgTemp": 4.0, "endTime": "2021-03-31T12:29:48.322427", "maxTemp": 18, "minTemp": null, "startTime": "2021-03-31T12:28:28.322381"}

Exibir dados brutos

Use a exibição de Dados brutos para examinar os dados brutos que o dispositivo está enviando para o IoT Central:

Screenshot that shows the raw data view.

Nessa exibição, você pode selecionar as colunas a serem exibidas e definir um intervalo de tempo para exibição. A coluna Dados não modelados mostra dados de dispositivo que não correspondem a nenhuma propriedade ou definição de telemetria do modelo de dispositivo.

Limpar os recursos

Se você não pretende concluir outros guias de início rápido nem tutoriais do IoT Central, pode excluir seu aplicativo do IoT Central:

  1. No aplicativo IoT Central, navegue até Aplicativo > Gerenciamento.
  2. Selecione Excluir e confirme a sua ação.

Próximas etapas

Se você preferir continuar com o conjunto de tutoriais do IoT Central e saber mais sobre como criar uma solução de IoT Central, confira: