Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates to include more TS samples (sas, and fwupdate) #1043

Merged
merged 11 commits into from
Oct 6, 2021
5 changes: 3 additions & 2 deletions device/samples/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,12 @@ node sample_sample_device.js
| Sample | Description | JavaScript | TypeScript |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | :--------: | :--------: |
| simple_sample_device | Connect to IoT Hub and send and receive messages. | ✔ | ✔ |
| simple_sample_device_with_sas | Connect using a SAS Token to IoT Hub and send and receive messages. | ✔ | |
| simple_sample_device_with_sas | Connect using a SAS Token to IoT Hub and send and receive messages. | ✔ | |
| simple_sample_device_x50 | Connect using an X-509 certificate to IoT Hub and send and receive messages. | ✔ | |
| send_batch_http | Connect to IoT Hub and send a batch of messages over an HTTP connection. | ✔ | ✔ |
| remote_monitoring | Implements the device code used to connect to an [Azure IoT Suite Remote Monitoring preconfigured solution][remote-monitoring-pcs]. | ✔ | ✔ |
| edge_downstream_device | Connect a downstream device to IoT Edge and send and receive messages. | ✔ | |
| device_through_proxy | Connect to IoT Hub and send and recieve messages through a proxy | ✔ | ✔ |

### **Device services samples (Device Twins, Methods, and Device Management)...**

Expand All @@ -141,7 +142,7 @@ node sample_sample_device.js
| simple_sample_device_twin | Shows how to synchronize a Device Twin with Azure IoT Hub on a device. | ✔ | ✔ |
| device_method | Shows how to implement an Azure IoT Hub Cloud to Device Direct Method on a device. | ✔ | ✔ |
| dmpatterns_reboot_device | Shows how a device handles a C2D method to reboot and provides progress updates through twin reported properties. See [device management patterns][dm-patterns] for instructions on running the device management patterns samples. | ✔ | |
| dmpatterns_fwupdate_device | Shows how a device handles a C2D method to initiate a firmware update and provides progress updates through twin reported properties. See [device management patterns][dm-patterns] for instructions on running the device management patterns samples. | ✔ | |
| dmpatterns_fwupdate_device | Shows how a device handles a C2D method to initiate a firmware update and provides progress updates through twin reported properties. See [device management patterns][dm-patterns] for instructions on running the device management patterns samples. | ✔ | |

### **Uploading blob to Azure...**

Expand Down
104 changes: 104 additions & 0 deletions device/samples/typescript/device_through_proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

// !IMPORTANT! This sample only pertains to HTTPS Proxy, via the HTTP Transport, MQTTWS Transport, and AMQPWS Transport.

// Uncomment one of these transports and then change it in fromConnectionString to test other transports
// import { Http as Protocol } from 'azure-iot-device-http';
// import { AmqpWs as Protocol } from 'azure-iot-device-amqp'

import { MqttWs as Protocol } from 'azure-iot-device-mqtt';
import { Client, Message } from 'azure-iot-device';

// Using HTTPS Proxy Agent
import * as url from 'url';
import { HttpsProxyAgent } from 'https-proxy-agent';
danhellem marked this conversation as resolved.
Show resolved Hide resolved

// String containing Hostname, Device Id & Device Key in the following formats:
// "HostName=<iothub_host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>"
const deviceConnectionString: string = process.env.DEVICE_CONNECTION_STRING || '';

if (deviceConnectionString === '') {
console.log('device connection string has not been set');
process.exit(-1);
}

let sendInterval: NodeJS.Timer;

function disconnectHandler (): void {
clearInterval(sendInterval);
client.removeAllListeners();
client.open().catch((err) => {
console.error(err.message);
});
}

