fix(samples): adjust samples to work better add readme.md (#883)
* fix(samples): adjust samples to work better add readme.md * fix(pnp samples): adjust sample to ensure specific security types In addition make sure if it's connectionString that there is something in the connection string and at a minimum it has a HostName and DeviceId * style(plug and play samples): change pnp to Plug and Play
This commit is contained in:
Родитель
50c96ec41c
Коммит
fdcf47cc33
|
@ -4,25 +4,26 @@
|
|||
'use strict';
|
||||
|
||||
const Protocol = require('azure-iot-device-mqtt').Mqtt;
|
||||
var ProvProtocol = require('azure-iot-provisioning-device-mqtt').Mqtt;
|
||||
const ProvProtocol = require('azure-iot-provisioning-device-mqtt').Mqtt;
|
||||
|
||||
const Client = require('azure-iot-device').Client;
|
||||
const Message = require('azure-iot-device').Message;
|
||||
const ConnectionString = require('azure-iot-common').ConnectionString;
|
||||
const SymmetricKeySecurityClient = require('azure-iot-security-symmetric-key').SymmetricKeySecurityClient;
|
||||
const ProvisioningDeviceClient = require('azure-iot-provisioning-device').ProvisioningDeviceClient;
|
||||
|
||||
// String containing Hostname, Device Id & Device Key in the following formats:
|
||||
// 'HostName=<iothub_host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>'
|
||||
var deviceConnectionString = process.env.IOTHUB_DEVICE_CONNECTION_STRING;
|
||||
let deviceConnectionString = process.env.IOTHUB_DEVICE_CONNECTION_STRING;
|
||||
|
||||
// DPS connection information
|
||||
var provisioningHost = process.env.IOTHUB_DEVICE_DPS_ENDPOINT;
|
||||
var idScope = process.env.IOTHUB_DEVICE_DPS_ID_SCOPE;
|
||||
var registrationId = process.env.IOTHUB_DEVICE_DPS_DEVICE_ID;
|
||||
var symmetricKey = process.env.IOTHUB_DEVICE_DPS_DEVICE_KEY;
|
||||
var useDps = process.env.IOTHUB_DEVICE_SECURITY_TYPE;
|
||||
const provisioningHost = process.env.IOTHUB_DEVICE_DPS_ENDPOINT ||'global.azure-devices-provisioning.net';
|
||||
const idScope = process.env.IOTHUB_DEVICE_DPS_ID_SCOPE;
|
||||
const registrationId = process.env.IOTHUB_DEVICE_DPS_DEVICE_ID;
|
||||
const symmetricKey = process.env.IOTHUB_DEVICE_DPS_DEVICE_KEY;
|
||||
const useDps = process.env.IOTHUB_DEVICE_SECURITY_TYPE;
|
||||
|
||||
const modelId = 'dtmi:com:example:TemperatureController;1';
|
||||
const modelIdObject = { modelId: 'dtmi:com:example:TemperatureController;1' };
|
||||
const messageSubjectProperty = '$.sub';
|
||||
const thermostat1ComponentName = 'thermostat1';
|
||||
const thermostat2ComponentName = 'thermostat2';
|
||||
|
@ -32,6 +33,47 @@ let intervalToken1;
|
|||
let intervalToken2;
|
||||
let intervalToken3;
|
||||
|
||||
class TemperatureSensor {
|
||||
constructor() {
|
||||
this.currTemp = 1 + (Math.random() * 90);
|
||||
this.maxTemp = this.currTemp;
|
||||
this.minTemp = this.currTemp;
|
||||
this.cumulativeTemperature = this.currTemp;
|
||||
this.startTime = (new Date(Date.now())).toISOString();
|
||||
this.numberOfTemperatureReadings = 1;
|
||||
}
|
||||
getCurrentTemperatureObject() {
|
||||
return { temperature: this.currTemp };
|
||||
}
|
||||
updateSensor() {
|
||||
this.currTemp = 1 + (Math.random() * 90);
|
||||
this.cumulativeTemperature += this.currTemp;
|
||||
this.numberOfTemperatureReadings++;
|
||||
if (this.currTemp > this.maxTemp) {
|
||||
this.maxTemp = this.currTemp;
|
||||
}
|
||||
if (this.currTemp < this.minTemp) {
|
||||
this.minTemp = this.currTemp;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
getMaxMinReportObject() {
|
||||
return {
|
||||
maxTemp: this.maxTemp,
|
||||
minTemp: this.minTemp,
|
||||
avgTemp: this.cumulativeTemperature / this.numberOfTemperatureReadings,
|
||||
endTime: (new Date(Date.now())).toISOString(),
|
||||
startTime: this.startTime
|
||||
};
|
||||
}
|
||||
getMaxTemperatureValue() {
|
||||
return this.maxTemp;
|
||||
}
|
||||
}
|
||||
|
||||
const thermostat1 = new TemperatureSensor();
|
||||
const thermostat2 = new TemperatureSensor();
|
||||
|
||||
const commandNameGetMaxMinReport1 = thermostat1ComponentName + commandComponentCommandNameSeparator + 'getMaxMinReport';
|
||||
const commandNameGetMaxMinReport2 = thermostat2ComponentName + commandComponentCommandNameSeparator + 'getMaxMinReport';
|
||||
const commandNameReboot = 'reboot';
|
||||
|
@ -41,11 +83,11 @@ const commandHandler = async (request, response) => {
|
|||
helperLogCommandRequest(request);
|
||||
switch (request.methodName) {
|
||||
case commandNameGetMaxMinReport1: {
|
||||
await sendCommandResponse(request, response, 200, 'max min report from thermostat 1');
|
||||
await sendCommandResponse(request, response, 200, thermostat1.getMaxMinReportObject());
|
||||
break;
|
||||
}
|
||||
case commandNameGetMaxMinReport2: {
|
||||
await sendCommandResponse(request, response, 200, 'max min report from thermostat 2');
|
||||
await sendCommandResponse(request, response, 200, thermostat2.getMaxMinReportObject());
|
||||
break;
|
||||
}
|
||||
case commandNameReboot: {
|
||||
|
@ -68,7 +110,7 @@ const sendCommandResponse = async (request, response, status, payload) => {
|
|||
};
|
||||
|
||||
const helperLogCommandRequest = (request) => {
|
||||
console.log('Received command request for comand name: ' + request.methodName);
|
||||
console.log('Received command request for command name: ' + request.methodName);
|
||||
|
||||
if (!!(request.payload)) {
|
||||
console.log('The command request payload is:');
|
||||
|
@ -139,7 +181,7 @@ const desiredPropertyPatchListener = (deviceTwin, componentNames) => {
|
|||
patchForRoot[key] = propertyContent;
|
||||
updateComponentReportedProperties(deviceTwin, patchForRoot, null);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -168,13 +210,13 @@ async function sendTelemetry(deviceClient, data, index, componentName) {
|
|||
} else {
|
||||
console.log('Sending telemetry message %d from root interface', index);
|
||||
}
|
||||
const pnpMsg = new Message(data);
|
||||
const msg = new Message(data);
|
||||
if (!!(componentName)) {
|
||||
pnpMsg.properties.add(messageSubjectProperty, componentName);
|
||||
msg.properties.add(messageSubjectProperty, componentName);
|
||||
}
|
||||
pnpMsg.contentType = 'application/json';
|
||||
pnpMsg.contentEncoding = 'utf-8';
|
||||
await deviceClient.sendEvent(pnpMsg);
|
||||
msg.contentType = 'application/json';
|
||||
msg.contentEncoding = 'utf-8';
|
||||
await deviceClient.sendEvent(msg);
|
||||
}
|
||||
|
||||
async function provisionDevice(payload) {
|
||||
|
@ -199,8 +241,21 @@ async function provisionDevice(payload) {
|
|||
|
||||
async function main() {
|
||||
// If the user include a provision host then use DPS
|
||||
if (useDps === "DPS") {
|
||||
await provisionDevice();
|
||||
if (useDps === 'DPS') {
|
||||
await provisionDevice(modelIdObject);
|
||||
} else if (useDps === 'connectionString') {
|
||||
try {
|
||||
if (!(deviceConnectionString && ConnectionString.parse(deviceConnectionString,['HostName','DeviceId']))) {
|
||||
console.error('Connection string was not specified.');
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Invalid connection string specified.');
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.log('No proper SECURITY TYPE provided.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// fromConnectionString must specify a transport, coming from any transport package.
|
||||
|
@ -210,7 +265,7 @@ async function main() {
|
|||
|
||||
try {
|
||||
// Add the modelId here
|
||||
await client.setOptions({ modelId: modelId });
|
||||
await client.setOptions(modelIdObject);
|
||||
await client.open();
|
||||
console.log('Enabling the commands on the client');
|
||||
client.onDeviceMethod(commandNameGetMaxMinReport1, commandHandler);
|
||||
|
@ -222,13 +277,13 @@ async function main() {
|
|||
let index2 = 0;
|
||||
let index3 = 0;
|
||||
intervalToken1 = setInterval(() => {
|
||||
const data = JSON.stringify({ temperature: 1 + (Math.random() * 90) });
|
||||
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({ temperature: 1 + (Math.random() * 90) });
|
||||
const data = JSON.stringify(thermostat2.updateSensor().getCurrentTemperatureObject());
|
||||
sendTelemetry(client, data, index2, thermostat2ComponentName).catch((err) => console.log('error ', err.toString()));
|
||||
index2 += 1;
|
||||
}, 5500);
|
||||
|
@ -245,14 +300,14 @@ async function main() {
|
|||
|
||||
try {
|
||||
resultTwin = await client.getTwin();
|
||||
// Only report readable propertiess
|
||||
// Only report readable properties
|
||||
const patchRoot = helperCreateReportedPropertiesPatch({ serialNumber: serialNumber }, null);
|
||||
const patchThermostat1Info = helperCreateReportedPropertiesPatch({
|
||||
maxTempSinceLastReboot: 67.89,
|
||||
maxTempSinceLastReboot: thermostat1.getMaxTemperatureValue(),
|
||||
}, thermostat1ComponentName);
|
||||
|
||||
const patchThermostat2Info = helperCreateReportedPropertiesPatch({
|
||||
maxTempSinceLastReboot: 98.65,
|
||||
maxTempSinceLastReboot: thermostat2.getMaxTemperatureValue(),
|
||||
}, thermostat2ComponentName);
|
||||
|
||||
const patchDeviceInfo = helperCreateReportedPropertiesPatch({
|
||||
|
@ -276,7 +331,7 @@ async function main() {
|
|||
console.error('could not retrieve twin or report twin properties\n' + err.toString());
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('could not connect pnp client or could not attach interval function for telemetry\n' + err.toString());
|
||||
console.error('could not connect Plug and Play client or could not attach interval function for telemetry\n' + err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# Samples to demonstrate Azure IoT Plug and Play
|
||||
|
||||
The samples in this directory demonstrate how to implement an Azure IoT Plug and Play device. Azure IoT Plug and Play is documented [here](aka.ms/iotpnp). The samples assume basic familiarity with Plug and Play concepts, though not in depth knowledge of the Plug and Play "convention". The "convention" is a set of rules for serializing and de-serialing data that uses IoTHub primitives for transport which the samples themselves implement.
|
||||
|
||||
## Directory structure
|
||||
|
||||
The directory contains the following samples:
|
||||
|
||||
* [simple_thermostat](./simple_thermostat) A simple thermostat that implements the model [dtmi:com:example:Thermostat;1](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/samples/Thermostat.json). This sample is considered simple because it only implements one component, the thermostat itself. **You should begin with this sample.**
|
||||
|
||||
* [pnpTemperatureController](./pnpTemperatureController) A temperature controller that implements the model [dtmi:com:example:TemperatureController;1](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/samples/TemperatureController.json). This is considerably more complex than the [simple_thermostat](./simple_thermostat) and demonstrates the use of subcomponents. **You should move onto this sample only after fully understanding simple_thermostat.**
|
||||
|
||||
## Configuring the samples
|
||||
|
||||
Both samples use environment variables to retrieve configuration.
|
||||
|
||||
* If you are using a connection string to authenticate:
|
||||
* set IOTHUB_DEVICE_SECURITY_TYPE="connectionString"
|
||||
* set IOTHUB_DEVICE_CONNECTION_STRING="\<connection string of your device\>"
|
||||
|
||||
* If you are using a DPS enrollment group to authenticate:
|
||||
* set IOTHUB_DEVICE_SECURITY_TYPE="DPS"
|
||||
* set IOTHUB_DEVICE_DPS_ID_SCOPE="\<ID Scope of DPS instance\>"
|
||||
* set IOTHUB_DEVICE_DPS_DEVICE_ID="\<Device's ID\>"
|
||||
* set IOTHUB_DEVICE_DPS_DEVICE_KEY="\<Device's security key \>"
|
||||
* *OPTIONAL*, if you do not wish to use the default endpoint "global.azure-devices-provisioning.net"
|
||||
* set IOTHUB_DEVICE_DPS_ENDPOINT="\<DPS endpoint\>"
|
||||
|
||||
## Caveats
|
||||
|
||||
* Azure IoT Plug and Play is only supported for MQTT and MQTT over WebSockets for the Azure IoT Node Device SDK. Modifying these samples to use AMQP, AMQP over WebSockets, or HTTP protocols **will not work**.
|
||||
|
||||
* When the thermostat receives a desired temperature, it has no actual affect on the current temperature.
|
||||
|
||||
* The command `getMaxMinReport` allows the application to specify statistics of the temperature since a given date. To keep the sample simple, we ignore this field and instead return statistics from the entire lifecycle of the executable.
|
||||
|
||||
* The temperature controller implements a command named `reboot` which takes a request payload indicating the delay in seconds. The sample will ignore this command.
|
|
@ -4,10 +4,11 @@
|
|||
'use strict';
|
||||
|
||||
const Protocol = require('azure-iot-device-mqtt').Mqtt;
|
||||
var ProvProtocol = require('azure-iot-provisioning-device-mqtt').Mqtt;
|
||||
const ProvProtocol = require('azure-iot-provisioning-device-mqtt').Mqtt;
|
||||
|
||||
const Client = require('azure-iot-device').Client;
|
||||
const Message = require('azure-iot-device').Message;
|
||||
const ConnectionString = require('azure-iot-common').ConnectionString;
|
||||
const SymmetricKeySecurityClient = require('azure-iot-security-symmetric-key').SymmetricKeySecurityClient;
|
||||
const ProvisioningDeviceClient = require('azure-iot-provisioning-device').ProvisioningDeviceClient;
|
||||
|
||||
|
@ -15,24 +16,61 @@ const ProvisioningDeviceClient = require('azure-iot-provisioning-device').Provis
|
|||
|
||||
// String containing Hostname, Device Id & Device Key in the following formats:
|
||||
// 'HostName=<iothub_host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>'
|
||||
var deviceConnectionString = process.env.IOTHUB_DEVICE_CONNECTION_STRING;
|
||||
let deviceConnectionString = process.env.IOTHUB_DEVICE_CONNECTION_STRING;
|
||||
|
||||
// DPS connection information
|
||||
var provisioningHost = process.env.IOTHUB_DEVICE_DPS_ENDPOINT;
|
||||
var idScope = process.env.IOTHUB_DEVICE_DPS_ID_SCOPE;
|
||||
var registrationId = process.env.IOTHUB_DEVICE_DPS_DEVICE_ID;
|
||||
var symmetricKey = process.env.IOTHUB_DEVICE_DPS_DEVICE_KEY;
|
||||
var useDps = process.env.IOTHUB_DEVICE_SECURITY_TYPE;
|
||||
const provisioningHost = process.env.IOTHUB_DEVICE_DPS_ENDPOINT || 'global.azure-devices-provisioning.net';
|
||||
const idScope = process.env.IOTHUB_DEVICE_DPS_ID_SCOPE;
|
||||
const registrationId = process.env.IOTHUB_DEVICE_DPS_DEVICE_ID;
|
||||
const symmetricKey = process.env.IOTHUB_DEVICE_DPS_DEVICE_KEY;
|
||||
const useDps = process.env.IOTHUB_DEVICE_SECURITY_TYPE;
|
||||
|
||||
const modelId = 'dtmi:com:example:Thermostat;1';
|
||||
const modelIdObject = { modelId: 'dtmi:com:example:Thermostat;1' };
|
||||
const telemetrySendInterval = 10000;
|
||||
const deviceSerialNum = '123abc';
|
||||
|
||||
let intervalToken;
|
||||
let currTemp = 1 + (Math.random() * 90);
|
||||
const maxTemp = currTemp+10;
|
||||
class TemperatureSensor {
|
||||
constructor() {
|
||||
this.currTemp = 1 + (Math.random() * 90);
|
||||
this.maxTemp = this.currTemp;
|
||||
this.minTemp = this.currTemp;
|
||||
this.cumulativeTemperature = this.currTemp;
|
||||
this.startTime = (new Date(Date.now())).toISOString();
|
||||
this.numberOfTemperatureReadings = 1;
|
||||
}
|
||||
getCurrentTemperatureObject() {
|
||||
return { temperature: this.currTemp };
|
||||
}
|
||||
updateSensor() {
|
||||
this.currTemp = 1 + (Math.random() * 90);
|
||||
this.cumulativeTemperature += this.currTemp;
|
||||
this.numberOfTemperatureReadings++;
|
||||
if (this.currTemp > this.maxTemp) {
|
||||
this.maxTemp = this.currTemp;
|
||||
}
|
||||
if (this.currTemp < this.minTemp) {
|
||||
this.minTemp = this.currTemp;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
getMaxMinReportObject() {
|
||||
return {
|
||||
maxTemp: this.maxTemp,
|
||||
minTemp: this.minTemp,
|
||||
avgTemp: this.cumulativeTemperature / this.numberOfTemperatureReadings,
|
||||
endTime: (new Date(Date.now())).toISOString(),
|
||||
startTime: this.startTime
|
||||
};
|
||||
}
|
||||
getMaxTemperatureValue() {
|
||||
return this.maxTemp;
|
||||
}
|
||||
}
|
||||
|
||||
const commandMinMaxReport = 'getMaxMinReport';
|
||||
let intervalToken;
|
||||
|
||||
const deviceTemperatureSensor = new TemperatureSensor();
|
||||
const commandMaxMinReport = 'getMaxMinReport';
|
||||
|
||||
const propertyUpdateHandler = (deviceTwin, propertyName, reportedValue, desiredValue, version) => {
|
||||
console.log('Received an update for property: ' + propertyName + ' with value: ' + JSON.stringify(desiredValue));
|
||||
|
@ -51,9 +89,9 @@ const propertyUpdateHandler = (deviceTwin, propertyName, reportedValue, desiredV
|
|||
|
||||
const commandHandler = async (request, response) => {
|
||||
switch (request.methodName) {
|
||||
case commandMinMaxReport: {
|
||||
case commandMaxMinReport: {
|
||||
console.log('MaxMinReport ' + request.payload);
|
||||
await sendCommandResponse(request, response, 200, 'min/max response');
|
||||
await sendCommandResponse(request, response, 200, deviceTemperatureSensor.getMaxMinReportObject());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -120,12 +158,14 @@ const attachExitHandler = async (deviceClient) => {
|
|||
|
||||
async function sendTelemetry(deviceClient, index) {
|
||||
console.log('Sending telemetry message %d...', index);
|
||||
currTemp = 1 + (Math.random() * 90);
|
||||
const data = JSON.stringify({ temperature: currTemp });
|
||||
const pnpMsg = new Message(data);
|
||||
pnpMsg.contentType = 'application/json';
|
||||
pnpMsg.contentEncoding = 'utf-8';
|
||||
await deviceClient.sendEvent(pnpMsg);
|
||||
const msg = new Message(
|
||||
JSON.stringify(
|
||||
deviceTemperatureSensor.updateSensor().getCurrentTemperatureObject()
|
||||
)
|
||||
);
|
||||
msg.contentType = 'application/json';
|
||||
msg.contentEncoding = 'utf-8';
|
||||
await deviceClient.sendEvent(msg);
|
||||
}
|
||||
|
||||
async function provisionDevice(payload) {
|
||||
|
@ -150,8 +190,21 @@ async function provisionDevice(payload) {
|
|||
|
||||
async function main() {
|
||||
// If the user include a provision host then use DPS
|
||||
if (useDps === "DPS") {
|
||||
await provisionDevice();
|
||||
if (useDps === 'DPS') {
|
||||
await provisionDevice(modelIdObject);
|
||||
} else if (useDps === 'connectionString') {
|
||||
try {
|
||||
if (!(deviceConnectionString && ConnectionString.parse(deviceConnectionString,['HostName','DeviceId']))) {
|
||||
console.error('Connection string was not specified.');
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Invalid connection string specified.');
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.log('No proper SECURITY TYPE provided.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// fromConnectionString must specify a transport, coming from any transport package.
|
||||
|
@ -160,10 +213,10 @@ async function main() {
|
|||
let resultTwin;
|
||||
try {
|
||||
// Add the modelId here
|
||||
await client.setOptions({ modelId: modelId });
|
||||
await client.setOptions(modelIdObject);
|
||||
await client.open();
|
||||
console.log('Enabling the commands on the client');
|
||||
client.onDeviceMethod(commandMinMaxReport, commandHandler);
|
||||
client.onDeviceMethod(commandMaxMinReport, commandHandler);
|
||||
|
||||
// Send Telemetry every 10 secs
|
||||
let index = 0;
|
||||
|
@ -180,7 +233,7 @@ async function main() {
|
|||
resultTwin = await client.getTwin();
|
||||
const patchRoot = createReportPropPatch({ serialNumber: deviceSerialNum });
|
||||
const patchThermostat = createReportPropPatch({
|
||||
maxTempSinceLastReboot: maxTemp
|
||||
maxTempSinceLastReboot: deviceTemperatureSensor.getMaxTemperatureValue()
|
||||
});
|
||||
|
||||
// the below things can only happen once the twin is there
|
||||
|
@ -194,7 +247,7 @@ async function main() {
|
|||
console.error('could not retrieve twin or report twin properties\n' + err.toString());
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('could not connect pnp client or could not attach interval function for telemetry\n' + err.toString());
|
||||
console.error('could not connect Plug and Play client or could not attach interval function for telemetry\n' + err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче