From e47f4c3d2b32fbf3ea5d03a5b9e75a88bea71dcb Mon Sep 17 00:00:00 2001 From: Gus Class Date: Fri, 22 Sep 2017 15:01:09 -0700 Subject: [PATCH 1/9] Final additions in private beta --- iot/manager/manager.js | 43 ++++++++++++++++++++++++- iot/manager/system-test/manager.test.js | 6 ++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/iot/manager/manager.js b/iot/manager/manager.js index 732bb0f69c..2255612636 100644 --- a/iot/manager/manager.js +++ b/iot/manager/manager.js @@ -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. @@ -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] @@ -733,6 +761,18 @@ require(`yargs`) // eslint-disable-line getClient(opts.apiKey, opts.serviceAccount, cb); } ) + .command( + `getDeviceState `, + `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 `, `Retrieves a registry.`, @@ -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`) diff --git a/iot/manager/system-test/manager.test.js b/iot/manager/system-test/manager.test.js index 3b2d04d795..e797d24992 100644 --- a/iot/manager/system-test/manager.test.js +++ b/iot/manager/system-test/manager.test.js @@ -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`)); @@ -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`)); From e670a34fc9e3c36def78f9de9433bb7453cc04a3 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Mon, 25 Sep 2017 12:26:09 -0700 Subject: [PATCH 2/9] Changes to registry create --- iot/manager/manager.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iot/manager/manager.js b/iot/manager/manager.js index 2255612636..8e7c46cc2d 100644 --- a/iot/manager/manager.js +++ b/iot/manager/manager.js @@ -125,9 +125,9 @@ function createRegistry (client, registryId, projectId, cloudRegion, const request = { parent: parentName, resource: { - eventNotificationConfig: { - pubsubTopicName: pubsubTopic - }, + eventNotificationConfigs: [{ + "pubsubTopicName": pubsubTopic + }], 'id': registryId } }; From 4c83c859de4e3bd34510facd95de7811656b4574 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Mon, 25 Sep 2017 12:43:50 -0700 Subject: [PATCH 3/9] Adds HTTP client and state support for MQTT --- iot/http_example/README.md | 61 ++++ iot/http_example/cloudiot.rest.json | 279 ++++++++++++++++++ .../cloudiot_http_example_nodejs.js | 155 ++++++++++ iot/http_example/http_client_example.js | 112 +++++++ iot/http_example/package.json | 12 + iot/mqtt_example/README.md | 1 + .../cloudiot_mqtt_example_nodejs.js | 139 +++++---- iot/mqtt_example/package.json | 2 +- 8 files changed, 708 insertions(+), 53 deletions(-) create mode 100644 iot/http_example/README.md create mode 100644 iot/http_example/cloudiot.rest.json create mode 100644 iot/http_example/cloudiot_http_example_nodejs.js create mode 100644 iot/http_example/http_client_example.js create mode 100644 iot/http_example/package.json diff --git a/iot/http_example/README.md b/iot/http_example/README.md new file mode 100644 index 0000000000..24ed0fc4b8 --- /dev/null +++ b/iot/http_example/README.md @@ -0,0 +1,61 @@ +Google Cloud Platform logo + +# Google Cloud IoT Core NodeJS HTTP example + +This sample app publishes messages to Cloud Pub/Sub or 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. + +# 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 GCP cloud project name. + --registry_id Cloud IoT Core registry id. + --device_id Cloud IoT Core device id. + --private_key_file Path to private key file. + --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 diff --git a/iot/http_example/cloudiot.rest.json b/iot/http_example/cloudiot.rest.json new file mode 100644 index 0000000000..6b2dea920b --- /dev/null +++ b/iot/http_example/cloudiot.rest.json @@ -0,0 +1,279 @@ +{ + "rootUrl": "https://cloudiot-device.googleapis.com/", + "basePath": "", + "ownerDomain": "google.com", + "name": "cloudiot", + "batchPath": "batch", + "revision": "20170909", + "id": "cloudiot:v1beta1", + "documentationLink": "https://cloud.google.com/iot", + "title": "Google Cloud IoT API", + "discoveryVersion": "v1", + "ownerName": "Google", + "version_module": true, + "resources": { + "projects": { + "resources": { + "locations": { + "resources": { + "registries": { + "resources": { + "devices": { + "methods": { + "setState": { + "flatPath": "v1beta1/projects/{projectsId}/locations/{locationsId}/registries/{registriesId}/devices/{devicesId}:setState", + "id": "cloudiot.projects.locations.registries.devices.setState", + "path": "v1beta1/{+name}:setState", + "description": "Sets the state of a device.", + "request": { + "$ref": "HttpSetDeviceStateRequest" + }, + "response": { + "$ref": "Empty" + }, + "parameterOrder": [ + "name" + ], + "httpMethod": "POST", + "parameters": { + "name": { + "type": "string", + "required": true, + "pattern": "^projects/[^/]+/locations/[^/]+/registries/[^/]+/devices/[^/]+$", + "location": "path", + "description": "The name of the device. For example,\n`projects/p0/locations/us-central1/registries/registry0/devices/device0`." + } + } + }, + "getConfig": { + "response": { + "$ref": "HttpDeviceConfig" + }, + "parameterOrder": [ + "name" + ], + "httpMethod": "GET", + "parameters": { + "name": { + "location": "path", + "description": "The name of the device. For example,\n`projects/p0/locations/us-central1/registries/registry0/devices/device0`.", + "type": "string", + "required": true, + "pattern": "^projects/[^/]+/locations/[^/]+/registries/[^/]+/devices/[^/]+$" + }, + "localVersion": { + "type": "string", + "location": "query", + "format": "int64", + "description": "If zero, returns the current device configuration from Cloud IoT Core.\nIf nonzero, specifies the local version of the configuration on the device.\nThe server returns config data only if a higher (newer) version is\navailable from Cloud IoT Core.\nIf this value is higher than the latest version available in Cloud IoT\nCore, returns an `OUT_OF_RANGE` error." + } + }, + "flatPath": "v1beta1/projects/{projectsId}/locations/{locationsId}/registries/{registriesId}/devices/{devicesId}/config", + "id": "cloudiot.projects.locations.registries.devices.getConfig", + "path": "v1beta1/{+name}/config", + "description": "Gets the configuration of a device." + }, + "publishEvent": { + "httpMethod": "POST", + "parameterOrder": [ + "name" + ], + "response": { + "$ref": "HttpPublishEventResponse" + }, + "parameters": { + "name": { + "type": "string", + "required": true, + "pattern": "^projects/[^/]+/locations/[^/]+/registries/[^/]+/devices/[^/]+$", + "location": "path", + "description": "The name of the device. For example,\n`projects/p0/locations/us-central1/registries/registry0/devices/device0`." + } + }, + "flatPath": "v1beta1/projects/{projectsId}/locations/{locationsId}/registries/{registriesId}/devices/{devicesId}:publishEvent", + "path": "v1beta1/{+name}:publishEvent", + "id": "cloudiot.projects.locations.registries.devices.publishEvent", + "description": "Publishes a telemetry event for a device.", + "request": { + "$ref": "HttpPublishEventRequest" + } + } + } + } + } + } + } + } + } + } + }, + "parameters": { + "upload_protocol": { + "location": "query", + "description": "Upload protocol for media (e.g. \"raw\", \"multipart\").", + "type": "string" + }, + "prettyPrint": { + "default": "true", + "type": "boolean", + "location": "query", + "description": "Returns response with indentations and line breaks." + }, + "fields": { + "location": "query", + "description": "Selector specifying which fields to include in a partial response.", + "type": "string" + }, + "uploadType": { + "type": "string", + "location": "query", + "description": "Legacy upload protocol for media (e.g. \"media\", \"multipart\")." + }, + "callback": { + "type": "string", + "location": "query", + "description": "JSONP" + }, + "$.xgafv": { + "type": "string", + "enumDescriptions": [ + "v1 error format", + "v2 error format" + ], + "location": "query", + "enum": [ + "1", + "2" + ], + "description": "V1 error format." + }, + "alt": { + "type": "string", + "enumDescriptions": [ + "Responses with Content-Type of application/json", + "Media download with context-dependent Content-Type", + "Responses with Content-Type of application/x-protobuf" + ], + "location": "query", + "description": "Data format for response.", + "default": "json", + "enum": [ + "json", + "media", + "proto" + ] + }, + "key": { + "location": "query", + "description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.", + "type": "string" + }, + "access_token": { + "location": "query", + "description": "OAuth access token.", + "type": "string" + }, + "quotaUser": { + "type": "string", + "location": "query", + "description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters." + }, + "pp": { + "default": "true", + "type": "boolean", + "location": "query", + "description": "Pretty-print response." + }, + "oauth_token": { + "location": "query", + "description": "OAuth 2.0 token for the current user.", + "type": "string" + }, + "bearer_token": { + "type": "string", + "location": "query", + "description": "OAuth bearer token." + } + }, + "schemas": { + "HttpPublishEventResponse": { + "type": "object", + "properties": {}, + "id": "HttpPublishEventResponse", + "description": "Response for `PublishEvent`." + }, + "HttpSetDeviceStateRequest": { + "type": "object", + "properties": { + "state": { + "$ref": "HttpDeviceState", + "description": "The device state." + } + }, + "id": "HttpSetDeviceStateRequest", + "description": "Request for `SetDeviceState`." + }, + "HttpDeviceConfig": { + "type": "object", + "properties": { + "version": { + "type": "string", + "format": "int64", + "description": "The version of the configuration in Cloud IoT Core." + }, + "binaryData": { + "format": "byte", + "description": "Data in binary format.", + "type": "string" + } + }, + "id": "HttpDeviceConfig", + "description": "The device configuration obtained from Cloud IoT Core." + }, + "HttpDeviceState": { + "type": "object", + "properties": { + "binaryData": { + "type": "string", + "format": "byte", + "description": "Data in binary format." + } + }, + "id": "HttpDeviceState", + "description": "The device state reported to Cloud IoT Core." + }, + "HttpPublishEventRequest": { + "type": "object", + "properties": { + "subFolder": { + "type": "string", + "description": "Optional subfolder for the telemetry event. This can be used to classify\ntypes of events, and is included in the Pub/Sub message attributes." + }, + "binaryData": { + "format": "byte", + "description": "Payload data in binary format.", + "type": "string" + } + }, + "id": "HttpPublishEventRequest", + "description": "Request for `PublishEvent`." + }, + "Empty": { + "description": "A generic empty message that you can re-use to avoid defining duplicated\nempty messages in your APIs. A typical example is to use it as the request\nor the response type of an API method. For instance:\n\n service Foo {\n rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);\n }\n\nThe JSON representation for `Empty` is empty JSON object `{}`.", + "type": "object", + "properties": {}, + "id": "Empty" + } + }, + "icons": { + "x32": "http://www.google.com/images/icons/product/search-32.gif", + "x16": "http://www.google.com/images/icons/product/search-16.gif" + }, + "protocol": "rest", + "version": "v1beta1", + "baseUrl": "https://cloudiot-device.googleapis.com/", + "canonicalName": "Cloud Iot", + "servicePath": "", + "description": "Registers and manages IoT (Internet of Things) devices that connect to the Google Cloud Platform.\n", + "kind": "discovery#restDescription" +} diff --git a/iot/http_example/cloudiot_http_example_nodejs.js b/iot/http_example/cloudiot_http_example_nodejs.js new file mode 100644 index 0000000000..657caa9797 --- /dev/null +++ b/iot/http_example/cloudiot_http_example_nodejs.js @@ -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'], + 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}`; + 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); diff --git a/iot/http_example/http_client_example.js b/iot/http_example/http_client_example.js new file mode 100644 index 0000000000..d467682ec5 --- /dev/null +++ b/iot/http_example/http_client_example.js @@ -0,0 +1,112 @@ +/** + * 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. + */ + +/** NodeJS sample of connecting to Google Cloud IoT Core via HTTP. */ + +'use strict'; + +const fs = require('fs'); +const google = require('googleapis'); +const jwt = require('jsonwebtoken'); + +// 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 messages asynchronously, starting from message +// messageCount. +function publishAsync (bearer, client, messageCount, message, projectId, cloudRegion, registryId, deviceId) { + const path = 'projects/' + projectId + '/locations/' + cloudRegion + + '/registries/' + registryId + '/devices/' + deviceId; + + // Publish "payload" to the MQTT topic. qos=1 means at least once delivery. + // Cloud IoT Core also supports qos=0 for at most once delivery. + console.log('Publishing to:', path); + console.log('Publishing message:', message); + + // TODO: Publish message hurr + let request = { + name: path, + headers : [{'Authorization': 'Bearer ' + bearer}] + }; + client.projects.locations.registries.devices.publishEvent(request, function(res){console.log(res);}); +} + +require(`yargs`) // eslint-disable-line + .demand(1) + .options({ + deviceId: { + alias: 'd', + description: 'The ID used to register this device.', + type: 'string' + }, + cloudRegion: { + alias: 'c', + default: 'us-central1', + type: 'string' + }, + projectId: { + alias: 'p', + 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.', + type: 'string' + }, + registryId: { + alias: 'r', + description: 'The identifier for your device registry.', + type: 'string' + }, + rsaPath: { + alias: 'rsa', + description: 'The file path for your RSA private key.', + requiresArg: true, + type: 'string' + } + }) + .command( + `publish `, + `publishes a message`, + {}, + (opts) => { + let bearer = createJwt(opts.projectId, opts.rsaPath, 'RS256'); + console.log(bearer); + google.discoverAPI(`cloudiot.rest.json`, + {auth: bearer}, (err, client) => { + if (err) { + console.log('Error during API discovery', err); + return undefined; + } + // cb(client) + publishAsync(bearer, client, opts.messageCount, opts.message, + opts.projectId, opts.cloudRegion, opts.registryId, opts.deviceId); + }); + }) + .wrap(120) + .recommendCommands() + .epilogue(`For more information, see https://cloud.google.com/iot-core/docs`) + .help() + .strict() + .argv; diff --git a/iot/http_example/package.json b/iot/http_example/package.json new file mode 100644 index 0000000000..5fb97dadae --- /dev/null +++ b/iot/http_example/package.json @@ -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": {} +} diff --git a/iot/mqtt_example/README.md b/iot/mqtt_example/README.md index 342da97943..848681af22 100644 --- a/iot/mqtt_example/README.md +++ b/iot/mqtt_example/README.md @@ -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 diff --git a/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js b/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js index 7b73d4b128..e4e1ea2843 100644 --- a/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js +++ b/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js @@ -13,52 +13,85 @@ * limitations under the License. */ -/** - * NodeJS sample of connecting to Google Cloud IoT Core via MQTT, using JWT. - * - *

