Självstudie: Skapa och ansluta ett klientprogram till ditt Azure IoT Central-program

Den här självstudien visar hur du ansluter ett klientprogram till ditt Azure IoT Central-program. Programmet simulerar beteendet för en temperaturstyrenhet. När programmet ansluter till IoT Central skickar det modell-ID:t för temperaturstyrenhetsmodellen. IoT Central använder modell-ID:t för att hämta enhetsmodellen och skapa en enhetsmall åt dig. Du lägger till vyer i enhetsmallen så att en operatör kan interagera med en enhet.

I den här självstudien lär du dig att:

  • Skapa och kör enhetskoden och se den ansluta till ditt IoT Central-program.
  • Visa den simulerade telemetri som skickas från enheten.
  • Lägg till anpassade vyer i en enhetsmall.
  • Publicera enhetsmallen.
  • Använd en vy för att hantera enhetsegenskaper.
  • Anropa ett kommando för att styra enheten.

Browse code

Förutsättningar

För att slutföra stegen i den här självstudien behöver du:

Du kan köra den här självstudien i Linux eller Windows. Shell-kommandona i den här självstudien följer Linux-konventionen för sökvägsavgränsare ,/ om du följer med i Windows måste du växla dessa avgränsare mot "\".

Förutsättningarna skiljer sig åt beroende på operativsystem:

Linux

Den här självstudien förutsätter att du använder Ubuntu Linux. Stegen i den här självstudien har testats med Ubuntu 18.04.

Slutför den här självstudien om Linux genom att installera följande programvara i din lokala Linux-miljö:

Installera GCC, Git, cmake och alla nödvändiga beroenden apt-get med kommandot :

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

Kontrollera att versionen av cmake är större än 2.8.12 och att versionen av GCC är större än 4.4.7.

cmake --version
gcc --version

Windows

Slutför den här självstudien i Windows genom att installera följande programvara i din lokala Windows-miljö:

Ladda ned koden

I den här självstudien förbereder du en utvecklingsmiljö som du kan använda för att klona och skapa Azure IoT Hub Device C SDK.

Öppna en kommandotolk i valfri katalog. Kör följande kommando för att klona GitHub-lagringsplatsen azure IoT C SDK:er och bibliotek till den här platsen:

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

Förvänta dig att den här åtgärden tar flera minuter att slutföra.

Granska koden

I kopian av Microsoft Azure IoT SDK för C som du laddade ned tidigare öppnar du filerna azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_temperature_controller.c och azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_thermostat_component.c i en textredigerare.

Exemplet implementerar modellen för digital tvillingdefinitionsspråk för temperaturkontrollant med flera komponenter.

När du kör exemplet för att ansluta till IoT Central använder det Device Provisioning Service (DPS) för att registrera enheten och generera en anslutningssträng. Exemplet hämtar den DPS-anslutningsinformation som behövs från kommandoradsmiljön.

I pnp_temperature_controller.cmain anropar CreateDeviceClientAndAllocateComponents funktionen först till:

  • Ange modell-ID dtmi:com:example:Thermostat;1 . IoT Central använder modell-ID:t för att identifiera eller generera enhetsmallen för den här enheten. Mer information finns i Tilldela en enhet till en enhetsmall.
  • Använd DPS för att etablera och registrera enheten.
  • Skapa en enhetsklientreferens och anslut till ditt IoT Central-program.
  • Skapar en hanterare för kommandon i temperaturstyrenhetskomponenten.
  • Skapar en hanterare för egenskapsuppdateringar i temperaturstyrenhetskomponenten.
  • Skapar de två termostatkomponenterna.

Funktionen main nästa:

  • Rapporterar några initiala egenskapsvärden för alla komponenter.
  • Startar en loop för att skicka telemetri från alla komponenter.

Funktionen main startar sedan en tråd för att skicka telemetri regelbundet.

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

PnP_ThermostatComponent_SendCurrentTemperature I pnp_thermostat_component.cvisar funktionen hur enheten skickar temperaturtelemetrin från en komponent till 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);
}

PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property I pnp_thermostat_component.cskickar funktionen en maxTempSinceLastReboot egenskapsuppdatering från komponenten till 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);
    }
}

PnP_ThermostatComponent_ProcessPropertyUpdate I pnp_thermostat_component.chanterar funktionen skrivbara egenskapsuppdateringar från 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);
            }
        }
    }
}

PnP_ThermostatComponent_ProcessCommand I pnp_thermostat_component.chanterar funktionen kommandon som anropas från 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;
    }
}

Skapa koden

Du använder enhets-SDK:n för att skapa den inkluderade exempelkoden:

  1. Skapa en cmake-underkatalog i rotmappen för enhetens SDK och gå till den mappen:

    cd azure-iot-sdk-c
    mkdir cmake
    cd cmake
    
  2. Kör följande kommandon för att skapa SDK och exempel:

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

Hämta anslutningsinformation

När du kör exempelprogrammet för enheter senare i den här självstudien behöver du följande konfigurationsvärden:

  • ID-omfång: I ditt IoT Central-program går du till Behörigheter Enhetsanslutningsgrupper>. Anteckna ID-omfångsvärdet.
  • Grupp primärnyckel: I ditt IoT Central-program går du till Behörigheter > Enhetsanslutningsgrupper > SAS-IoT-Enheter. Anteckna värdet för den primära nyckeln för delad åtkomstsignatur.