// The AMQP and HTTP transports have the notion of completing, rejecting or abandoning the message.
// For example, this is only functional in AMQP and HTTP:
// client.complete(msg, printResultFor('completed'));
// If using MQTT calls to complete, reject, or abandon are no-ops.
// When completing a message, the service that sent the C2D message is notified that the message has been processed.
// When rejecting a message, the service that sent the C2D message is notified that the message won't be processed by the device. the method to use is client.reject(msg, callback).
// When abandoning the message, IoT Hub will immediately try to resend it. The method to use is client.abandon(msg, callback).
// MQTT is simpler: it accepts the message by default, and doesn't support rejecting or abandoning a message.
function messageHandler (msg: Message): void {
console.log('Id: ' + msg.messageId + ' Body: ' + msg.data);
client.complete(msg, printResultFor('completed'));
}

function generateMessage (): Message {
const windSpeed: number = 10 + (Math.random() * 4); // range: [10, 14]
const temperature: number = 20 + (Math.random() * 10); // range: [20, 30]
const humidity: number = 60 + (Math.random() * 20); // range: [60, 80]
const data: string = JSON.stringify({ deviceId: 'myFirstDevice', windSpeed: windSpeed, temperature: temperature, humidity: humidity });
const message: Message = new Message(data);
message.properties.add('temperatureAlert', (temperature > 28) ? 'true' : 'false');
return message;
}

function errorCallback (err: Error): void {
console.error(err.message);
}

function connectCallback (): void {
console.log('Client connected');
// Create a message and send it to the IoT Hub every two seconds
sendInterval = setInterval(() => {
const message = generateMessage();
console.log('Sending message: ' + message.getData());
client.sendEvent(message, printResultFor('send'));
}, 2000);

}

// Create Proxy Agent
// TODO: You need to change this to match the endpoint of your proxy server.
const proxy: string = 'http://localhost:8888/';
const options: url.UrlWithStringQuery = url.parse(proxy);
const agent: HttpsProxyAgent = new HttpsProxyAgent(options);

// fromConnectionString must specify a transport constructor, coming from any transport package.
let client: Client = Client.fromConnectionString(deviceConnectionString, Protocol);
// MQTTWS (MQTT over Websocket)
client.setOptions({mqtt: {webSocketAgent: agent}});
// AMQPWS (AMQP over Websocket)
// client.setOptions({amqp: {agent: agent}});
// HTTP
// client.setOptions({http: {webSocketAgent: agent}});
client.on('connect', connectCallback);
client.on('error', errorCallback);
client.on('disconnect', disconnectHandler);
client.on('message', messageHandler);

client.open()
.catch((err) => {
console.error('Could not connect: ' + err.message);
});

// Helper function to print results in the console
function printResultFor(op: any): (err: Error, res: any) => void {
return function printResult(err: Error, res: any): void {
if (err) console.log(op + ' error: ' + err.toString());
if (res) console.log(op + ' status: ' + res.constructor.name);
};
}
218 changes: 218 additions & 0 deletions device/samples/typescript/dmpatterns_fwupdate_device.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

import { Mqtt as Protocol } from 'azure-iot-device-mqtt';
import {
Client,
DeviceMethodRequest,
DeviceMethodResponse,
Twin,
} from 'azure-iot-device';
import * as url from 'url';
import * as async from 'async';

const deviceConnectionString: string =
process.env.DEVICE_CONNECTION_STRING || '';

if (deviceConnectionString === '') {
console.log('device connection string has not been set');
process.exit(-1);
}

const client: Client = Client.fromConnectionString(
deviceConnectionString,
Protocol
);

client.open(function (err: Error): void {
if (!err) {
client.onDeviceMethod(
'firmwareUpdate',
function (
request: DeviceMethodRequest,
response: DeviceMethodResponse
): void {
// Get the firmware image Uri from the body of the method request
const fwPackageUri: any = request.payload.fwPackageUri;
const fwPackageUriObj: url.UrlWithStringQuery = url.parse(fwPackageUri);

// Ensure that the url is to a secure url
if (fwPackageUriObj.protocol !== 'https:') {
response.send(
400,
'Invalid URL format. Must use https:// protocol.',
function (err: Error): void {
if (err)
console.error(
'Error sending method response :\n' + err.toString()
);
else
console.log(
'Response to method "' + request.methodName + '" sent successfully.'
);
}
);
} else {
// Respond the cloud app for the device method
response.send(
200,
'Firmware update started.',
function (err: Error): void {
if (err)
console.error(
'Error sending method response :\n' + err.toString()
);
else
console.log(
'Response to method "' + request.methodName + '" sent successfully.'
);
}
);

initiateFirmwareUpdateFlow(fwPackageUri, function (err: Error): void {
if (!err) console.log('Completed firmwareUpdate flow');
});
}
}
);
console.log(
'Client connected to IoT Hub. Waiting for firmwareUpdate device method.'
);
}
});