This example connects to Google Cloud IoT Core via MQTT, using a JWT for - * device authentication. After connecting, by default the device publishes 100 - * messages to the device's MQTT topic at a rate of one per second, and then - * exits. - * - *

Before you can run this sample, you must register a device as described - * in the parent README. - * - *

Usage example: - * - *

- *   $ npm install
- *   $ nodejs cloudiot_mqtt_example_nodejs.js \
- *       --project_id=my-project-id \
- *       --registry_id=my-registry-id \
- *       --device_id=my-device-id \
- *       --private_key_file=rsa_private.pem \
- *       --algorithm=RS256"
- * 
- */ - 'use strict'; const fs = require('fs'); const jwt = require('jsonwebtoken'); const mqtt = require('mqtt'); -const program = require('commander'); -program.description('Google Cloud IoT Core MQTT example.') - .option('--project_id ', 'GCP cloud project name.') - .option('--registry_id ', 'Cloud IoT registry id.') - .option('--device_id ', 'Cloud IoT device id.') - .option('--private_key_file ', 'Path to private key file.') - .option( - '--algorithm ', - 'Encryption algorithm to generate the JWT. Either RS256 or ES256') - .option('--cloud_region [region]', 'GCP cloud region', 'us-central1') - .option('--num_messages [num]', 'Number of messages to publish.', 100) - .option( - '--mqtt_bridge_hostname [hostname]', 'MQTT bridge hostname.', - 'mqtt.googleapis.com') - .option('--mqtt_bridge_port [port]', 'MQTT bridge port.', 8883) - .parse(process.argv); +console.log('Google Cloud IoT Core MQTT 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'], + type: 'string' + }, + num_messages: { + default: 100, + description: 'Number of messages to publish.', + requiresArg: true, + type: 'number' + }, + mqtt_bridge_hostname: { + default: 'mqtt.googleapis.com', + description: 'MQTT bridge hostname.', + requiresArg: true, + type: 'string' + }, + mqtt_bridge_port: { + default: 8883, + description: 'MQTT bridge port.', + requiresArg: true, + type: 'number' + }, + message_type: { + default: 'events', + description: 'Message type to publish.', + requiresArg: true, + choices: ['events', 'state'], + type: 'string' + } + }) + .example(`node $0 cloudiot_mqtt_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. @@ -78,18 +111,19 @@ function createJwt (projectId, privateKeyFile, algorithm) { // Publish numMessages messages asynchronously, starting from message // messageCount. function publishAsync (messageCount, numMessages) { - const payload = `${program.registry_id}/${program.device_id}-payload-${messageCount}`; + const payload = `${argv.registry_id}/${argv.device_id}-payload-${messageCount}`; // Publish "payload" to the MQTT topic. qos=1 means at least once delivery. // Cloud IoT Core also supports qos=0 for at most once delivery. console.log('Publishing message:', payload); client.publish(mqttTopic, payload, { qos: 1 }); + const delayMs = argv.message_type === 'events' ? 1000 : 2000; if (messageCount < numMessages) { // If we have published fewer than numMessage messages, publish payload // messageCount + 1 in 1 second. setTimeout(function () { publishAsync(messageCount + 1, numMessages); - }, 1000); + }, delayMs); } else { // Otherwise, close the connection. console.log('Closing connection to MQTT. Goodbye!'); @@ -99,34 +133,35 @@ function publishAsync (messageCount, numMessages) { // The mqttClientId is a unique string that identifies this device. For Google // Cloud IoT Core, it must be in the format below. -const mqttClientId = `projects/${program.project_id}/locations/${program.cloud_region}/registries/${program.registry_id}/devices/${program.device_id}`; +const mqttClientId = `projects/${argv.project_id}/locations/${argv.cloud_region}/registries/${argv.registry_id}/devices/${argv.device_id}`; // With Google Cloud IoT Core, the username field is ignored, however it must be // non-empty. The password field is used to transmit a JWT to authorize the // device. The "mqtts" protocol causes the library to connect using SSL, which // is required for Cloud IoT Core. const connectionArgs = { - host: program.mqtt_bridge_hostname, - port: program.mqtt_bridge_port, + host: argv.mqtt_bridge_hostname, + port: argv.mqtt_bridge_port, clientId: mqttClientId, username: 'unused', - password: createJwt(program.project_id, program.private_key_file, program.algorithm), + password: createJwt(argv.project_id, argv.private_key_file, argv.algorithm), protocol: 'mqtts' }; // Create a client, and connect to the Google MQTT bridge. const client = mqtt.connect(connectionArgs); -// The MQTT topic that this device will publish telemetry data to. The MQTT -// topic name is required to be in the format below. Note that this is not the -// same as the device registry's Cloud Pub/Sub topic. -const mqttTopic = `/devices/${program.device_id}/events`; +// The MQTT topic that this device will publish data to. The MQTT +// topic name is required to be in the format below. The topic name must end in +// 'state' to publish state and 'events' to publish telemetry. Note that this is +// not the same as the device registry's Cloud Pub/Sub topic. +const mqttTopic = `/devices/${argv.device_id}/${argv.message_type}`; client.on('connect', () => { console.log('connect', arguments); // After connecting, publish 'num_messages' messagse asynchronously, at a rate - // of 1 per second. - publishAsync(1, program.num_messages); + // of 1 per second for telemetry events and 1 every 2 seconds for states. + publishAsync(1, argv.num_messages); }); client.on('close', () => { diff --git a/iot/mqtt_example/package.json b/iot/mqtt_example/package.json index 137a303854..4309a7540b 100644 --- a/iot/mqtt_example/package.json +++ b/iot/mqtt_example/package.json @@ -4,7 +4,7 @@ "description": "MQTT Example for Google Cloud IoT Core using NodeJS.", "main": "cloudiot_mqtt_example_nodejs.js", "dependencies": { - "commander": "2.9.0", + "yargs": "8.0.2", "jsonwebtoken": "7.4.1", "mqtt": "2.7.2" }, From d4ecd50d1e6ebe4966666638dbb467dd3cfd18dd Mon Sep 17 00:00:00 2001 From: Gus Class Date: Mon, 25 Sep 2017 15:53:07 -0700 Subject: [PATCH 4/9] Removes extra code and discovery doc --- iot/http_example/cloudiot.rest.json | 279 ------------------------ iot/http_example/http_client_example.js | 112 ---------- 2 files changed, 391 deletions(-) delete mode 100644 iot/http_example/cloudiot.rest.json delete mode 100644 iot/http_example/http_client_example.js diff --git a/iot/http_example/cloudiot.rest.json b/iot/http_example/cloudiot.rest.json deleted file mode 100644 index 6b2dea920b..0000000000 --- a/iot/http_example/cloudiot.rest.json +++ /dev/null @@ -1,279 +0,0 @@ -{ - "rootUrl": "https://cloudiot-device.googleapis.com/", - "basePath": "", - "ownerDomain": "google.com", - "name": "cloudiot", - "batchPath": "batch", - "revision": "20170909", - "id": "cloudiot:v1beta1", - "documentationLink": "https://cloud.google.com/iot", - "title": "Google Cloud IoT API", - "discoveryVersion": "v1", - "ownerName": "Google", - "version_module": true, - "resources": { - "projects": { - "resources": { - "locations": { - "resources": { - "registries": { - "resources": { - "devices": { - "methods": { - "setState": { - "flatPath": "v1beta1/projects/{projectsId}/locations/{locationsId}/registries/{registriesId}/devices/{devicesId}:setState", - "id": "cloudiot.projects.locations.registries.devices.setState", - "path": "v1beta1/{+name}:setState", - "description": "Sets the state of a device.", - "request": { - "$ref": "HttpSetDeviceStateRequest" - }, - "response": { - "$ref": "Empty" - }, - "parameterOrder": [ - "name" - ], - "httpMethod": "POST", - "parameters": { - "name": { - "type": "string", - "required": true, - "pattern": "^projects/[^/]+/locations/[^/]+/registries/[^/]+/devices/[^/]+$", - "location": "path", - "description": "The name of the device. For example,\n`projects/p0/locations/us-central1/registries/registry0/devices/device0`." - } - } - }, - "getConfig": { - "response": { - "$ref": "HttpDeviceConfig" - }, - "parameterOrder": [ - "name" - ], - "httpMethod": "GET", - "parameters": { - "name": { - "location": "path", - "description": "The name of the device. For example,\n`projects/p0/locations/us-central1/registries/registry0/devices/device0`.", - "type": "string", - "required": true, - "pattern": "^projects/[^/]+/locations/[^/]+/registries/[^/]+/devices/[^/]+$" - }, - "localVersion": { - "type": "string", - "location": "query", - "format": "int64", - "description": "If zero, returns the current device configuration from Cloud IoT Core.\nIf nonzero, specifies the local version of the configuration on the device.\nThe server returns config data only if a higher (newer) version is\navailable from Cloud IoT Core.\nIf this value is higher than the latest version available in Cloud IoT\nCore, returns an `OUT_OF_RANGE` error." - } - }, - "flatPath": "v1beta1/projects/{projectsId}/locations/{locationsId}/registries/{registriesId}/devices/{devicesId}/config", - "id": "cloudiot.projects.locations.registries.devices.getConfig", - "path": "v1beta1/{+name}/config", - "description": "Gets the configuration of a device." - }, - "publishEvent": { - "httpMethod": "POST", - "parameterOrder": [ - "name" - ], - "response": { - "$ref": "HttpPublishEventResponse" - }, - "parameters": { - "name": { - "type": "string", - "required": true, - "pattern": "^projects/[^/]+/locations/[^/]+/registries/[^/]+/devices/[^/]+$", - "location": "path", - "description": "The name of the device. For example,\n`projects/p0/locations/us-central1/registries/registry0/devices/device0`." - } - }, - "flatPath": "v1beta1/projects/{projectsId}/locations/{locationsId}/registries/{registriesId}/devices/{devicesId}:publishEvent", - "path": "v1beta1/{+name}:publishEvent", - "id": "cloudiot.projects.locations.registries.devices.publishEvent", - "description": "Publishes a telemetry event for a device.", - "request": { - "$ref": "HttpPublishEventRequest" - } - } - } - } - } - } - } - } - } - } - }, - "parameters": { - "upload_protocol": { - "location": "query", - "description": "Upload protocol for media (e.g. \"raw\", \"multipart\").", - "type": "string" - }, - "prettyPrint": { - "default": "true", - "type": "boolean", - "location": "query", - "description": "Returns response with indentations and line breaks." - }, - "fields": { - "location": "query", - "description": "Selector specifying which fields to include in a partial response.", - "type": "string" - }, - "uploadType": { - "type": "string", - "location": "query", - "description": "Legacy upload protocol for media (e.g. \"media\", \"multipart\")." - }, - "callback": { - "type": "string", - "location": "query", - "description": "JSONP" - }, - "$.xgafv": { - "type": "string", - "enumDescriptions": [ - "v1 error format", - "v2 error format" - ], - "location": "query", - "enum": [ - "1", - "2" - ], - "description": "V1 error format." - }, - "alt": { - "type": "string", - "enumDescriptions": [ - "Responses with Content-Type of application/json", - "Media download with context-dependent Content-Type", - "Responses with Content-Type of application/x-protobuf" - ], - "location": "query", - "description": "Data format for response.", - "default": "json", - "enum": [ - "json", - "media", - "proto" - ] - }, - "key": { - "location": "query", - "description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.", - "type": "string" - }, - "access_token": { - "location": "query", - "description": "OAuth access token.", - "type": "string" - }, - "quotaUser": { - "type": "string", - "location": "query", - "description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters." - }, - "pp": { - "default": "true", - "type": "boolean", - "location": "query", - "description": "Pretty-print response." - }, - "oauth_token": { - "location": "query", - "description": "OAuth 2.0 token for the current user.", - "type": "string" - }, - "bearer_token": { - "type": "string", - "location": "query", - "description": "OAuth bearer token." - } - }, - "schemas": { - "HttpPublishEventResponse": { - "type": "object", - "properties": {}, - "id": "HttpPublishEventResponse", - "description": "Response for `PublishEvent`." - }, - "HttpSetDeviceStateRequest": { - "type": "object", - "properties": { - "state": { - "$ref": "HttpDeviceState", - "description": "The device state." - } - }, - "id": "HttpSetDeviceStateRequest", - "description": "Request for `SetDeviceState`." - }, - "HttpDeviceConfig": { - "type": "object", - "properties": { - "version": { - "type": "string", - "format": "int64", - "description": "The version of the configuration in Cloud IoT Core." - }, - "binaryData": { - "format": "byte", - "description": "Data in binary format.", - "type": "string" - } - }, - "id": "HttpDeviceConfig", - "description": "The device configuration obtained from Cloud IoT Core." - }, - "HttpDeviceState": { - "type": "object", - "properties": { - "binaryData": { - "type": "string", - "format": "byte", - "description": "Data in binary format." - } - }, - "id": "HttpDeviceState", - "description": "The device state reported to Cloud IoT Core." - }, - "HttpPublishEventRequest": { - "type": "object", - "properties": { - "subFolder": { - "type": "string", - "description": "Optional subfolder for the telemetry event. This can be used to classify\ntypes of events, and is included in the Pub/Sub message attributes." - }, - "binaryData": { - "format": "byte", - "description": "Payload data in binary format.", - "type": "string" - } - }, - "id": "HttpPublishEventRequest", - "description": "Request for `PublishEvent`." - }, - "Empty": { - "description": "A generic empty message that you can re-use to avoid defining duplicated\nempty messages in your APIs. A typical example is to use it as the request\nor the response type of an API method. For instance:\n\n service Foo {\n rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);\n }\n\nThe JSON representation for `Empty` is empty JSON object `{}`.", - "type": "object", - "properties": {}, - "id": "Empty" - } - }, - "icons": { - "x32": "http://www.google.com/images/icons/product/search-32.gif", - "x16": "http://www.google.com/images/icons/product/search-16.gif" - }, - "protocol": "rest", - "version": "v1beta1", - "baseUrl": "https://cloudiot-device.googleapis.com/", - "canonicalName": "Cloud Iot", - "servicePath": "", - "description": "Registers and manages IoT (Internet of Things) devices that connect to the Google Cloud Platform.\n", - "kind": "discovery#restDescription" -} diff --git a/iot/http_example/http_client_example.js b/iot/http_example/http_client_example.js deleted file mode 100644 index d467682ec5..0000000000 --- a/iot/http_example/http_client_example.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * 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. - */ - -/** NodeJS sample of connecting to Google Cloud IoT Core via HTTP. */ - -'use strict'; - -const fs = require('fs'); -const google = require('googleapis'); -const jwt = require('jsonwebtoken'); - -// 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 messages asynchronously, starting from message -// messageCount. -function publishAsync (bearer, client, messageCount, message, projectId, cloudRegion, registryId, deviceId) { - const path = 'projects/' + projectId + '/locations/' + cloudRegion + - '/registries/' + registryId + '/devices/' + deviceId; - - // Publish "payload" to the MQTT topic. qos=1 means at least once delivery. - // Cloud IoT Core also supports qos=0 for at most once delivery. - console.log('Publishing to:', path); - console.log('Publishing message:', message); - - // TODO: Publish message hurr - let request = { - name: path, - headers : [{'Authorization': 'Bearer ' + bearer}] - }; - client.projects.locations.registries.devices.publishEvent(request, function(res){console.log(res);}); -} - -require(`yargs`) // eslint-disable-line - .demand(1) - .options({ - deviceId: { - alias: 'd', - description: 'The ID used to register this device.', - type: 'string' - }, - cloudRegion: { - alias: 'c', - default: 'us-central1', - type: 'string' - }, - projectId: { - alias: 'p', - 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.', - type: 'string' - }, - registryId: { - alias: 'r', - description: 'The identifier for your device registry.', - type: 'string' - }, - rsaPath: { - alias: 'rsa', - description: 'The file path for your RSA private key.', - requiresArg: true, - type: 'string' - } - }) - .command( - `publish `, - `publishes a message`, - {}, - (opts) => { - let bearer = createJwt(opts.projectId, opts.rsaPath, 'RS256'); - console.log(bearer); - google.discoverAPI(`cloudiot.rest.json`, - {auth: bearer}, (err, client) => { - if (err) { - console.log('Error during API discovery', err); - return undefined; - } - // cb(client) - publishAsync(bearer, client, opts.messageCount, opts.message, - opts.projectId, opts.cloudRegion, opts.registryId, opts.deviceId); - }); - }) - .wrap(120) - .recommendCommands() - .epilogue(`For more information, see https://cloud.google.com/iot-core/docs`) - .help() - .strict() - .argv; From 915d1971a6c04a885286097d694230631f6e6005 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Mon, 25 Sep 2017 18:14:50 -0700 Subject: [PATCH 5/9] Add product link to readme, options to yargs. --- iot/http_example/README.md | 10 ++++++---- iot/http_example/cloudiot_http_example_nodejs.js | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/iot/http_example/README.md b/iot/http_example/README.md index 24ed0fc4b8..a4e2ed9b55 100644 --- a/iot/http_example/README.md +++ b/iot/http_example/README.md @@ -2,12 +2,13 @@ # Google Cloud IoT Core NodeJS HTTP example -This sample app publishes messages to Cloud Pub/Sub or states using the HTTP -bridge provided as part of Google Cloud IoT Core. +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: @@ -29,10 +30,11 @@ Options: --registry_id Cloud IoT Core registry id. --device_id Cloud IoT Core device id. --private_key_file Path to private key file. - --algorithm Encryption algorithm to generate the JWT. Either RS256 or ES256 + --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. + --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 diff --git a/iot/http_example/cloudiot_http_example_nodejs.js b/iot/http_example/cloudiot_http_example_nodejs.js index 657caa9797..802e53684b 100644 --- a/iot/http_example/cloudiot_http_example_nodejs.js +++ b/iot/http_example/cloudiot_http_example_nodejs.js @@ -53,7 +53,7 @@ var argv = require(`yargs`) type: 'string' }, algorithm: { - description: 'Encryption algorithm to generate the JWT.', + description: 'Encryption algorithm to generate the RSA or EC JWT.', requiresArg: true, demandOption: true, choices: ['RS256', 'ES256'], From 562e952c014c249c8f33bf22e94142bdc2038f86 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Tue, 26 Sep 2017 12:45:27 -0700 Subject: [PATCH 6/9] Add license to package.json --- iot/http_example/package.json | 2 ++ iot/mqtt_example/package.json | 2 ++ 2 files changed, 4 insertions(+) diff --git a/iot/http_example/package.json b/iot/http_example/package.json index 5fb97dadae..89c6b77985 100644 --- a/iot/http_example/package.json +++ b/iot/http_example/package.json @@ -2,6 +2,8 @@ "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", diff --git a/iot/mqtt_example/package.json b/iot/mqtt_example/package.json index 4309a7540b..6a8c79eaf5 100644 --- a/iot/mqtt_example/package.json +++ b/iot/mqtt_example/package.json @@ -3,6 +3,8 @@ "version": "0.0.1", "description": "MQTT Example for Google Cloud IoT Core using NodeJS.", "main": "cloudiot_mqtt_example_nodejs.js", + "license": "Apache-2.0", + "author": "Google Inc.", "dependencies": { "yargs": "8.0.2", "jsonwebtoken": "7.4.1", From 9e8af84bcf754610ea0665dfb161e321cabb7b06 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Tue, 26 Sep 2017 12:51:59 -0700 Subject: [PATCH 7/9] Adds includecode blocks to MQTT / HTTP --- iot/http_example/cloudiot_http_example_nodejs.js | 9 ++++++++- iot/mqtt_example/cloudiot_mqtt_example_nodejs.js | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/iot/http_example/cloudiot_http_example_nodejs.js b/iot/http_example/cloudiot_http_example_nodejs.js index 802e53684b..8dc2baa0f6 100644 --- a/iot/http_example/cloudiot_http_example_nodejs.js +++ b/iot/http_example/cloudiot_http_example_nodejs.js @@ -14,10 +14,11 @@ */ '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`) @@ -89,6 +90,7 @@ var argv = require(`yargs`) // 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 @@ -101,10 +103,12 @@ function createJwt (projectId, privateKeyFile, algorithm) { 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); @@ -140,7 +144,9 @@ function publishAsync (messageCount, numMessages) { } }); } +// [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}`; @@ -153,3 +159,4 @@ const authToken = createJwt(argv.project_id, argv.private_key_file, argv.algorit // Publish messages. publishAsync(1, argv.num_messages); +// [END iot_run_http] diff --git a/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js b/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js index e4e1ea2843..4fa1597d6d 100644 --- a/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js +++ b/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js @@ -15,9 +15,12 @@ 'use strict'; + +// [START iot_mqtt_include] const fs = require('fs'); const jwt = require('jsonwebtoken'); const mqtt = require('mqtt'); +// [END iot_mqtt_include] console.log('Google Cloud IoT Core MQTT example.'); var argv = require(`yargs`) @@ -95,6 +98,7 @@ var argv = require(`yargs`) // Create a Cloud IoT Core JWT for the given project id, signed with the given // private key. +// [START iot_mqtt_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 @@ -107,9 +111,11 @@ function createJwt (projectId, privateKeyFile, algorithm) { const privateKey = fs.readFileSync(privateKeyFile); return jwt.sign(token, privateKey, { algorithm: algorithm }); } +// [END iot_mqtt_jwt] // Publish numMessages messages asynchronously, starting from message // messageCount. +// [START iot_mqtt_publish] function publishAsync (messageCount, numMessages) { const payload = `${argv.registry_id}/${argv.device_id}-payload-${messageCount}`; // Publish "payload" to the MQTT topic. qos=1 means at least once delivery. @@ -130,7 +136,10 @@ function publishAsync (messageCount, numMessages) { client.end(); } } +// [END iot_mqtt_publish] + +// [START iot_mqtt_run] // The mqttClientId is a unique string that identifies this device. For Google // Cloud IoT Core, it must be in the format below. const mqttClientId = `projects/${argv.project_id}/locations/${argv.cloud_region}/registries/${argv.registry_id}/devices/${argv.device_id}`; @@ -178,3 +187,4 @@ client.on('packetsend', () => { // Once all of the messages have been published, the connection to Google Cloud // IoT will be closed and the process will exit. See the publishAsync method. +// [END iot_mqtt_run] From ddc42313f991225b4b78bae82f00f2ae5d643d82 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Tue, 26 Sep 2017 13:10:06 -0700 Subject: [PATCH 8/9] Fix lint --- iot/manager/manager.js | 2 +- iot/mqtt_example/cloudiot_mqtt_example_nodejs.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/iot/manager/manager.js b/iot/manager/manager.js index 8e7c46cc2d..53dad96b9d 100644 --- a/iot/manager/manager.js +++ b/iot/manager/manager.js @@ -126,7 +126,7 @@ function createRegistry (client, registryId, projectId, cloudRegion, parent: parentName, resource: { eventNotificationConfigs: [{ - "pubsubTopicName": pubsubTopic + 'pubsubTopicName': pubsubTopic }], 'id': registryId } diff --git a/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js b/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js index 4fa1597d6d..8bdb60450e 100644 --- a/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js +++ b/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js @@ -15,7 +15,6 @@ 'use strict'; - // [START iot_mqtt_include] const fs = require('fs'); const jwt = require('jsonwebtoken'); @@ -138,7 +137,6 @@ function publishAsync (messageCount, numMessages) { } // [END iot_mqtt_publish] - // [START iot_mqtt_run] // The mqttClientId is a unique string that identifies this device. For Google // Cloud IoT Core, it must be in the format below. From 930b9fdc13fa0b0e3dc511271287cac090d1bd53 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Tue, 26 Sep 2017 14:20:31 -0700 Subject: [PATCH 9/9] Adds comment on state vs event frequency --- iot/http_example/cloudiot_http_example_nodejs.js | 1 + 1 file changed, 1 insertion(+) diff --git a/iot/http_example/cloudiot_http_example_nodejs.js b/iot/http_example/cloudiot_http_example_nodejs.js index 8dc2baa0f6..101676cb27 100644 --- a/iot/http_example/cloudiot_http_example_nodejs.js +++ b/iot/http_example/cloudiot_http_example_nodejs.js @@ -128,6 +128,7 @@ function publishAsync (messageCount, numMessages) { 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) {