Använd Azure Cloud Shell för att generera en enhetsnyckel från den grupppriorativnyckel som du hämtade:

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

Anteckna den genererade enhetsnyckeln. Du använder den senare i den här självstudien.

Kommentar

Om du vill köra det här exemplet behöver du inte registrera enheten i förväg i ditt IoT Central-program. Exemplet använder funktionen IoT Central för att automatiskt registrera enheter när de ansluter för första gången.

Kör koden

Om du vill köra exempelprogrammet öppnar du en kommandoradsmiljö och navigerar till mappen azure-iot-sdk-c\cmake.

Ange miljövariablerna för att konfigurera exemplet. Följande kodfragment visar hur du ställer in miljövariablerna i Windows-kommandotolken. Om du använder ett bash-gränssnitt ersätter set du kommandona med export kommandon:

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

Så här kör du exemplet:

# 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

Följande utdata visar enheten som registrerar och ansluter till IoT Central. Exemplet börjar skicka telemetri:

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

Som operatör i ditt Azure IoT Central-program kan du:

  • Visa telemetrin som skickas av de två termostatkomponenterna på översiktssidan:

    Screenshot that shows the device overview page.

  • Visa enhetsegenskaperna på sidan Om . Den här sidan visar egenskaperna från enhetens informationskomponent och de två termostatkomponenterna:

    Screenshot that shows the device properties view.

Anpassa enhetsmallen

Som lösningsutvecklare kan du anpassa enhetsmallen som IoT Central skapade automatiskt när temperaturstyrenheten anslöt.

Så här lägger du till en molnegenskap för att lagra det kundnamn som är associerat med enheten:

  1. I ditt IoT Central-program går du till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. I modellen Temperaturkontrollant väljer du +Lägg till kapacitet.

  3. Ange Kundnamn som Visningsnamn, välj Molnegenskap som kapacitetstyp, expandera posten och välj Sträng som schema. Välj sedan Spara.

Så här anpassar du hur kommandona Hämta Max-Min-rapport visas i ditt IoT Central-program:

  1. Gå till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. För getMaxMinReport (termostat1)ersätter du Hämta Max-Min-rapport. med Hämta termostat1-statusrapport.

  3. För getMaxMinReport (termostat2)ersätter du Hämta Max-Min-rapport. med Hämta termostat2-statusrapport.

  4. Välj Spara.

Så här anpassar du hur skrivbara egenskaper för måltemperatur visas i ditt IoT Central-program:

  1. Gå till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. För targetTemperature (termostat1)ersätter du Måltemperatur med Måltemperatur (1).

  3. För targetTemperature (termostat2)ersätter du Måltemperatur med Måltemperatur (2).

  4. Välj Spara.

Termostatkomponenterna i temperaturkontrollantmodellen innehåller egenskapen Måltemperatur skrivbar, enhetsmallen innehåller molnegenskapen Kundnamn . Skapa en vy som en operator kan använda för att redigera dessa egenskaper:

  1. Välj Vyer och välj sedan panelen Redigera enhet och molndata .

  2. Ange Egenskaper som formulärnamn.

  3. Välj egenskaperna Måltemperatur (1), Måltemperatur (2) och Kundnamn. Välj sedan Lägg till avsnitt.

  4. Spara dina ändringar.

Screenshot that shows a view for updating property values.

Publicera enhetsmallen

Innan en operatör kan se och använda de anpassningar som du har gjort måste du publicera enhetsmallen.

I mallen Termostatenhet väljer du Publicera. I mallen Publicera den här enheten till programpanelen väljer du Publicera.

En operator kan nu använda egenskapsvyn för att uppdatera egenskapsvärdena och anropa kommandon som kallas Hämta termostat1-statusrapport och Hämta termostat2-statusrapport på enhetens kommandosida:

  • Uppdatera skrivbara egenskapsvärden på sidan Egenskaper :

    Screenshot that shows updating the device properties.

  • Anropa kommandona från sidan Kommandon . Om du kör kommandot statusrapport väljer du ett datum och en tid för parametern Sedan innan du kör den:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Du kan se hur enheten svarar på kommandon och egenskapsuppdateringar:

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

Förutsättningar

För att slutföra stegen i den här artikeln behöver du följande resurser:

Granska koden

Öppna azure-iot-sdk-csharp-main\azureiot.sln-lösningsfilen i Visual Studio i kopian av Microsoft Azure IoT SDK för C#-lagringsplatsen som du laddade ned tidigare. I Solution Explorer expanderar du mappen PnpDeviceSamples > TemperatureController och öppnar filerna Program.cs och TemperatureControllerSample.cs för att visa koden för det här exemplet.

Exemplet implementerar modellen för digital tvillingdefinitionsspråk för temperaturkontrollant med flera komponenter.

När du kör exemplet för att ansluta till IoT Central använder det Device Provisioning Service (DPS) för att registrera enheten och generera en anslutningssträng. Exemplet hämtar den DPS-anslutningsinformation som behövs från miljön.

I Program.csMain anropas SetupDeviceClientAsync metoden till:

  • Använd modell-ID dtmi:com:example:TemperatureController;2 :t när det etablerar enheten med DPS. IoT Central använder modell-ID:t för att identifiera eller generera enhetsmallen för den här enheten. Mer information finns i Tilldela en enhet till en enhetsmall.
  • Skapa en DeviceClient-instans för att ansluta till 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;
}

Huvudmetoden skapar sedan en TemperatureControllerSample-instans och anropar PerformOperationsAsync metoden för att hantera interaktionerna med IoT Central.

I TemperatureControllerSample.csPerformOperationsAsync metoden:

  • Anger en hanterare för omstartskommandot på standardkomponenten.
  • Anger hanterare för kommandona getMaxMinReport på de två termostatkomponenterna.
  • Anger att hanterare ska ta emot uppdateringar av måltemperaturegenskapen på de två termostatkomponenterna.
  • Skickar initiala enhetsinformationsegenskapsuppdateringar.
  • Skickar regelbundet temperaturtelemetri från de två termostatkomponenterna.
  • Skickar regelbundet telemetri för arbetsuppsättningar från standardkomponenten.
  • Skickar den högsta temperaturen sedan den senaste omstarten när en ny högsta temperatur uppnås i de två termostatkomponenterna.
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);
  }
}

Metoden SendTemperatureAsync visar hur enheten skickar temperaturtelemetrin från en komponent till IoT Central. Metoden SendTemperatureTelemetryAsync använder PnpConvention klassen för att skapa meddelandet:

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

Metoden UpdateMaxTemperatureSinceLastRebootAsync skickar en maxTempSinceLastReboot egenskapsuppdatering till IoT Central. Den här metoden använder PnpConvention klassen för att skapa korrigeringen:

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

Metoden TargetTemperatureUpdateCallbackAsync hanterar den skrivbara måltemperaturegenskapsuppdateringen från IoT Central. Den här metoden använder PnpConvention klassen för att läsa egenskapsuppdateringsmeddelandet och konstruera svaret:

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

Metoden HandleMaxMinReportCommand hanterar kommandona för de komponenter som anropas från 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)
    {
        // ...
    }
}

Hämta anslutningsinformation

När du kör exempelprogrammet för enheter senare i den här självstudien behöver du följande konfigurationsvärden:

  • ID-omfång: I ditt IoT Central-program går du till Behörigheter Enhetsanslutningsgrupper>. Anteckna ID-omfångsvärdet.
  • Grupp primärnyckel: I ditt IoT Central-program går du till Behörigheter > Enhetsanslutningsgrupper > SAS-IoT-Enheter. Anteckna värdet för den primära nyckeln för delad åtkomstsignatur.

Använd Azure Cloud Shell för att generera en enhetsnyckel från den grupppriorativnyckel som du hämtade:

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

Anteckna den genererade enhetsnyckeln. Du använder den senare i den här självstudien.

Kommentar

Om du vill köra det här exemplet behöver du inte registrera enheten i förväg i ditt IoT Central-program. Exemplet använder funktionen IoT Central för att automatiskt registrera enheter när de ansluter för första gången.

Kör koden

Kommentar

Konfigurera TemperatureController som startprojekt innan du kör koden.

Så här kör du exempelprogrammet i Visual Studio:

  1. I Solution Explorer väljer du projektfilen PnpDeviceSamples > TemperatureController .

  2. Gå till Felsökning av egenskaper >för Project > TemperatureController. Lägg sedan till följande miljövariabler i projektet:

    Name Värde
    IOTHUB_DEVICE_SECURITY_TYPE DPS
    IOTHUB_DEVICE_DPS_ENDPOINT global.azure-devices-provisioning.net
    IOTHUB_DEVICE_DPS_ID_SCOPE Det ID-omfångsvärde som du antecknade tidigare.
    IOTHUB_DEVICE_DPS_DEVICE_ID sample-device-01
    IOTHUB_DEVICE_DPS_DEVICE_KEY Det genererade enhetsnyckelvärdet som du antecknade tidigare.

Nu kan du köra och felsöka exemplet i Visual Studio.

Följande utdata visar enheten som registrerar och ansluter till IoT Central. Exemplet börjar skicka telemetri:

