Skip to content

Commit

Permalink
Final additions in private beta (#489)
Browse files Browse the repository at this point in the history
* Changes to registry create
* Adds HTTP client and state support for MQTT
* Removes extra code and discovery doc
* Add product link to readme, options to yargs.
* Adds includecode blocks to MQTT / HTTP
* Add license to package.json
  • Loading branch information
gguuss authored Sep 26, 2017
1 parent 072496e commit 5d90464
Show file tree
Hide file tree
Showing 8 changed files with 391 additions and 58 deletions.
63 changes: 63 additions & 0 deletions iot/http_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<img src="https://avatars2.githubusercontent.com/u/2810941?v=3&s=96" alt="Google Cloud Platform logo" title="Google Cloud Platform" align="right" height="96" width="96"/>

# Google Cloud IoT Core NodeJS HTTP example

This sample app publishes messages to [Google Cloud Pub/Sub](pubsub) or updates
device states using the HTTP bridge provided as part of Google Cloud IoT Core.

Note that before you can run this sample, you must register a device as
described in the parent README.

[pubsub]: https://cloud.google.com/pubsub/docs
# Setup

Run the following command to install the library dependencies for NodeJS:

npm install

# Running the sample

The following command summarizes the sample usage:

Usage: cloudiot_http_example_nodejs [options]

Example Google Cloud IoT Core HTTP device connection code.

Options:

-h, --help output usage information
--project_id <project_id> GCP cloud project name.
--registry_id <registry_id> Cloud IoT Core registry id.
--device_id <device_id> Cloud IoT Core device id.
--private_key_file <key_file> Path to private key file.
--algorithm <algorithm> Encryption algorithm to generate the JWT.
Either RS256 (RSA) or ES256 (Eliptic Curve)
--cloud_region [region] GCP cloud region
--num_messages [num] Number of messages to publish.
--http_bridge_address [address] HTTP bridge address.
--message_type [events|state] The message type to publish.

For example, if your project ID is `blue-jet-123`, your service account
credentials are stored in your home folder in creds.json and you have generated
your credentials using the shell script provided in the parent folder, you can
run the sample as:

node cloudiot_http_example_nodejs.js \
--project_id=blue-jet-123 \
--registry_id=my-registry \
--device_id=my-node-device \
--private_key_file=../rsa_private.pem \
--algorithm=RS256

# Reading Cloud Pub/Sub messages written by the sample client

1. Create a subscription to your topic.

gcloud beta pubsub subscriptions create \
projects/your-project-id/subscriptions/my-subscription \
--topic device-events

2. Read messages published to the topic

gcloud beta pubsub subscriptions pull --auto-ack \
projects/my-iot-project/subscriptions/my-subscription
163 changes: 163 additions & 0 deletions iot/http_example/cloudiot_http_example_nodejs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/**
* Copyright 2017, Google, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';
// [START iot_http_includes]
const fs = require('fs');
const jwt = require('jsonwebtoken');
const request = require('request');
// [END iot_http_includes]

console.log('Google Cloud IoT Core HTTP example.');
var argv = require(`yargs`)
.options({
project_id: {
default: process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT,
description: 'The Project ID to use. Defaults to the value of the GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT environment variables.',
requiresArg: true,
type: 'string'
},
cloud_region: {
default: 'us-central1',
description: 'GCP cloud region.',
requiresArg: true,
type: 'string'
},
registry_id: {
description: 'Cloud IoT registry ID.',
requiresArg: true,
demandOption: true,
type: 'string'
},
device_id: {
description: 'Cloud IoT device ID.',
requiresArg: true,
demandOption: true,
type: 'string'
},
private_key_file: {
description: 'Path to private key file.',
requiresArg: true,
demandOption: true,
type: 'string'
},
algorithm: {
description: 'Encryption algorithm to generate the RSA or EC JWT.',
requiresArg: true,
demandOption: true,
choices: ['RS256', 'ES256'],
type: 'string'
},
num_messages: {
default: 100,
description: 'Number of messages to publish.',
requiresArg: true,
type: 'number'
},
http_bridge_address: {
default: 'cloudiot-device.googleapis.com',
description: 'HTTP bridge address.',
requiresArg: true,
type: 'string'
},
message_type: {
default: 'events',
description: 'Message type to publish.',
requiresArg: true,
choices: ['events', 'state'],
type: 'string'
}
})
.example(`node $0 cloudiot_http_example_nodejs.js --project_id=blue-jet-123 --registry_id=my-registry --device_id=my-node-device --private_key_file=../rsa_private.pem --algorithm=RS256`)
.wrap(120)
.recommendCommands()
.epilogue(`For more information, see https://cloud.google.com/iot-core/docs`)
.help()
.strict()
.argv;

// Create a Cloud IoT Core JWT for the given project ID, signed with the given
// private key.
// [START iot_http_jwt]
function createJwt (projectId, privateKeyFile, algorithm) {
// Create a JWT to authenticate this device. The device will be disconnected
// after the token expires, and will have to reconnect with a new token. The
// audience field should always be set to the GCP project ID.
const token = {
'iat': parseInt(Date.now() / 1000),
'exp': parseInt(Date.now() / 1000) + 20 * 60, // 20 minutes
'aud': projectId
};
const privateKey = fs.readFileSync(privateKeyFile);
return jwt.sign(token, privateKey, { algorithm: algorithm });
}
// [END iot_http_jwt]

// Publish numMessages message asynchronously, starting from message
// messageCount. Telemetry events are published at a rate of 1 per second and
// states at a rate of 1 every 2 seconds.
// [START iot_http_publish]
function publishAsync (messageCount, numMessages) {
const payload = `${argv.registry_id}/${argv.device_id}-payload-${messageCount}`;
console.log('Publishing message:', payload);
const binaryData = Buffer.from(payload).toString('base64');
const postData = argv.message_type === 'events' ? {
binary_data: binaryData
} : {
state: {
binary_data: binaryData
}
};
const options = {
url: url,
headers: {
'Authorization': 'Bearer ' + authToken
},
json: true,
body: postData
};
// Send events for high-frequency updates, update state only occasionally.
const delayMs = argv.message_type === 'events' ? 1000 : 2000;
request.post(options, function (error, response, body) {
if (error) {
return console.error('Received error: ', error);
}
console.log('Received response: ');
console.dir(response);
if (messageCount < numMessages) {
// If we have published fewer than numMessage messages, publish payload
// messageCount + 1.
setTimeout(function () {
publishAsync(messageCount + 1, numMessages);
}, delayMs);
}
});
}
// [END iot_http_publish]

// [START iot_run_http]
// A unique string that identifies this device. For Google Cloud IoT Core, it
// must be in the format below.
const devicePath = `projects/${argv.project_id}/locations/${argv.cloud_region}/registries/${argv.registry_id}/devices/${argv.device_id}`;

// The request path, set accordingly depending on the message type.
const pathSuffix = argv.message_type === 'events'
? ':publishEvent' : ':setState';
const url = `https://${argv.http_bridge_address}/v1beta1/${devicePath}${pathSuffix}`;
const authToken = createJwt(argv.project_id, argv.private_key_file, argv.algorithm);

// Publish messages.
publishAsync(1, argv.num_messages);
// [END iot_run_http]
14 changes: 14 additions & 0 deletions iot/http_example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "nodejs-docs-samples-iot-http-example",
"version": "0.0.1",
"description": "HTTP Example for Google Cloud IoT Core using NodeJS.",
"license": "Apache-2.0",
"author": "Google Inc.",
"main": "cloudiot_http_example_nodejs.js",
"dependencies": {
"yargs": "8.0.2",
"jsonwebtoken": "7.4.1",
"request": "2.82.0"
},
"devDependencies": {}
}
49 changes: 45 additions & 4 deletions iot/manager/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
const fs = require('fs');
const google = require('googleapis');

const API_VERSION = 'v1beta1';
const API_VERSION = 'v1';
const DISCOVERY_API = 'https://cloudiot.googleapis.com/$discovery/rest';

// Configures the topic for Cloud IoT Core.
Expand Down Expand Up @@ -125,9 +125,9 @@ function createRegistry (client, registryId, projectId, cloudRegion,
const request = {
parent: parentName,
resource: {
eventNotificationConfig: {
pubsubTopicName: pubsubTopic
},
eventNotificationConfigs: [{
'pubsubTopicName': pubsubTopic
}],
'id': registryId
}
};
Expand Down Expand Up @@ -545,6 +545,34 @@ function getDevice (client, deviceId, registryId, projectId, cloudRegion) {
// [END iot_get_device]
}

// Retrieve the given device's state from the registry.
function getDeviceState (client, deviceId, registryId, projectId,
cloudRegion) {
// [START iot_get_device_state]
// Client retrieved in callback
// getClient(apiKey, serviceAccountJson, function(client) {...});
// const cloudRegion = 'us-central1';
// const deviceId = 'my-device';
// const projectId = 'adjective-noun-123';
// const registryId = 'my-registry';
const parentName = `projects/${projectId}/locations/${cloudRegion}`;
const registryName = `${parentName}/registries/${registryId}`;
const request = {
name: `${registryName}/devices/${deviceId}`
};

client.projects.locations.registries.devices.states.list(request,
(err, data) => {
if (err) {
console.log('Could not find device:', deviceId);
console.log(err);
} else {
console.log('State:', data);
}
});
// [END iot_get_device_state]
}

// Retrieve the given device from the registry.
function getRegistry (client, registryId, projectId, cloudRegion) {
// [START iot_get_registry]
Expand Down Expand Up @@ -733,6 +761,18 @@ require(`yargs`) // eslint-disable-line
getClient(opts.apiKey, opts.serviceAccount, cb);
}
)
.command(
`getDeviceState <deviceId> <registryId>`,
`Retrieves device state given a device ID.`,
{},
(opts) => {
const cb = function (client) {
getDeviceState(client, opts.deviceId, opts.registryId, opts.projectId,
opts.cloudRegion);
};
getClient(opts.apiKey, opts.serviceAccount, cb);
}
)
.command(
`getRegistry <registryId>`,
`Retrieves a registry.`,
Expand Down Expand Up @@ -797,6 +837,7 @@ require(`yargs`) // eslint-disable-line
.example(`node $0 deleteDevice my-device my-registry`)
.example(`node $0 deleteRegistry my-device my-registry`)
.example(`node $0 getDevice my-device my-registry`)
.example(`node $0 getDeviceState my-device my-registry`)
.example(`node $0 getRegistry my-registry`)
.example(`node $0 listDevices my-node-registry`)
.example(`node $0 listRegistries`)
Expand Down
6 changes: 6 additions & 0 deletions iot/manager/system-test/manager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ test(`should create and delete an RSA256 device`, async (t) => {
output = await tools.runAsync(
`${cmd} createRsa256Device ${localDevice} ${localRegName} resources/rsa_cert.pem`, cwd);
t.regex(output, new RegExp(`Created device`));
output = await tools.runAsync(
`${cmd} getDeviceState ${localDevice} ${localRegName}`, cwd);
t.regex(output, new RegExp(`State`));
output = await tools.runAsync(
`${cmd} deleteDevice ${localDevice} ${localRegName}`, cwd);
t.regex(output, new RegExp(`Successfully deleted device`));
Expand All @@ -85,6 +88,9 @@ test(`should create and delete an EC256 device`, async (t) => {
output = await tools.runAsync(
`${cmd} createEs256Device ${localDevice} ${localRegName} resources/ec_public.pem`, cwd);
t.regex(output, new RegExp(`Created device`));
output = await tools.runAsync(
`${cmd} getDeviceState ${localDevice} ${localRegName}`, cwd);
t.regex(output, new RegExp(`State`));
output = await tools.runAsync(
`${cmd} deleteDevice ${localDevice} ${localRegName}`, cwd);
t.regex(output, new RegExp(`Successfully deleted device`));
Expand Down
1 change: 1 addition & 0 deletions iot/mqtt_example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The following command summarizes the sample usage:
--num_messages [num] Number of messages to publish.
--mqtt_bridge_hostname [hostname] MQTT bridge hostname.
--mqtt_bridge_port [port] MQTT bridge port.
--message_type [events|state] The message type to publish.

For example, if your project ID is `blue-jet-123`, your service account
credentials are stored in your home folder in creds.json and you have generated
Expand Down
Loading

0 comments on commit 5d90464

Please sign in to comment.