Skip to content

Commit 5d90464

Browse files
authored
Final additions in private beta (GoogleCloudPlatform#489)
* 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
1 parent 072496e commit 5d90464

8 files changed

Lines changed: 391 additions & 58 deletions

File tree

iot/http_example/README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<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"/>
2+
3+
# Google Cloud IoT Core NodeJS HTTP example
4+
5+
This sample app publishes messages to [Google Cloud Pub/Sub](pubsub) or updates
6+
device states using the HTTP bridge provided as part of Google Cloud IoT Core.
7+
8+
Note that before you can run this sample, you must register a device as
9+
described in the parent README.
10+
11+
[pubsub]: https://cloud.google.com/pubsub/docs
12+
# Setup
13+
14+
Run the following command to install the library dependencies for NodeJS:
15+
16+
npm install
17+
18+
# Running the sample
19+
20+
The following command summarizes the sample usage:
21+
22+
Usage: cloudiot_http_example_nodejs [options]
23+
24+
Example Google Cloud IoT Core HTTP device connection code.
25+
26+
Options:
27+
28+
-h, --help output usage information
29+
--project_id <project_id> GCP cloud project name.
30+
--registry_id <registry_id> Cloud IoT Core registry id.
31+
--device_id <device_id> Cloud IoT Core device id.
32+
--private_key_file <key_file> Path to private key file.
33+
--algorithm <algorithm> Encryption algorithm to generate the JWT.
34+
Either RS256 (RSA) or ES256 (Eliptic Curve)
35+
--cloud_region [region] GCP cloud region
36+
--num_messages [num] Number of messages to publish.
37+
--http_bridge_address [address] HTTP bridge address.
38+
--message_type [events|state] The message type to publish.
39+
40+
For example, if your project ID is `blue-jet-123`, your service account
41+
credentials are stored in your home folder in creds.json and you have generated
42+
your credentials using the shell script provided in the parent folder, you can
43+
run the sample as:
44+
45+
node cloudiot_http_example_nodejs.js \
46+
--project_id=blue-jet-123 \
47+
--registry_id=my-registry \
48+
--device_id=my-node-device \
49+
--private_key_file=../rsa_private.pem \
50+
--algorithm=RS256
51+
52+
# Reading Cloud Pub/Sub messages written by the sample client
53+
54+
1. Create a subscription to your topic.
55+
56+
gcloud beta pubsub subscriptions create \
57+
projects/your-project-id/subscriptions/my-subscription \
58+
--topic device-events
59+
60+
2. Read messages published to the topic
61+
62+
gcloud beta pubsub subscriptions pull --auto-ack \
63+
projects/my-iot-project/subscriptions/my-subscription
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/**
2+
* Copyright 2017, Google, Inc.
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
'use strict';
17+
// [START iot_http_includes]
18+
const fs = require('fs');
19+
const jwt = require('jsonwebtoken');
20+
const request = require('request');
21+
// [END iot_http_includes]
22+
23+
console.log('Google Cloud IoT Core HTTP example.');
24+
var argv = require(`yargs`)
25+
.options({
26+
project_id: {
27+
default: process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT,
28+
description: 'The Project ID to use. Defaults to the value of the GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT environment variables.',
29+
requiresArg: true,
30+
type: 'string'
31+
},
32+
cloud_region: {
33+
default: 'us-central1',
34+
description: 'GCP cloud region.',
35+
requiresArg: true,
36+
type: 'string'
37+
},
38+
registry_id: {
39+
description: 'Cloud IoT registry ID.',
40+
requiresArg: true,
41+
demandOption: true,
42+
type: 'string'
43+
},
44+
device_id: {
45+
description: 'Cloud IoT device ID.',
46+
requiresArg: true,
47+
demandOption: true,
48+
type: 'string'
49+
},
50+
private_key_file: {
51+
description: 'Path to private key file.',
52+
requiresArg: true,
53+
demandOption: true,
54+
type: 'string'
55+
},
56+
algorithm: {
57+
description: 'Encryption algorithm to generate the RSA or EC JWT.',
58+
requiresArg: true,
59+
demandOption: true,
60+
choices: ['RS256', 'ES256'],
61+
type: 'string'
62+
},
63+
num_messages: {
64+
default: 100,
65+
description: 'Number of messages to publish.',
66+
requiresArg: true,
67+
type: 'number'
68+
},
69+
http_bridge_address: {
70+
default: 'cloudiot-device.googleapis.com',
71+
description: 'HTTP bridge address.',
72+
requiresArg: true,
73+
type: 'string'
74+
},
75+
message_type: {
76+
default: 'events',
77+
description: 'Message type to publish.',
78+
requiresArg: true,
79+
choices: ['events', 'state'],
80+
type: 'string'
81+
}
82+
})
83+
.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`)
84+
.wrap(120)
85+
.recommendCommands()
86+
.epilogue(`For more information, see https://cloud.google.com/iot-core/docs`)
87+
.help()
88+
.strict()
89+
.argv;
90+
91+
// Create a Cloud IoT Core JWT for the given project ID, signed with the given
92+
// private key.
93+
// [START iot_http_jwt]
94+
function createJwt (projectId, privateKeyFile, algorithm) {
95+
// Create a JWT to authenticate this device. The device will be disconnected
96+
// after the token expires, and will have to reconnect with a new token. The
97+
// audience field should always be set to the GCP project ID.
98+
const token = {
99+
'iat': parseInt(Date.now() / 1000),
100+
'exp': parseInt(Date.now() / 1000) + 20 * 60, // 20 minutes
101+
'aud': projectId
102+
};
103+
const privateKey = fs.readFileSync(privateKeyFile);
104+
return jwt.sign(token, privateKey, { algorithm: algorithm });
105+
}
106+
// [END iot_http_jwt]
107+
108+
// Publish numMessages message asynchronously, starting from message
109+
// messageCount. Telemetry events are published at a rate of 1 per second and
110+
// states at a rate of 1 every 2 seconds.
111+
// [START iot_http_publish]
112+
function publishAsync (messageCount, numMessages) {
113+
const payload = `${argv.registry_id}/${argv.device_id}-payload-${messageCount}`;
114+
console.log('Publishing message:', payload);
115+
const binaryData = Buffer.from(payload).toString('base64');
116+
const postData = argv.message_type === 'events' ? {
117+
binary_data: binaryData
118+
} : {
119+
state: {
120+
binary_data: binaryData
121+
}
122+
};
123+
const options = {
124+
url: url,
125+
headers: {
126+
'Authorization': 'Bearer ' + authToken
127+
},
128+
json: true,
129+
body: postData
130+
};
131+
// Send events for high-frequency updates, update state only occasionally.
132+
const delayMs = argv.message_type === 'events' ? 1000 : 2000;
133+
request.post(options, function (error, response, body) {
134+
if (error) {
135+
return console.error('Received error: ', error);
136+
}
137+
console.log('Received response: ');
138+
console.dir(response);
139+
if (messageCount < numMessages) {
140+
// If we have published fewer than numMessage messages, publish payload
141+
// messageCount + 1.
142+
setTimeout(function () {
143+
publishAsync(messageCount + 1, numMessages);
144+
}, delayMs);
145+
}
146+
});
147+
}
148+
// [END iot_http_publish]
149+
150+
// [START iot_run_http]
151+
// A unique string that identifies this device. For Google Cloud IoT Core, it
152+
// must be in the format below.
153+
const devicePath = `projects/${argv.project_id}/locations/${argv.cloud_region}/registries/${argv.registry_id}/devices/${argv.device_id}`;
154+
155+
// The request path, set accordingly depending on the message type.
156+
const pathSuffix = argv.message_type === 'events'
157+
? ':publishEvent' : ':setState';
158+
const url = `https://${argv.http_bridge_address}/v1beta1/${devicePath}${pathSuffix}`;
159+
const authToken = createJwt(argv.project_id, argv.private_key_file, argv.algorithm);
160+
161+
// Publish messages.
162+
publishAsync(1, argv.num_messages);
163+
// [END iot_run_http]

iot/http_example/package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "nodejs-docs-samples-iot-http-example",
3+
"version": "0.0.1",
4+
"description": "HTTP Example for Google Cloud IoT Core using NodeJS.",
5+
"license": "Apache-2.0",
6+
"author": "Google Inc.",
7+
"main": "cloudiot_http_example_nodejs.js",
8+
"dependencies": {
9+
"yargs": "8.0.2",
10+
"jsonwebtoken": "7.4.1",
11+
"request": "2.82.0"
12+
},
13+
"devDependencies": {}
14+
}

iot/manager/manager.js

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
const fs = require('fs');
1919
const google = require('googleapis');
2020

21-
const API_VERSION = 'v1beta1';
21+
const API_VERSION = 'v1';
2222
const DISCOVERY_API = 'https://cloudiot.googleapis.com/$discovery/rest';
2323

2424
// Configures the topic for Cloud IoT Core.
@@ -125,9 +125,9 @@ function createRegistry (client, registryId, projectId, cloudRegion,
125125
const request = {
126126
parent: parentName,
127127
resource: {
128-
eventNotificationConfig: {
129-
pubsubTopicName: pubsubTopic
130-
},
128+
eventNotificationConfigs: [{
129+
'pubsubTopicName': pubsubTopic
130+
}],
131131
'id': registryId
132132
}
133133
};
@@ -545,6 +545,34 @@ function getDevice (client, deviceId, registryId, projectId, cloudRegion) {
545545
// [END iot_get_device]
546546
}
547547

548+
// Retrieve the given device's state from the registry.
549+
function getDeviceState (client, deviceId, registryId, projectId,
550+
cloudRegion) {
551+
// [START iot_get_device_state]
552+
// Client retrieved in callback
553+
// getClient(apiKey, serviceAccountJson, function(client) {...});
554+
// const cloudRegion = 'us-central1';
555+
// const deviceId = 'my-device';
556+
// const projectId = 'adjective-noun-123';
557+
// const registryId = 'my-registry';
558+
const parentName = `projects/${projectId}/locations/${cloudRegion}`;
559+
const registryName = `${parentName}/registries/${registryId}`;
560+
const request = {
561+
name: `${registryName}/devices/${deviceId}`
562+
};
563+
564+
client.projects.locations.registries.devices.states.list(request,
565+
(err, data) => {
566+
if (err) {
567+
console.log('Could not find device:', deviceId);
568+
console.log(err);
569+
} else {
570+
console.log('State:', data);
571+
}
572+
});
573+
// [END iot_get_device_state]
574+
}
575+
548576
// Retrieve the given device from the registry.
549577
function getRegistry (client, registryId, projectId, cloudRegion) {
550578
// [START iot_get_registry]
@@ -733,6 +761,18 @@ require(`yargs`) // eslint-disable-line
733761
getClient(opts.apiKey, opts.serviceAccount, cb);
734762
}
735763
)
764+
.command(
765+
`getDeviceState <deviceId> <registryId>`,
766+
`Retrieves device state given a device ID.`,
767+
{},
768+
(opts) => {
769+
const cb = function (client) {
770+
getDeviceState(client, opts.deviceId, opts.registryId, opts.projectId,
771+
opts.cloudRegion);
772+
};
773+
getClient(opts.apiKey, opts.serviceAccount, cb);
774+
}
775+
)
736776
.command(
737777
`getRegistry <registryId>`,
738778
`Retrieves a registry.`,
@@ -797,6 +837,7 @@ require(`yargs`) // eslint-disable-line
797837
.example(`node $0 deleteDevice my-device my-registry`)
798838
.example(`node $0 deleteRegistry my-device my-registry`)
799839
.example(`node $0 getDevice my-device my-registry`)
840+
.example(`node $0 getDeviceState my-device my-registry`)
800841
.example(`node $0 getRegistry my-registry`)
801842
.example(`node $0 listDevices my-node-registry`)
802843
.example(`node $0 listRegistries`)

iot/manager/system-test/manager.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ test(`should create and delete an RSA256 device`, async (t) => {
7070
output = await tools.runAsync(
7171
`${cmd} createRsa256Device ${localDevice} ${localRegName} resources/rsa_cert.pem`, cwd);
7272
t.regex(output, new RegExp(`Created device`));
73+
output = await tools.runAsync(
74+
`${cmd} getDeviceState ${localDevice} ${localRegName}`, cwd);
75+
t.regex(output, new RegExp(`State`));
7376
output = await tools.runAsync(
7477
`${cmd} deleteDevice ${localDevice} ${localRegName}`, cwd);
7578
t.regex(output, new RegExp(`Successfully deleted device`));
@@ -85,6 +88,9 @@ test(`should create and delete an EC256 device`, async (t) => {
8588
output = await tools.runAsync(
8689
`${cmd} createEs256Device ${localDevice} ${localRegName} resources/ec_public.pem`, cwd);
8790
t.regex(output, new RegExp(`Created device`));
91+
output = await tools.runAsync(
92+
`${cmd} getDeviceState ${localDevice} ${localRegName}`, cwd);
93+
t.regex(output, new RegExp(`State`));
8894
output = await tools.runAsync(
8995
`${cmd} deleteDevice ${localDevice} ${localRegName}`, cwd);
9096
t.regex(output, new RegExp(`Successfully deleted device`));

iot/mqtt_example/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ The following command summarizes the sample usage:
3434
--num_messages [num] Number of messages to publish.
3535
--mqtt_bridge_hostname [hostname] MQTT bridge hostname.
3636
--mqtt_bridge_port [port] MQTT bridge port.
37+
--message_type [events|state] The message type to publish.
3738

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

0 commit comments

Comments
 (0)