[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.

Som operatör i ditt Azure IoT Central-program kan du:

  • Visa telemetrin som skickas av de två termostatkomponenterna på översiktssidan:

    Screenshot that shows the device overview page.

  • Visa enhetsegenskaperna på sidan Om . Den här sidan visar egenskaperna från enhetens informationskomponent och de två termostatkomponenterna:

    Screenshot that shows the device properties view.

Anpassa enhetsmallen

Som lösningsutvecklare kan du anpassa enhetsmallen som IoT Central skapade automatiskt när temperaturstyrenheten anslöt.

Så här lägger du till en molnegenskap för att lagra det kundnamn som är associerat med enheten:

  1. I ditt IoT Central-program går du till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. I modellen Temperaturkontrollant väljer du +Lägg till kapacitet.

  3. Ange Kundnamn som Visningsnamn, välj Molnegenskap som kapacitetstyp, expandera posten och välj Sträng som schema. Välj sedan Spara.

Så här anpassar du hur kommandona Hämta Max-Min-rapport visas i ditt IoT Central-program:

  1. Gå till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. För getMaxMinReport (termostat1)ersätter du Hämta Max-Min-rapport. med Hämta termostat1-statusrapport.

  3. För getMaxMinReport (termostat2)ersätter du Hämta Max-Min-rapport. med Hämta termostat2-statusrapport.

  4. Välj Spara.

Så här anpassar du hur skrivbara egenskaper för måltemperatur visas i ditt IoT Central-program:

  1. Gå till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. För targetTemperature (termostat1)ersätter du Måltemperatur med Måltemperatur (1).

  3. För targetTemperature (termostat2)ersätter du Måltemperatur med Måltemperatur (2).

  4. Välj Spara.

Termostatkomponenterna i temperaturkontrollantmodellen innehåller egenskapen Måltemperatur skrivbar, enhetsmallen innehåller molnegenskapen Kundnamn . Skapa en vy som en operator kan använda för att redigera dessa egenskaper:

  1. Välj Vyer och välj sedan panelen Redigera enhet och molndata .

  2. Ange Egenskaper som formulärnamn.

  3. Välj egenskaperna Måltemperatur (1), Måltemperatur (2) och Kundnamn. Välj sedan Lägg till avsnitt.

  4. Spara dina ändringar.

Screenshot that shows a view for updating property values.

Publicera enhetsmallen

Innan en operatör kan se och använda de anpassningar som du har gjort måste du publicera enhetsmallen.

I mallen Termostatenhet väljer du Publicera. I mallen Publicera den här enheten till programpanelen väljer du Publicera.

En operator kan nu använda egenskapsvyn för att uppdatera egenskapsvärdena och anropa kommandon som kallas Hämta termostat1-statusrapport och Hämta termostat2-statusrapport på enhetens kommandosida:

  • Uppdatera skrivbara egenskapsvärden på sidan Egenskaper :

    Screenshot that shows updating the device properties.

  • Anropa kommandona från sidan Kommandon . Om du kör kommandot statusrapport väljer du ett datum och en tid för parametern Sedan innan du kör den:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Du kan se hur enheten svarar på kommandon och egenskapsuppdateringar:

[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

Förutsättningar

För att slutföra stegen i den här artikeln behöver du följande resurser:

Granska koden

I kopian av Microsoft Azure IoT SDK för Java som du laddade ned tidigare öppnar du filen 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 i en textredigerare.

Exemplet implementerar modellen för digital tvillingdefinitionsspråk för temperaturkontrollant med flera komponenter.

När du kör exemplet för att ansluta till IoT Central använder det Device Provisioning Service (DPS) för att registrera enheten och generera en anslutningssträng. Exemplet hämtar den DPS-anslutningsinformation som behövs från kommandoradsmiljön.

Metoden main :

  • Anrop initializeAndProvisionDevice för att ange dtmi:com:example:TemperatureController;2 modell-ID, använda DPS för att etablera och registrera enheten, skapa en DeviceClient-instans och ansluta till ditt IoT Central-program. IoT Central använder modell-ID:t för att identifiera eller generera enhetsmallen för den här enheten. Mer information finns i Tilldela en enhet till en enhetsmall.
  • Skapar kommandohanterare för kommandona getMaxMinReport och reboot .
  • Skapar egenskapsuppdateringshanterare för skrivbara targetTemperature egenskaper.
  • Skickar initiala värden för egenskaperna i enhetsinformationsgränssnittet och egenskaperna Enhetsminne och Serienummer.
  • Startar en tråd för att skicka temperaturtelemetri från de två termostaterna och uppdatera maxTempSinceLastReboot egenskapen var femte sekund.
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();
}

Metoden initializeAndProvisionDevice visar hur enheten använder DPS för att registrera och ansluta till IoT Central. Nyttolasten innehåller det modell-ID som IoT Central använder för att tilldela en enhet till en enhetsmall:

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

Metoden sendTemperatureTelemetry visar hur enheten skickar temperaturtelemetrin från en komponent till IoT Central. Den här metoden använder PnpConvention klassen för att skapa meddelandet:

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

Metoden updateMaxTemperatureSinceLastReboot skickar en maxTempSinceLastReboot egenskapsuppdatering från en komponent till IoT Central. Den här metoden använder PnpConvention klassen för att skapa korrigeringen:

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

Klassen TargetTemperatureUpdateCallback innehåller metoden onPropertyChanged för att hantera skrivbara egenskapsuppdateringar till en komponent från IoT Central. Den här metoden använder PnpConvention klassen för att skapa svaret:

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

Klassen MethodCallback innehåller metoden onMethodInvoked för att hantera komponentkommandon som anropas från 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);
        }
    }
}

Hämta anslutningsinformation

När du kör exempelprogrammet för enheter senare i den här självstudien behöver du följande konfigurationsvärden:

  • ID-omfång: I ditt IoT Central-program går du till Behörigheter Enhetsanslutningsgrupper>. Anteckna ID-omfångsvärdet.
  • Grupp primärnyckel: I ditt IoT Central-program går du till Behörigheter > Enhetsanslutningsgrupper > SAS-IoT-Enheter. Anteckna värdet för den primära nyckeln för delad åtkomstsignatur.

Använd Azure Cloud Shell för att generera en enhetsnyckel från den grupppriorativnyckel som du hämtade:

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

Anteckna den genererade enhetsnyckeln. Du använder den senare i den här självstudien.

Kommentar

Om du vill köra det här exemplet behöver du inte registrera enheten i förväg i ditt IoT Central-program. Exemplet använder funktionen IoT Central för att automatiskt registrera enheter när de ansluter för första gången.