// Implementation of firmwareUpdate flow
function initiateFirmwareUpdateFlow(fwPackageUri: any, callback: any): void {
async.waterfall(
[
function (callback: any): void {
downloadImage(fwPackageUri, callback);
},
applyImage,
],
function (err: Error): void {
if (err) {
console.error('Error : ' + err.message);
}
callback(err);
}
);
}

// Function that implements the 'downloadImage' phase of the
// firmware update process.
function downloadImage(fwPackageUriVal: string, callback: any): void {
const imageResult: string = '[Fake firmware image data]';

async.waterfall(
[
function (callback: any): void {
reportFWUpdateThroughTwin(
{
status: 'downloading',
startedDownloadingTime: new Date().toISOString(),
},
callback
);
},
function (callback: any): void {
console.log('Downloading image from URI: ' + fwPackageUriVal);

// Replace this line with the code to download the image. Delay used to simulate the download.
setTimeout(function (): void {
callback(null);
}, 4000);
},
function (callback: any): void {
reportFWUpdateThroughTwin(
{
status: 'download complete',
downloadCompleteTime: new Date().toISOString(),
},
callback
);
},
],
function (err: Error): void {
if (err) {
reportFWUpdateThroughTwin(
{ status: 'Download image failed' },
function (err: Error): void {
callback(err);
}
);
} else {
callback(null, imageResult);
}
}
);
}

// Implementation for the apply phase, which reports status after
// completing the image apply.
function applyImage(_imageData: any, callback: any): void {
async.waterfall(
[
function (callback: any): void {
reportFWUpdateThroughTwin(
{
status: 'applying',
startedApplyingImage: new Date().toISOString(),
},
callback
);
},
function (callback: any): void {
console.log('Applying firmware image');

// Replace this line with the code to download the image. Delay used to simulate the download.
setTimeout(function (): void {
callback(null);
}, 4000);
},
function (callback: any): void {
reportFWUpdateThroughTwin(
{
status: 'apply firmware image complete',
lastFirmwareUpdate: new Date().toISOString(),
},
callback
);
},
],
function (err: Error): void {
if (err) {
reportFWUpdateThroughTwin(
{ status: 'Apply image failed' },
function (err: Error): void {
callback(err);
}
);
}
callback(null);
}
);
}

// Helper function to update the twin reported properties.
// Used by every phase of the firmware update.
function reportFWUpdateThroughTwin(
firmwareUpdateValue: any,
callback: any
): void {
const patch = {
iothubDM: {
firmwareUpdate: firmwareUpdateValue,
},
};
console.log(JSON.stringify(patch, null, 2));
client.getTwin(function (err: Error, twin: Twin): void {
if (!err) {
twin.properties.reported.update(patch, function (err: Error): void {
callback(err);
});
} else {
callback(err);
}
});
}
6 changes: 4 additions & 2 deletions device/samples/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
"author": "Microsoft Corp.",
"license": "MIT",
"dependencies": {
"@azure/storage-blob": "^12.8.0",
"@types/ws": "^7.4.7",
"azure-iot-device": "1.17.6",
"azure-iot-device-amqp": "1.13.6",
"azure-iot-device-http": "1.13.6",
"azure-iot-device-mqtt": "1.15.6",
"@azure/storage-blob": "^12.8.0"
"https-proxy-agent": "^5.0.0"
},
"devDependencies": {
"@types/node": "^16.9.6",
"@types/async": "^3.2.8",
"@types/node": "^16.10.2",
"@types/ws": "^7.4.7",
"source-map-support": "^0.5.16",
"ts-node": "^8.6.2",
Expand Down
Loading