Skip to content

Final additions in private beta #489

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

Merged
merged 11 commits into from
Sep 26, 2017
61 changes: 61 additions & 0 deletions iot/http_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<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 Cloud Pub/Sub or states using the HTTP
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what "Cloud Pub/Sub or states" means.

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.

# 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 or ES256
--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
155 changes: 155 additions & 0 deletions iot/http_example/cloudiot_http_example_nodejs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* 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';

const fs = require('fs');
const jwt = require('jsonwebtoken');
const request = require('request');

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 JWT.',
requiresArg: true,
demandOption: true,
choices: ['RS256', 'ES256'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the target user of this sample is expected to be familiar with JWT, or node already prints allowed choices, then please ignore my comment:

It might help to print the allowed choices in the help/description message.

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

// 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.
function publishAsync (messageCount, numMessages) {
const payload = `${argv.registry_id}/${argv.device_id}-payload-${messageCount}`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The publishAsync function in http_client_example.js seems more flexible allowing the user to specify a message argument. Why is the publishAsync function in this file not allowing the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked in duplicate versions of the same sample, will clean up after dinner.

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

// 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);
12 changes: 12 additions & 0 deletions iot/http_example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "nodejs-docs-samples-iot-http-example",
"version": "0.0.1",
"description": "HTTP Example for Google Cloud IoT Core using NodeJS.",
"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