I Windows navigerar du till rotmappen för Azure IoT SDK för Java-lagringsplatsen som du laddade ned.

Kör följande kommando för att skapa exempelprogrammet:

mvn install -T 2C -DskipTests

Kör koden

Om du vill köra exempelprogrammet öppnar du en kommandoradsmiljö och navigerar till mappen azure-iot-sdk-java/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample som innehåller mappen src med TemperatureController.java exempelfilen.

Ange miljövariablerna för att konfigurera exemplet. Följande kodfragment visar hur du ställer in miljövariablerna i Windows-kommandotolken. Om du använder ett bash-gränssnitt ersätter set du kommandona med export kommandon:

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

Kör exemplet:

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

Följande utdata visar enheten som registrerar och ansluter till IoT Central. Exemplet börjar skicka telemetri:

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.

Som operatör i ditt Azure IoT Central-program kan du:

  • Visa telemetrin som skickas av de två termostatkomponenterna på översiktssidan:

    Screenshot that shows the device overview page.

  • Visa enhetsegenskaperna på sidan Om . Den här sidan visar egenskaperna från enhetens informationskomponent och de två termostatkomponenterna:

    Screenshot that shows the device properties view.

Anpassa enhetsmallen

Som lösningsutvecklare kan du anpassa enhetsmallen som IoT Central skapade automatiskt när temperaturstyrenheten anslöt.

Så här lägger du till en molnegenskap för att lagra det kundnamn som är associerat med enheten:

  1. I ditt IoT Central-program går du till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. I modellen Temperaturkontrollant väljer du +Lägg till kapacitet.

  3. Ange Kundnamn som Visningsnamn, välj Molnegenskap som kapacitetstyp, expandera posten och välj Sträng som schema. Välj sedan Spara.

Så här anpassar du hur kommandona Hämta Max-Min-rapport visas i ditt IoT Central-program:

  1. Gå till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. För getMaxMinReport (termostat1)ersätter du Hämta Max-Min-rapport. med Hämta termostat1-statusrapport.

  3. För getMaxMinReport (termostat2)ersätter du Hämta Max-Min-rapport. med Hämta termostat2-statusrapport.

  4. Välj Spara.

Så här anpassar du hur skrivbara egenskaper för måltemperatur visas i ditt IoT Central-program:

  1. Gå till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. För targetTemperature (termostat1)ersätter du Måltemperatur med Måltemperatur (1).

  3. För targetTemperature (termostat2)ersätter du Måltemperatur med Måltemperatur (2).

  4. Välj Spara.

Termostatkomponenterna i temperaturkontrollantmodellen innehåller egenskapen Måltemperatur skrivbar, enhetsmallen innehåller molnegenskapen Kundnamn . Skapa en vy som en operator kan använda för att redigera dessa egenskaper:

  1. Välj Vyer och välj sedan panelen Redigera enhet och molndata .

  2. Ange Egenskaper som formulärnamn.

  3. Välj egenskaperna Måltemperatur (1), Måltemperatur (2) och Kundnamn. Välj sedan Lägg till avsnitt.

  4. Spara dina ändringar.

Screenshot that shows a view for updating property values.

Publicera enhetsmallen

Innan en operatör kan se och använda de anpassningar som du har gjort måste du publicera enhetsmallen.

I mallen Termostatenhet väljer du Publicera. I mallen Publicera den här enheten till programpanelen väljer du Publicera.

En operator kan nu använda egenskapsvyn för att uppdatera egenskapsvärdena och anropa kommandon som kallas Hämta termostat1-statusrapport och Hämta termostat2-statusrapport på enhetens kommandosida:

  • Uppdatera skrivbara egenskapsvärden på sidan Egenskaper :

    Screenshot that shows updating the device properties.

  • Anropa kommandona från sidan Kommandon . Om du kör kommandot statusrapport väljer du ett datum och en tid för parametern Sedan innan du kör den:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Du kan se hur enheten svarar på kommandon och egenskapsuppdateringar:

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

Förutsättningar

För att slutföra stegen i den här artikeln behöver du följande resurser:

  • En utvecklingsdator med Node.js version 6 eller senare installerad. Du kan köra node --version på kommandoraden för att kontrollera din version. Anvisningarna i den här självstudien förutsätter att du kör nodkommandot i Windows-kommandotolken. Du kan dock använda Node.js på många andra operativsystem.

  • En lokal kopia av Microsoft Azure IoT SDK för Node.js GitHub-lagringsplats som innehåller exempelkoden. Använd den här länken om du vill ladda ned en kopia av lagringsplatsen: Ladda ned ZIP. Packa sedan upp filen på en lämplig plats på den lokala datorn.

Granska koden

I kopian av Microsoft Azure IoT SDK för Node.js du laddade ned tidigare öppnar du filen azure-iot-sdk-node/device/samples/javascript/pnp_temperature_controller.js i en textredigerare.

Exemplet implementerar modellen för digital tvillingdefinitionsspråk för temperaturkontrollant med flera komponenter.

När du kör exemplet för att ansluta till IoT Central använder det Device Provisioning Service (DPS) för att registrera enheten och generera en anslutningssträng. Exemplet hämtar den DPS-anslutningsinformation som behövs från kommandoradsmiljön.

Metoden main :

  • Skapar ett client objekt och anger modell-ID dtmi:com:example:TemperatureController;2 :t innan anslutningen öppnas. IoT Central använder modell-ID:t för att identifiera eller generera enhetsmallen för den här enheten. Mer information finns i Tilldela en enhet till en enhetsmall.
  • Skapar kommandohanterare för tre kommandon.
  • Startar en slinga för varje termostatkomponent för att skicka temperaturtelemetri var 5:e sekund.
  • Startar en loop för standardkomponenten för att skicka telemetri för arbetsuppsättningsstorlek var 6:e sekund.
  • Skickar egenskapen maxTempSinceLastReboot för varje termostatkomponent.
  • Skickar egenskaperna för enhetsinformation.
  • Skapar skrivbara egenskapshanterare för de tre komponenterna.
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());
  }
}

Funktionen provisionDevice visar hur enheten använder DPS för att registrera och ansluta till IoT Central. Nyttolasten innehåller det modell-ID som IoT Central använder för att tilldela en enhet till en enhetsmall:

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

Funktionen sendTelemetry visar hur enheten skickar temperaturtelemetrin till IoT Central. För telemetri från komponenter lägger den till en egenskap som heter $.sub med komponentnamnet:

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

Metoden main använder en hjälpmetod som kallas helperCreateReportedPropertiesPatch för att skapa egenskapsuppdateringsmeddelanden. Den här metoden använder en valfri parameter för att ange komponenten som skickar egenskapen:

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

Metoden main använder följande metod för att hantera uppdateringar av skrivbara egenskaper från IoT Central. Observera hur metoden skapar svaret med versions- och statuskoden:

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

Metoden main använder följande metoder för att hantera kommandon från 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());
  }
};

Hämta anslutningsinformation

När du kör exempelprogrammet för enheter senare i den här självstudien behöver du följande konfigurationsvärden:

  • ID-omfång: I ditt IoT Central-program går du till Behörigheter Enhetsanslutningsgrupper>. Anteckna ID-omfångsvärdet.
  • Grupp primärnyckel: I ditt IoT Central-program går du till Behörigheter > Enhetsanslutningsgrupper > SAS-IoT-Enheter. Anteckna värdet för den primära nyckeln för delad åtkomstsignatur.

Använd Azure Cloud Shell för att generera en enhetsnyckel från den grupppriorativnyckel som du hämtade:

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

Anteckna den genererade enhetsnyckeln. Du använder den senare i den här självstudien.

Kommentar

Om du vill köra det här exemplet behöver du inte registrera enheten i förväg i ditt IoT Central-program. Exemplet använder funktionen IoT Central för att automatiskt registrera enheter när de ansluter för första gången.

Kör koden

Om du vill köra exempelprogrammet öppnar du en kommandoradsmiljö och navigerar till mappen azure-iot-sdk-node/device/samples/javascript som innehåller den pnp_temperature_controller.js exempelfilen.

Ange miljövariablerna för att konfigurera exemplet. Följande kodfragment visar hur du ställer in miljövariablerna i Windows-kommandotolken. Om du använder ett bash-gränssnitt ersätter set du kommandona med export kommandon:

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

Installera de paket som krävs:

npm install

Kör exemplet:

node pnp_temperature_controller.js

Följande utdata visar enheten som registrerar och ansluter till IoT Central. Exemplet skickar maxTempSinceLastReboot sedan egenskapen från de två termostatkomponenterna innan den börjar skicka telemetri:

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

Som operatör i ditt Azure IoT Central-program kan du:

  • Visa telemetrin som skickas av de två termostatkomponenterna på översiktssidan:

    Screenshot that shows the device overview page.

  • Visa enhetsegenskaperna på sidan Om . Den här sidan visar egenskaperna från enhetens informationskomponent och de två termostatkomponenterna:

    Screenshot that shows the device properties view.

Anpassa enhetsmallen

Som lösningsutvecklare kan du anpassa enhetsmallen som IoT Central skapade automatiskt när temperaturstyrenheten anslöt.

Så här lägger du till en molnegenskap för att lagra det kundnamn som är associerat med enheten:

  1. I ditt IoT Central-program går du till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. I modellen Temperaturkontrollant väljer du +Lägg till kapacitet.

  3. Ange Kundnamn som Visningsnamn, välj Molnegenskap som kapacitetstyp, expandera posten och välj Sträng som schema. Välj sedan Spara.

Så här anpassar du hur kommandona Hämta Max-Min-rapport visas i ditt IoT Central-program:

  1. Gå till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. För getMaxMinReport (termostat1)ersätter du Hämta Max-Min-rapport. med Hämta termostat1-statusrapport.

  3. För getMaxMinReport (termostat2)ersätter du Hämta Max-Min-rapport. med Hämta termostat2-statusrapport.

  4. Välj Spara.

Så här anpassar du hur skrivbara egenskaper för måltemperatur visas i ditt IoT Central-program:

  1. Gå till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. För targetTemperature (termostat1)ersätter du Måltemperatur med Måltemperatur (1).

  3. För targetTemperature (termostat2)ersätter du Måltemperatur med Måltemperatur (2).

  4. Välj Spara.

Termostatkomponenterna i temperaturkontrollantmodellen innehåller egenskapen Måltemperatur skrivbar, enhetsmallen innehåller molnegenskapen Kundnamn . Skapa en vy som en operator kan använda för att redigera dessa egenskaper:

  1. Välj Vyer och välj sedan panelen Redigera enhet och molndata .

  2. Ange Egenskaper som formulärnamn.

  3. Välj egenskaperna Måltemperatur (1), Måltemperatur (2) och Kundnamn. Välj sedan Lägg till avsnitt.

  4. Spara dina ändringar.

Screenshot that shows a view for updating property values.

Publicera enhetsmallen

Innan en operatör kan se och använda de anpassningar som du har gjort måste du publicera enhetsmallen.

I mallen Termostatenhet väljer du Publicera. I mallen Publicera den här enheten till programpanelen väljer du Publicera.

En operator kan nu använda egenskapsvyn för att uppdatera egenskapsvärdena och anropa kommandon som kallas Hämta termostat1-statusrapport och Hämta termostat2-statusrapport på enhetens kommandosida:

  • Uppdatera skrivbara egenskapsvärden på sidan Egenskaper :

    Screenshot that shows updating the device properties.

  • Anropa kommandona från sidan Kommandon . Om du kör kommandot statusrapport väljer du ett datum och en tid för parametern Sedan innan du kör den:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Du kan se hur enheten svarar på kommandon och egenskapsuppdateringar. Kommandot getMaxMinReport finns i komponenten thermostat2reboot , kommandot finns i standardkomponenten. Den targetTemperature skrivbara egenskapen har angetts för komponenten 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

Förutsättningar

För att slutföra stegen i den här artikeln behöver du följande resurser:

  • En utvecklingsdator med Python installerad. Kontrollera Azure IoT Python SDK för aktuella versionskrav för Python. Du kan köra python --version på kommandoraden för att kontrollera din version. Python är tillgängligt för en mängd olika operativsystem. Anvisningarna i den här självstudien förutsätter att du kör python-kommandot i Windows-kommandotolken.

  • En lokal kopia av Microsoft Azure IoT SDK för Python GitHub-lagringsplatsen som innehåller exempelkoden. Använd den här länken om du vill ladda ned en kopia av lagringsplatsen: Ladda ned ZIP. Packa sedan upp filen på en lämplig plats på den lokala datorn.

Granska koden

I kopian av Microsoft Azure IoT SDK för Python som du laddade ned tidigare öppnar du filen azure-iot-sdk-python/samples/pnp/temp_controller_with_thermostats.py i en textredigerare.

Exemplet implementerar modellen för digital tvillingdefinitionsspråk för temperaturkontrollant med flera komponenter.

När du kör exemplet för att ansluta till IoT Central använder det Device Provisioning Service (DPS) för att registrera enheten och generera en anslutningssträng. Exemplet hämtar den DPS-anslutningsinformation som behövs från kommandoradsmiljön.

Funktionen main :

  • Använder DPS för att etablera enheten. Etableringsinformationen innehåller modell-ID:t. IoT Central använder modell-ID:t för att identifiera eller generera enhetsmallen för den här enheten. Mer information finns i Tilldela en enhet till en enhetsmall.
  • Skapar ett Device_client objekt och anger modell-ID dtmi:com:example:TemperatureController;2 :t innan anslutningen öppnas.
  • Skickar initiala egenskapsvärden till IoT Central. Den använder pnp_helper för att skapa korrigeringarna.
  • Skapar lyssnare för kommandona getMaxMinReport och reboot . Varje termostatkomponent har ett eget getMaxMinReport kommando.
  • Skapar egenskapslyssnare för att lyssna efter skrivbara egenskapsuppdateringar.
  • Startar en loop för att skicka temperaturtelemetri från de två termostatkomponenterna och arbetsuppsättningstelemetri från standardkomponenten var 8:e sekund.
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())

    # ...

Funktionen provision_device använder DPS för att etablera enheten och registrera den med IoT Central. Funktionen innehåller enhetsmodell-ID som IoT Central använder för att tilldela en enhet till en enhetsmall i etableringsnyttolasten:

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

Funktionen execute_command_listener hanterar kommandobegäranden, kör max_min_handler funktionen när enheten tar emot getMaxMinReport kommandot för termostatkomponenterna och reboot_handler funktionen när enheten tar emot reboot kommandot. Den använder modulen pnp_helper för att skapa svaret:

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

Hanterar async def execute_property_listener skrivbara egenskapsuppdateringar, till exempel targetTemperature för termostatkomponenterna och genererar JSON-svaret. Den använder modulen pnp_helper för att skapa svaret:

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)

Funktionen send_telemetry_from_temp_controller skickar telemetrimeddelandena från termostatkomponenterna till IoT Central. Den använder modulen pnp_helper för att skapa meddelandena:

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)

Hämta anslutningsinformation

När du kör exempelprogrammet för enheter senare i den här självstudien behöver du följande konfigurationsvärden:

  • ID-omfång: I ditt IoT Central-program går du till Behörigheter Enhetsanslutningsgrupper>. Anteckna ID-omfångsvärdet.
  • Grupp primärnyckel: I ditt IoT Central-program går du till Behörigheter > Enhetsanslutningsgrupper > SAS-IoT-Enheter. Anteckna värdet för den primära nyckeln för delad åtkomstsignatur.

Använd Azure Cloud Shell för att generera en enhetsnyckel från den grupppriorativnyckel som du hämtade:

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

Anteckna den genererade enhetsnyckeln. Du använder den senare i den här självstudien.

Kommentar

Om du vill köra det här exemplet behöver du inte registrera enheten i förväg i ditt IoT Central-program. Exemplet använder funktionen IoT Central för att automatiskt registrera enheter när de ansluter för första gången.

Kör koden

Om du vill köra exempelprogrammet öppnar du en kommandoradsmiljö och navigerar till mappen azure-iot-sdk-python-2/samples/pnp som innehåller temp_controller_with_thermostats.py exempelfilen.

Ange miljövariablerna för att konfigurera exemplet. Följande kodfragment visar hur du ställer in miljövariablerna i Windows-kommandotolken. Om du använder ett bash-gränssnitt ersätter set du kommandona med export kommandon:

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

Installera de paket som krävs:

pip install azure-iot-device

Kör exemplet:

python temp_controller_with_thermostats.py

Följande utdata visar enheten som registrerar och ansluter till IoT Central. Exemplet skickar maxTempSinceLastReboot egenskaperna från de två termostatkomponenterna innan det börjar skicka telemetri:

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}

Som operatör i ditt Azure IoT Central-program kan du:

  • Visa telemetrin som skickas av de två termostatkomponenterna på översiktssidan:

    Screenshot that shows the device overview page.

  • Visa enhetsegenskaperna på sidan Om . Den här sidan visar egenskaperna från enhetens informationskomponent och de två termostatkomponenterna:

    Screenshot that shows the device properties view.

Anpassa enhetsmallen

Som lösningsutvecklare kan du anpassa enhetsmallen som IoT Central skapade automatiskt när temperaturstyrenheten anslöt.

Så här lägger du till en molnegenskap för att lagra det kundnamn som är associerat med enheten:

  1. I ditt IoT Central-program går du till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. I modellen Temperaturkontrollant väljer du +Lägg till kapacitet.

  3. Ange Kundnamn som Visningsnamn, välj Molnegenskap som kapacitetstyp, expandera posten och välj Sträng som schema. Välj sedan Spara.

Så här anpassar du hur kommandona Hämta Max-Min-rapport visas i ditt IoT Central-program:

  1. Gå till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. För getMaxMinReport (termostat1)ersätter du Hämta Max-Min-rapport. med Hämta termostat1-statusrapport.

  3. För getMaxMinReport (termostat2)ersätter du Hämta Max-Min-rapport. med Hämta termostat2-statusrapport.

  4. Välj Spara.

Så här anpassar du hur skrivbara egenskaper för måltemperatur visas i ditt IoT Central-program:

  1. Gå till enhetsmallen Temperaturkontrollantsidan Enhetsmallar .

  2. För targetTemperature (termostat1)ersätter du Måltemperatur med Måltemperatur (1).

  3. För targetTemperature (termostat2)ersätter du Måltemperatur med Måltemperatur (2).

  4. Välj Spara.

Termostatkomponenterna i temperaturkontrollantmodellen innehåller egenskapen Måltemperatur skrivbar, enhetsmallen innehåller molnegenskapen Kundnamn . Skapa en vy som en operator kan använda för att redigera dessa egenskaper:

  1. Välj Vyer och välj sedan panelen Redigera enhet och molndata .

  2. Ange Egenskaper som formulärnamn.

  3. Välj egenskaperna Måltemperatur (1), Måltemperatur (2) och Kundnamn. Välj sedan Lägg till avsnitt.

  4. Spara dina ändringar.

Screenshot that shows a view for updating property values.

Publicera enhetsmallen

Innan en operatör kan se och använda de anpassningar som du har gjort måste du publicera enhetsmallen.

I mallen Termostatenhet väljer du Publicera. I mallen Publicera den här enheten till programpanelen väljer du Publicera.

En operator kan nu använda egenskapsvyn för att uppdatera egenskapsvärdena och anropa kommandon som kallas Hämta termostat1-statusrapport och Hämta termostat2-statusrapport på enhetens kommandosida:

  • Uppdatera skrivbara egenskapsvärden på sidan Egenskaper :

    Screenshot that shows updating the device properties.

  • Anropa kommandona från sidan Kommandon . Om du kör kommandot statusrapport väljer du ett datum och en tid för parametern Sedan innan du kör den:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Du kan se hur enheten svarar på kommandon och egenskapsuppdateringar:

{'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"}

Visa rådata

Du kan använda vyn Rådata för att undersöka de rådata som enheten skickar till IoT Central:

Screenshot that shows the raw data view.

I den här vyn kan du välja vilka kolumner som ska visas och ange ett tidsintervall som ska visas. Kolumnen Odefinierade data visar enhetsdata som inte matchar några egenskaps- eller telemetridefinitioner i enhetsmallen.

Rensa resurser

Om du inte planerar att slutföra ytterligare snabbstarter eller självstudier för IoT Central kan du ta bort ditt IoT Central-program:

  1. I ditt IoT Central-program går du till Programhantering>.
  2. Välj Ta bort och bekräfta sedan åtgärden.

Nästa steg

Om du föredrar att fortsätta med IoT Central-självstudierna och lära dig mer om att skapa en IoT Central-lösning kan du läsa: