diff --git a/iot/http_example/cloudiot_http_example.js b/iot/http_example/cloudiot_http_example.js index 9b84c123dd..b3907aacd7 100644 --- a/iot/http_example/cloudiot_http_example.js +++ b/iot/http_example/cloudiot_http_example.js @@ -172,7 +172,7 @@ function publishAsync (authToken, messageCount, numMessages) { authToken = createJwt(argv.projectId, argv.privateKeyFile, argv.algorithm); } - publishAsync(messageCount + 1, numMessages); + publishAsync(authToken, messageCount + 1, numMessages); }, delayMs); } }); diff --git a/iot/http_example/resources/rsa_cert.pem b/iot/http_example/resources/rsa_cert.pem index d81ae7c4f4..db7c7fa344 100644 --- a/iot/http_example/resources/rsa_cert.pem +++ b/iot/http_example/resources/rsa_cert.pem @@ -1,18 +1,18 @@ -----BEGIN CERTIFICATE----- -MIIC9TCCAd2gAwIBAgIJALM44e3ivEWkMA0GCSqGSIb3DQEBCwUAMBExDzANBgNV -BAMMBnVudXNlZDAeFw0xNzEyMDcwMDQ1MjdaFw0yNzEyMDUwMDQ1MjdaMBExDzAN -BgNVBAMMBnVudXNlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL4w -BxHuEyYdbiwKiD8yXY7vYpcygeOQ4/ZdEg3wCB2OuYcaFRcCuqLcTLMnuzdcL+y3 -HBjWkrRW658cg3NG93Vj0iwSrga6u24CGBNYV+h8MBvwaDxk+uubnd5M/Q2OyL1J -GiMxQ1blR/71Hr5hhqaQZ2+qOF6kuf1m9rLUtMUJwOKp/PjPDmy654ZGsFWFSZmy -eRpNzmGU+KJg0o+Qf+sm75a8gQZ8AsrqveW0S/8o+zAjD0SkPcd01QBmYzQhjbi/ -LGGITrzbaB3ld9umJBIcXfnYPYisJfwSsT/jFwiXhrhpxNNaIaKlTzlQIt5l8bSs -HXzJBbuIg5Jb/SyIEpkCAwEAAaNQME4wHQYDVR0OBBYEFOfaQTUVAoNb6fc7qzzl -uKyHGrCYMB8GA1UdIwQYMBaAFOfaQTUVAoNb6fc7qzzluKyHGrCYMAwGA1UdEwQF -MAMBAf8wDQYJKoZIhvcNAQELBQADggEBALKKDtiV1YV8k0YsNdiIXRlS3jsuoGuI -VVBrvDGz5Hel0rH9YmmfPS/Yn08kk3DF8Uynr4Xo1Zt9hmhgoq3ZoWm7MIP1+a9s -WyACyEMhVQSCzQrexRvG5ElpHx/vNjbcwiBkE5urlIvMBVt+BRRNKMNWq6F9ae63 -FxRp7CtNFSbibtLJuPgCs6qoNs0nlt2FPsNvs7jpPipj69o+egVckvQjAyppirWO -+jO5hCLy7EahLz2wCn90z0Xf9lhOZni9meaV1Vy3CHHg6jwIB8/XlRaHFrOGMGXg -h8eQqsmpk9/3o8pv00yj6Hkq+swVg7Rg9FZaUiOv/HO/J7stWU7qPbI= +MIIC+DCCAeCgAwIBAgIJAMCU5DifwbH2MA0GCSqGSIb3DQEBCwUAMBExDzANBgNV +BAMMBnVudXNlZDAeFw0xNzEyMjAxODU0MzRaFw0yMDA5MTUxODU0MzRaMBExDzAN +BgNVBAMMBnVudXNlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMc2 +pktp/RV1g7omV5CyNi1pQugZvxn6TciLqcjHP1XYfU6mnl7ySvH1xbkzaAlL/8Y8 +IqSFF4eI14qI71OT+7eLq6Ljj8j3XTqAtSANMSpi5KIRVQsa0h0rbRt7Xt1TeEd1 +ErVdzdRKrKvn7wM6UID0QGxC+1gpHGo3Aaz9Q4tVxAeWM6skCDh9WHea0HNj8MPA +hS4fc83PitfITOR9eCP2HW83HXu32yFpCU96WIQGSDfHfN7vD7mDXN+iZa02GJ2K +NBiwQa3LmHmcoaru2yTB9OTf1wGRY/QETrkmA+7enq7p2RQSe6uXu97k9UgV1Wes +c4QcJ/BAsLbaWB/f2h8CAwEAAaNTMFEwHQYDVR0OBBYEFCeNUvOsKm+BB9VgO1l1 +QlwS5rIEMB8GA1UdIwQYMBaAFCeNUvOsKm+BB9VgO1l1QlwS5rIEMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAG/40RXw9lojT7aTJq7f196cL1v +qYiVNeZFE2Ki0iVjzDOk1SOXRqy8v8N4ay32gD+TZOg1Yy8loscKhGCnWIRepQC8 +1pyh2ZGHysICfHyxaKQgIMUzINSRI0E4bYt7kro497AodKN4P3QBH5Q6U7Ra3hG2 +b1dXOBVL0kUCKOnFuByEF2Zhytyhl3bTrm/W6dvhHBGTpB2RXwnldPnyEA4rmdyk +Ui9LYmEhWeizlBpw6jcXcd9qABrUMlxBt24DB3nKo/b8h6K4gXYfgO8yYbgsB+E6 +5wZGLi6nP5O98M+VmQZ5CBZSZid+sfK6eoT+Ca5vn7jSTQGzAIapc57dJEQ= -----END CERTIFICATE----- diff --git a/iot/http_example/resources/rsa_private.pem b/iot/http_example/resources/rsa_private.pem index 06a66e3d40..d627d8a8c5 100644 --- a/iot/http_example/resources/rsa_private.pem +++ b/iot/http_example/resources/rsa_private.pem @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC+MAcR7hMmHW4s -Cog/Ml2O72KXMoHjkOP2XRIN8AgdjrmHGhUXArqi3EyzJ7s3XC/stxwY1pK0Vuuf -HINzRvd1Y9IsEq4GurtuAhgTWFfofDAb8Gg8ZPrrm53eTP0Njsi9SRojMUNW5Uf+ -9R6+YYamkGdvqjhepLn9Zvay1LTFCcDiqfz4zw5suueGRrBVhUmZsnkaTc5hlPii -YNKPkH/rJu+WvIEGfALK6r3ltEv/KPswIw9EpD3HdNUAZmM0IY24vyxhiE6822gd -5XfbpiQSHF352D2IrCX8ErE/4xcIl4a4acTTWiGipU85UCLeZfG0rB18yQW7iIOS -W/0siBKZAgMBAAECggEAfwLmBdRfl2m6JNFX0hSZpJY72kuRsN8XTnUzVHmDgfHJ -9u61POvGpnLHCjIzdjIrk0NqETBjQup1aooJQ1gWdKAYQPSsobPc7geZ+nlaI9mj -61Su1/58EBKZ6Faz/HTpnHeQbAY/OW3fmeYrBOtumBgB6/HauWH7D77Oa/lfS+Ij -4f6OVAxevsi6PUtNmNtBwk5S0lZl9SFcKeHurVindquX9vWZjBEEFtNXazJttIJS -z9KX29VYwoLfflIKaUKckn8X+wYc+3u3BvH8zJpd60yQ6MSo7Sb1XkxT9549m+JW -Cb+i1K7MC/yQo4mvDtAQIVBh8p8qpd4VjpBwMuUbgQKBgQDexuAaLO3adSYFXGwW -nom6Mz/ImYcpxYo0ouAR1talbmF5/oKl9Tcwh7l1eDHfe70gfeP+g4uwAcc1hx3a -ZtXusrJFBktFezlFQnZXaE5ppryrFWeu0he0RYLAVxnL6IlP9dYQhVsTZm+7uX5d -UP7aZtmOU9ZTEsAoqvjJQXvaCQKBgQDajPebXOxIUj8ffGTeiPZczTwXux04caDC -eFKSCbAlHWgG7mR4P3fQONfEGWNHF0CxBSrew9CHmKdPyiISaExCfUaUWDDCPQCp -UE5VAHPdjSlb4lqi+cyNVlJxBJGONtyYkbQNd6N9GHMnBS8jZi7zf8VzIXpeExA4 -n79Aml/YEQKBgDFrGId19AWD+z0xNWEHJjJB8CJFvHANvAzVHLOYXuEvzTvMs5qw -/N8tHHzsftO+lUPB6XOqJrCSlGhRYtPx//8FcPpS3Ru6rAerKKlXIB3buPqSsv9a -55s72DdmmvhayysLs8LSclOpY5vXGCsHLqGwMw6Zlm+zNyFOXAX5GspRAoGAaJMx -W68ABK8OM0OzhGQm9kriKTzIg5yjXspyQBzQo0HJ6B8kBgHgk8rPO68mOPsgYlPl -qogp/OgHjv9ahFJRwzLslckJM7g628loYfYAew+zrZrG4dsDjNG0Sw3zlAgeUAbQ -D+2iVhZf61josFiRuMP3t9paEi+vAFk4C3KSz/ECgYBpi1akpIzsYehW5uOL7Jhw -Hay5eshQ4vmHYuhDnn3gtT3h6J7TMwWs9pOygBG1I1b7GJ+tp4BZWJ2PmI7P8s45 -jdI99WODHwv03lAzjLwigoqDUDduaYqXcGghcGht5Sknkl2uYDChwLtI5JdBZ9/x -8h9dE9oAiH/KTzhPmK1E1Q== +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDHNqZLaf0VdYO6 +JleQsjYtaULoGb8Z+k3Ii6nIxz9V2H1Opp5e8krx9cW5M2gJS//GPCKkhReHiNeK +iO9Tk/u3i6ui44/I9106gLUgDTEqYuSiEVULGtIdK20be17dU3hHdRK1Xc3USqyr +5+8DOlCA9EBsQvtYKRxqNwGs/UOLVcQHljOrJAg4fVh3mtBzY/DDwIUuH3PNz4rX +yEzkfXgj9h1vNx17t9shaQlPeliEBkg3x3ze7w+5g1zfomWtNhidijQYsEGty5h5 +nKGq7tskwfTk39cBkWP0BE65JgPu3p6u6dkUEnurl7ve5PVIFdVnrHOEHCfwQLC2 +2lgf39ofAgMBAAECggEAa8LBI8w6jfAaiIiTF39/VQM6oQjEWg4PI2uBNU2qkgpg +hjoVMzEHn2keQrdvEJBe1GHVL3+bIcLBLXwzUdeb80aHWr6UGdbNRIc48xSPwujp +2k1b3nzsqcDC7XnH8Btl1embbCZI4Axg6UC2Bt31jDwT2TxGcAMVDtOggW4iqmyO +8v+J0j2tl+J/d7K49VtYkN5epoNn+gxpvsMbaDNmolE3Pcg2hr/h5tkzTWB53bwW +7K89FczsTvhCs8LWA/R+kKX9O9jpeUpbE6afAGdU6JCAYohClva1SIpksijV2HFg +SYZJ1QTjKfV11iBvYpj4wXGcuXgui4WL/0xCCXZt2QKBgQDq2PdYgpmJJFZHNRr9 +QlgZSAsuu16t49ezAxlm4Q8orAht0Nq02l6eMHbsjV3lRmyo0vK2byMQdhaSfweB +eT7sD/TSmT7jqGEqW+jtf8mNQpAeQhJJKJ8AbERF5heKcMwk6CbFAUipc1TgCiuZ +335SyJRLUU1yUSvGqbMevUKAJQKBgQDZKAuyItjZFlyU2dVXYC8tALWjIaLwSsXw +SbCfhlMLUt7DTb8+V834ygPtEKagcaw4QwcB+brH6dO1vwL3KQWMOeZcUkXg8NCW +F71J84LLmUsZQkHx8HqLG6o/DUq+M1SuklMROAI14ZKtL0xMW/lIgICngENanUS8 +m/kT+zIr8wKBgQCAkBgQDybICellf+/vdvNNm7/rbE1vekxpxieQx2IKDOtsWqpv +2R91D+j6pIUjb4MqhJi0QHD8oPjIZ9wPB/XnOvD2nUftTw8qJjiEU7FdsHO+7MKf +5CnvDFXvwf9fHVLUXXZj4MkLYoSzRT4LNWstHHr6JF72VvNVBS92g3s1NQKBgQDN +Rw3HVymmDX13OHR0JHh0Ejeh/ioI/KYgf4h3RIM8MRpjhRtD07zhchZEA3mAMfSb +OsFlHK10TZXUOLyU6DJWXGVm4F8uRHbpa4eEYK1DyaU0O24bRziQR5aoA+ij4+Ip +JCliHY/5X0NIc22swUTlRJRZwuXowsI3iTnrqYnEAQKBgQC9AhF+S6d7uIsEOg7I +Kv8ndASx7Nfsy6OQfOltgLArV4w2POQjbPYsLLrz4s82mSpS205w2OzCfcUXKLxD +mswC0Bs/U3UmJW0d1oWfUm28FjGbzGYGfOLm1dPuyNW32FbZXvO4+jaIemjtg2U7 +avJDFdpNwVz/bVg0HWQWNP/C0A== -----END PRIVATE KEY----- diff --git a/iot/http_example/system-test/cloudiot_http_example_test.js b/iot/http_example/system-test/cloudiot_http_example_test.js index c689e8c06e..e3513edf9d 100644 --- a/iot/http_example/system-test/cloudiot_http_example_test.js +++ b/iot/http_example/system-test/cloudiot_http_example_test.js @@ -51,6 +51,7 @@ test.after.always(async () => { test(`should receive configuration message`, async (t) => { const localDevice = `test-rsa-device`; const localRegName = `${registryName}-rsa256`; + await tools.runAsync(`${helper} setupIotTopic ${topicName}`, cwd); await tools.runAsync( `${helper} createRegistry ${localRegName} ${topicName}`, cwd); @@ -71,6 +72,7 @@ test(`should receive configuration message`, async (t) => { test(`should send event message`, async (t) => { const localDevice = `test-rsa-device`; const localRegName = `${registryName}-rsa256`; + await tools.runAsync(`${helper} setupIotTopic ${topicName}`, cwd); await tools.runAsync( `${helper} createRegistry ${localRegName} ${topicName}`, cwd); diff --git a/iot/manager/README.md b/iot/manager/README.md index 2738227ad5..eda812987b 100644 --- a/iot/manager/README.md +++ b/iot/manager/README.md @@ -30,6 +30,7 @@ Run the following command to install the library dependencies for NodeJS: listDevices Lists the devices in a given registry. patchEs256 Patches a device with ES256 authorization credentials. patchRsa256 Patches a device with RSA256 authentication credentials. + setConfig Sets a devices configuration to the specified data. Options: --projectId, -p The Project ID to use. Defaults to the value of the GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT @@ -39,18 +40,21 @@ Run the following command to install the library dependencies for NodeJS: --cloudRegion, -c [string] [default: "us-central1"] Examples: - node manager.js createEs256Device my-es-device my-registry ../ec_public.pem --serviceAccount=$HOME/creds_iot.json + node manager.js createEs256Device my-es-device my-registry ../ec_public.pem node manager.js createRegistry my-registry my-iot-topic --serviceAccount=$HOME/creds_iot.json - --api_key=abc123zz --project_id=my-project-id - node manager.js createRsa256Device my-rsa-device my-registry ../rsa_cert.pem --serviceAccount=$HOME/creds_iot.json - node manager.js createUnauthDevice my-device my-registry --serviceAccount=$HOME/creds_iot.json - node manager.js deleteDevice my-device my-registry --serviceAccount=$HOME/creds_iot.json - node manager.js deleteRegistry my-device my-registry --serviceAccount=$HOME/creds_iot.json - node manager.js getDevice my-device my-registry --serviceAccount=$HOME/creds_iot.json - node manager.js listDevices my-node-registry --serviceAccount=$HOME/creds_iot.json - node manager.js patchRsa256 my-device my-registry ../rsa_cert.pem --serviceAccount=$HOME/creds_iot.json - node manager.js patchEs256 my-device my-registry ../ec_public.pem --serviceAccount=$HOME/creds_iot.json - node manager.js setupTopic my-iot-topic --serviceAccount=$HOME/creds_iot.json --project_id=my-project-id + node manager.js createRsa256Device my-rsa-device my-registry ../rsa_cert.pem + node manager.js createUnauthDevice my-device my-registry + node manager.js deleteDevice my-device my-registry + node manager.js deleteRegistry my-device my-registry + node manager.js getDevice my-device my-registry + node manager.js getDeviceState my-device my-registry + node manager.js getRegistry my-registry + node manager.js listDevices my-node-registry + node manager.js listRegistries + node manager.js patchRsa256 my-device my-registry ../rsa_cert.pem + node manager.js patchEs256 my-device my-registry ../ec_public.pem + node manager.js setConfig my-device my-registry "test" 0 + node manager.js setupTopic my-iot-topic --serviceAccount=$HOME/creds_iot.json --projectId=my-project-id For more information, see https://cloud.google.com/iot-core/docs diff --git a/iot/manager/manager.js b/iot/manager/manager.js index a4da810365..9394a68e17 100644 --- a/iot/manager/manager.js +++ b/iot/manager/manager.js @@ -573,6 +573,40 @@ function getDeviceState (client, deviceId, registryId, projectId, // [END iot_get_device_state] } +// Retrieve the given device's state from the registry. +function setDeviceConfig (client, deviceId, registryId, projectId, + cloudRegion, data, version) { + // [START iot_set_device_config] + // Client retrieved in callback + // getClient(serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const deviceId = 'my-device'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + // const data = 'test-data'; + // const version = 0; + const parentName = `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + + const binaryData = Buffer.from(data).toString('base64'); + const request = { + name: `${registryName}/devices/${deviceId}`, + versionToUpdate: version, + binaryData: binaryData + }; + + client.projects.locations.registries.devices.modifyCloudToDeviceConfig(request, + (err, data) => { + if (err) { + console.log('Could not update config:', deviceId); + console.log('Message: ', err); + } else { + console.log('Success :', data); + } + }); + // [END iot_set_device_config] +} + // Retrieve the given device from the registry. function getRegistry (client, registryId, projectId, cloudRegion) { // [START iot_get_registry] @@ -823,6 +857,19 @@ require(`yargs`) // eslint-disable-line getClient(opts.serviceAccount, cb); } ) + .command( + `setConfig `, + `Sets a devices configuration to the specified data.`, + {}, + (opts) => { + const cb = function (client) { + setDeviceConfig(client, opts.deviceId, opts.registryId, + opts.projectId, opts.cloudRegion, opts.configuration, + opts.version || 0); + }; + getClient(opts.serviceAccount, cb); + } + ) .example(`node $0 createEs256Device my-es-device my-registry ../ec_public.pem`) .example(`node $0 createRegistry my-registry my-iot-topic --serviceAccount=$HOME/creds_iot.json --project_id=my-project-id`) .example(`node $0 createRsa256Device my-rsa-device my-registry ../rsa_cert.pem`) @@ -836,6 +883,7 @@ require(`yargs`) // eslint-disable-line .example(`node $0 listRegistries`) .example(`node $0 patchRsa256 my-device my-registry ../rsa_cert.pem`) .example(`node $0 patchEs256 my-device my-registry ../ec_public.pem`) + .example(`node $0 setConfig my-device my-registry "test" 0`) .example(`node $0 setupTopic my-iot-topic --serviceAccount=$HOME/creds_iot.json --projectId=my-project-id`) .wrap(120) .recommendCommands() diff --git a/iot/manager/resources/rsa_cert.pem b/iot/manager/resources/rsa_cert.pem index b41020505d..db7c7fa344 100644 --- a/iot/manager/resources/rsa_cert.pem +++ b/iot/manager/resources/rsa_cert.pem @@ -1,19 +1,18 @@ -----BEGIN CERTIFICATE----- -MIIDFzCCAf+gAwIBAgIJAP2XoKtudrz5MA0GCSqGSIb3DQEBBQUAMBExDzANBgNV -BAMTBnVudXNlZDAeFw0xNzA3MTExOTE2MzBaFw0xNzA4MTAxOTE2MzBaMBExDzAN -BgNVBAMTBnVudXNlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANfn -stQebiazvSs0t81t7YTBNk3+Aqal0qFKY+QisbTs+BpnDcQIg6Ik974v8OtZ+t/f -c+AdCnyWpOsVzpIVpWfE6p71LsZknGgzL8/lMfgy6PFzzzU7kWzPw+MZ90VbHed3 -k1fJv5E09Er684tAkyCsnr5ihol5jWq1UlnqgnHugYlJu3uOGXjdXCBtG1EzNkDx -ilfVkpgitKCGHYqXrpCCSTZ2Yhp4GrbYh0Hpxg2lF3bE5XMW8p8jl8Ykmjvq+hqt -DFOSS76FXOVG7oQU6Fc8owIRLknAK2IXPI/ciGlubivA0oQwD4E+7hLo4c2M+Jq7 -faitjqPPiuemUcHrZV8CAwEAAaNyMHAwHQYDVR0OBBYEFGJSZ6h5VH/g74dez/WJ -Ftl+C3NVMEEGA1UdIwQ6MDiAFGJSZ6h5VH/g74dez/WJFtl+C3NVoRWkEzARMQ8w -DQYDVQQDEwZ1bnVzZWSCCQD9l6Crbna8+TAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 -DQEBBQUAA4IBAQBvygToNj8Y4hYcNwLsZwJeqhLwiSu2oQ12vpB8FnfCUsrnn9sZ -rqVheNEbr8UjPfTtgUMABxu+iabQHXT2FJqVf0J97Ted/CKUFfvXL3pMs/N5nAuV -TJzvM+Foi3NBjiDxptp3YNS+htiV/z4hX7wvFVhPf+g+Wp8RqmbZwLdcyB4ftBx1 -AZ50HFv+6xH9pF6QrqS8HtfV4Jy9ZHWTnkspdP3U4sEawHxGdcjNx4zwb6oaD6hz -+F2FxlekG2gBsk4tDq+ReYqGa/G9NT4HPIIMwVyIv72ru/9HsL8yU1me3NugXWnf -IatIVphZrfrp+6yJuvmIT5vUn8tMivQp/6rI +MIIC+DCCAeCgAwIBAgIJAMCU5DifwbH2MA0GCSqGSIb3DQEBCwUAMBExDzANBgNV +BAMMBnVudXNlZDAeFw0xNzEyMjAxODU0MzRaFw0yMDA5MTUxODU0MzRaMBExDzAN +BgNVBAMMBnVudXNlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMc2 +pktp/RV1g7omV5CyNi1pQugZvxn6TciLqcjHP1XYfU6mnl7ySvH1xbkzaAlL/8Y8 +IqSFF4eI14qI71OT+7eLq6Ljj8j3XTqAtSANMSpi5KIRVQsa0h0rbRt7Xt1TeEd1 +ErVdzdRKrKvn7wM6UID0QGxC+1gpHGo3Aaz9Q4tVxAeWM6skCDh9WHea0HNj8MPA +hS4fc83PitfITOR9eCP2HW83HXu32yFpCU96WIQGSDfHfN7vD7mDXN+iZa02GJ2K +NBiwQa3LmHmcoaru2yTB9OTf1wGRY/QETrkmA+7enq7p2RQSe6uXu97k9UgV1Wes +c4QcJ/BAsLbaWB/f2h8CAwEAAaNTMFEwHQYDVR0OBBYEFCeNUvOsKm+BB9VgO1l1 +QlwS5rIEMB8GA1UdIwQYMBaAFCeNUvOsKm+BB9VgO1l1QlwS5rIEMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAG/40RXw9lojT7aTJq7f196cL1v +qYiVNeZFE2Ki0iVjzDOk1SOXRqy8v8N4ay32gD+TZOg1Yy8loscKhGCnWIRepQC8 +1pyh2ZGHysICfHyxaKQgIMUzINSRI0E4bYt7kro497AodKN4P3QBH5Q6U7Ra3hG2 +b1dXOBVL0kUCKOnFuByEF2Zhytyhl3bTrm/W6dvhHBGTpB2RXwnldPnyEA4rmdyk +Ui9LYmEhWeizlBpw6jcXcd9qABrUMlxBt24DB3nKo/b8h6K4gXYfgO8yYbgsB+E6 +5wZGLi6nP5O98M+VmQZ5CBZSZid+sfK6eoT+Ca5vn7jSTQGzAIapc57dJEQ= -----END CERTIFICATE----- diff --git a/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js b/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js index a2ac0bdda9..0e2040300d 100644 --- a/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js +++ b/iot/mqtt_example/cloudiot_mqtt_example_nodejs.js @@ -24,31 +24,31 @@ const mqtt = require('mqtt'); console.log('Google Cloud IoT Core MQTT example.'); var argv = require(`yargs`) .options({ - project_id: { + projectId: { 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: { + cloudRegion: { default: 'us-central1', description: 'GCP cloud region.', requiresArg: true, type: 'string' }, - registry_id: { + registryId: { description: 'Cloud IoT registry ID.', requiresArg: true, demandOption: true, type: 'string' }, - device_id: { + deviceId: { description: 'Cloud IoT device ID.', requiresArg: true, demandOption: true, type: 'string' }, - private_key_file: { + privateKeyFile: { description: 'Path to private key file.', requiresArg: true, demandOption: true, @@ -61,31 +61,31 @@ var argv = require(`yargs`) choices: ['RS256', 'ES256'], type: 'string' }, - num_messages: { + numMessages: { default: 100, description: 'Number of messages to publish.', requiresArg: true, type: 'number' }, - token_exp_mins: { + tokenExpMins: { default: 20, description: 'Minutes to JWT token expiration.', requiresArg: true, type: 'number' }, - mqtt_bridge_hostname: { + mqttBridgeHostname: { default: 'mqtt.googleapis.com', description: 'MQTT bridge hostname.', requiresArg: true, type: 'string' }, - mqtt_bridge_port: { + mqttBridgePort: { default: 8883, description: 'MQTT bridge port.', requiresArg: true, type: 'number' }, - message_type: { + messageType: { default: 'events', description: 'Message type to publish.', requiresArg: true, @@ -93,7 +93,7 @@ var argv = require(`yargs`) 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`) + .example(`node $0 cloudiot_mqtt_example_nodejs.js --projectId=blue-jet-123 --registryId=my-registry --deviceId=my-node-device --privateKeyFile=../rsa_private.pem --algorithm=RS256`) .wrap(120) .recommendCommands() .epilogue(`For more information, see https://cloud.google.com/iot-core/docs`) @@ -122,40 +122,49 @@ function createJwt (projectId, privateKeyFile, algorithm) { // messageCount. // [START iot_mqtt_publish] function publishAsync (messageCount, numMessages) { - const payload = `${argv.registry_id}/${argv.device_id}-payload-${messageCount}`; + const payload = `${argv.registryId}/${argv.deviceId}-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; + const delayMs = argv.messageType === 'events' ? 1000 : 2000; if (messageCount < numMessages) { // If we have published fewer than numMessage messages, publish payload // messageCount + 1 in 1 second. setTimeout(function () { let secsFromIssue = parseInt(Date.now() / 1000) - iatTime; - if (secsFromIssue > argv.token_exp_mins * 60) { + if (secsFromIssue > argv.tokenExpMins * 60) { iatTime = parseInt(Date.now() / 1000); console.log(`\tRefreshing token after ${secsFromIssue} seconds.`); client.end(); - connectionArgs.password = createJwt(argv.project_id, argv.private_key_file, argv.algorithm); + connectionArgs.password = createJwt(argv.projectId, argv.privateKeyFile, argv.algorithm); client = mqtt.connect(connectionArgs); - client.on('connect', () => { - console.log('connect', arguments); + client.on('connect', (success) => { + console.log('connect'); + if (success) { + publishAsync(1, argv.numMessages); + } else { + console.log('Client not connected...'); + } }); client.on('close', () => { - console.log('close', arguments); + console.log('close'); }); - client.on('error', () => { - console.log('error', arguments); + client.on('error', (err) => { + console.log('error', err); + }); + + client.on('message', (topic, message, packet) => { + console.log('message received: ', Buffer.from(message, 'base64').toString('ascii')); }); client.on('packetsend', () => { - // Too verbose to log here + // Note: logging packet send is very verbose }); } publishAsync(messageCount + 1, numMessages); @@ -171,18 +180,18 @@ function publishAsync (messageCount, numMessages) { // [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}`; +const mqttClientId = `projects/${argv.projectId}/locations/${argv.cloudRegion}/registries/${argv.registryId}/devices/${argv.deviceId}`; // 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. let connectionArgs = { - host: argv.mqtt_bridge_hostname, - port: argv.mqtt_bridge_port, + host: argv.mqttBridgeHostname, + port: argv.mqttBridgePort, clientId: mqttClientId, username: 'unused', - password: createJwt(argv.project_id, argv.private_key_file, argv.algorithm), + password: createJwt(argv.projectId, argv.privateKeyFile, argv.algorithm), protocol: 'mqtts', secureProtocol: 'TLSv1_2_method' }; @@ -191,25 +200,33 @@ let connectionArgs = { let iatTime = parseInt(Date.now() / 1000); let client = mqtt.connect(connectionArgs); +client.subscribe(`/devices/${argv.deviceId}/config`); + // 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}`; +const mqttTopic = `/devices/${argv.deviceId}/${argv.messageType}`; -client.on('connect', () => { - console.log('connect', arguments); - // After connecting, publish 'num_messages' messagse asynchronously, at a rate - // of 1 per second for telemetry events and 1 every 2 seconds for states. - publishAsync(1, argv.num_messages); +client.on('connect', (success) => { + console.log('connect'); + if (success) { + publishAsync(1, argv.numMessages); + } else { + console.log('Client not connected...'); + } }); client.on('close', () => { - console.log('close', arguments); + console.log('close'); +}); + +client.on('error', (err) => { + console.log('error', err); }); -client.on('error', () => { - console.log('error', arguments); +client.on('message', (topic, message, packet) => { + console.log('message received: ', Buffer.from(message, 'base64').toString('ascii')); }); client.on('packetsend', () => { diff --git a/iot/mqtt_example/package.json b/iot/mqtt_example/package.json index 6a8c79eaf5..ea886891a8 100644 --- a/iot/mqtt_example/package.json +++ b/iot/mqtt_example/package.json @@ -6,9 +6,13 @@ "license": "Apache-2.0", "author": "Google Inc.", "dependencies": { - "yargs": "8.0.2", + "@google-cloud/pubsub": "0.13.2", + "@google-cloud/nodejs-repo-tools": "1.4.17", + "ava": "0.22.0", "jsonwebtoken": "7.4.1", - "mqtt": "2.7.2" + "mqtt": "2.7.2", + "uuid": "3.1.0", + "yargs": "8.0.2" }, "devDependencies": {} } diff --git a/iot/mqtt_example/resources/README.md b/iot/mqtt_example/resources/README.md new file mode 100644 index 0000000000..29a1f453ee --- /dev/null +++ b/iot/mqtt_example/resources/README.md @@ -0,0 +1,4 @@ +# Test public certificate files + +The certificates in this folder are only provided for testing and should not be +used for registering or connecting your devices. diff --git a/iot/mqtt_example/resources/rsa_cert.pem b/iot/mqtt_example/resources/rsa_cert.pem new file mode 100644 index 0000000000..db7c7fa344 --- /dev/null +++ b/iot/mqtt_example/resources/rsa_cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC+DCCAeCgAwIBAgIJAMCU5DifwbH2MA0GCSqGSIb3DQEBCwUAMBExDzANBgNV +BAMMBnVudXNlZDAeFw0xNzEyMjAxODU0MzRaFw0yMDA5MTUxODU0MzRaMBExDzAN +BgNVBAMMBnVudXNlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMc2 +pktp/RV1g7omV5CyNi1pQugZvxn6TciLqcjHP1XYfU6mnl7ySvH1xbkzaAlL/8Y8 +IqSFF4eI14qI71OT+7eLq6Ljj8j3XTqAtSANMSpi5KIRVQsa0h0rbRt7Xt1TeEd1 +ErVdzdRKrKvn7wM6UID0QGxC+1gpHGo3Aaz9Q4tVxAeWM6skCDh9WHea0HNj8MPA +hS4fc83PitfITOR9eCP2HW83HXu32yFpCU96WIQGSDfHfN7vD7mDXN+iZa02GJ2K +NBiwQa3LmHmcoaru2yTB9OTf1wGRY/QETrkmA+7enq7p2RQSe6uXu97k9UgV1Wes +c4QcJ/BAsLbaWB/f2h8CAwEAAaNTMFEwHQYDVR0OBBYEFCeNUvOsKm+BB9VgO1l1 +QlwS5rIEMB8GA1UdIwQYMBaAFCeNUvOsKm+BB9VgO1l1QlwS5rIEMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAG/40RXw9lojT7aTJq7f196cL1v +qYiVNeZFE2Ki0iVjzDOk1SOXRqy8v8N4ay32gD+TZOg1Yy8loscKhGCnWIRepQC8 +1pyh2ZGHysICfHyxaKQgIMUzINSRI0E4bYt7kro497AodKN4P3QBH5Q6U7Ra3hG2 +b1dXOBVL0kUCKOnFuByEF2Zhytyhl3bTrm/W6dvhHBGTpB2RXwnldPnyEA4rmdyk +Ui9LYmEhWeizlBpw6jcXcd9qABrUMlxBt24DB3nKo/b8h6K4gXYfgO8yYbgsB+E6 +5wZGLi6nP5O98M+VmQZ5CBZSZid+sfK6eoT+Ca5vn7jSTQGzAIapc57dJEQ= +-----END CERTIFICATE----- diff --git a/iot/mqtt_example/resources/rsa_private.pem b/iot/mqtt_example/resources/rsa_private.pem new file mode 100644 index 0000000000..d627d8a8c5 --- /dev/null +++ b/iot/mqtt_example/resources/rsa_private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDHNqZLaf0VdYO6 +JleQsjYtaULoGb8Z+k3Ii6nIxz9V2H1Opp5e8krx9cW5M2gJS//GPCKkhReHiNeK +iO9Tk/u3i6ui44/I9106gLUgDTEqYuSiEVULGtIdK20be17dU3hHdRK1Xc3USqyr +5+8DOlCA9EBsQvtYKRxqNwGs/UOLVcQHljOrJAg4fVh3mtBzY/DDwIUuH3PNz4rX +yEzkfXgj9h1vNx17t9shaQlPeliEBkg3x3ze7w+5g1zfomWtNhidijQYsEGty5h5 +nKGq7tskwfTk39cBkWP0BE65JgPu3p6u6dkUEnurl7ve5PVIFdVnrHOEHCfwQLC2 +2lgf39ofAgMBAAECggEAa8LBI8w6jfAaiIiTF39/VQM6oQjEWg4PI2uBNU2qkgpg +hjoVMzEHn2keQrdvEJBe1GHVL3+bIcLBLXwzUdeb80aHWr6UGdbNRIc48xSPwujp +2k1b3nzsqcDC7XnH8Btl1embbCZI4Axg6UC2Bt31jDwT2TxGcAMVDtOggW4iqmyO +8v+J0j2tl+J/d7K49VtYkN5epoNn+gxpvsMbaDNmolE3Pcg2hr/h5tkzTWB53bwW +7K89FczsTvhCs8LWA/R+kKX9O9jpeUpbE6afAGdU6JCAYohClva1SIpksijV2HFg +SYZJ1QTjKfV11iBvYpj4wXGcuXgui4WL/0xCCXZt2QKBgQDq2PdYgpmJJFZHNRr9 +QlgZSAsuu16t49ezAxlm4Q8orAht0Nq02l6eMHbsjV3lRmyo0vK2byMQdhaSfweB +eT7sD/TSmT7jqGEqW+jtf8mNQpAeQhJJKJ8AbERF5heKcMwk6CbFAUipc1TgCiuZ +335SyJRLUU1yUSvGqbMevUKAJQKBgQDZKAuyItjZFlyU2dVXYC8tALWjIaLwSsXw +SbCfhlMLUt7DTb8+V834ygPtEKagcaw4QwcB+brH6dO1vwL3KQWMOeZcUkXg8NCW +F71J84LLmUsZQkHx8HqLG6o/DUq+M1SuklMROAI14ZKtL0xMW/lIgICngENanUS8 +m/kT+zIr8wKBgQCAkBgQDybICellf+/vdvNNm7/rbE1vekxpxieQx2IKDOtsWqpv +2R91D+j6pIUjb4MqhJi0QHD8oPjIZ9wPB/XnOvD2nUftTw8qJjiEU7FdsHO+7MKf +5CnvDFXvwf9fHVLUXXZj4MkLYoSzRT4LNWstHHr6JF72VvNVBS92g3s1NQKBgQDN +Rw3HVymmDX13OHR0JHh0Ejeh/ioI/KYgf4h3RIM8MRpjhRtD07zhchZEA3mAMfSb +OsFlHK10TZXUOLyU6DJWXGVm4F8uRHbpa4eEYK1DyaU0O24bRziQR5aoA+ij4+Ip +JCliHY/5X0NIc22swUTlRJRZwuXowsI3iTnrqYnEAQKBgQC9AhF+S6d7uIsEOg7I +Kv8ndASx7Nfsy6OQfOltgLArV4w2POQjbPYsLLrz4s82mSpS205w2OzCfcUXKLxD +mswC0Bs/U3UmJW0d1oWfUm28FjGbzGYGfOLm1dPuyNW32FbZXvO4+jaIemjtg2U7 +avJDFdpNwVz/bVg0HWQWNP/C0A== +-----END PRIVATE KEY----- diff --git a/iot/mqtt_example/system-test/cloudiot_mqtt_example_test.js b/iot/mqtt_example/system-test/cloudiot_mqtt_example_test.js new file mode 100644 index 0000000000..f901807648 --- /dev/null +++ b/iot/mqtt_example/system-test/cloudiot_mqtt_example_test.js @@ -0,0 +1,116 @@ +/** + * 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 path = require(`path`); +const PubSub = require(`@google-cloud/pubsub`); +const test = require(`ava`); +const tools = require(`@google-cloud/nodejs-repo-tools`); +const uuid = require(`uuid`); + +const topicName = `nodejs-docs-samples-test-iot-${uuid.v4()}`; +const registryName = `nodejs-test-registry-iot-${uuid.v4()}`; +const helper = `node ../manager/manager.js`; +const cmd = `node cloudiot_mqtt_example_nodejs.js `; +const cmdSuffix = ` --numMessages=1 --privateKeyFile=resources/rsa_private.pem --algorithm=RS256`; +const cwd = path.join(__dirname, `..`); + +test.before(tools.checkCredentials); +test.before(async () => { + const pubsub = PubSub(); + return pubsub.createTopic(topicName) + .then((results) => { + const topic = results[0]; + console.log(`Topic ${topic.name} created.`); + return topic; + }); +}); + +test.after.always(async () => { + const pubsub = PubSub(); + const topic = pubsub.topic(topicName); + return topic.delete() + .then(() => { + console.log(`Topic ${topic.name} deleted.`); + }); +}); + +test(`should receive configuration message`, async (t) => { + const localDevice = `test-rsa-device`; + const localRegName = `${registryName}-rsa256`; + + let output = await tools.runAsync(`${helper} setupIotTopic ${topicName}`, cwd); + await tools.runAsync( + `${helper} createRegistry ${localRegName} ${topicName}`, cwd); + await tools.runAsync( + `${helper} createRsa256Device ${localDevice} ${localRegName} resources/rsa_cert.pem`, cwd); + + output = await tools.runAsync( + `${cmd} --messageType=events --registryId="${localRegName}" --deviceId="${localDevice}" ${cmdSuffix}`, + cwd); + t.regex(output, new RegExp(`message received`)); + + // Check / cleanup + await tools.runAsync( + `${helper} getDeviceState ${localDevice} ${localRegName}`, cwd); + await tools.runAsync( + `${helper} deleteDevice ${localDevice} ${localRegName}`, cwd); + await tools.runAsync(`${helper} deleteRegistry ${localRegName}`, cwd); +}); + +test(`should send event message`, async (t) => { + const localDevice = `test-rsa-device`; + const localRegName = `${registryName}-rsa256`; + await tools.runAsync(`${helper} setupIotTopic ${topicName}`, cwd); + await tools.runAsync( + `${helper} createRegistry ${localRegName} ${topicName}`, cwd); + await tools.runAsync( + `${helper} createRsa256Device ${localDevice} ${localRegName} resources/rsa_cert.pem`, cwd); + + const output = await tools.runAsync( + `${cmd} --messageType=events --registryId="${localRegName}" --deviceId="${localDevice}" ${cmdSuffix}`, + cwd); + t.regex(output, new RegExp(`Publishing message:`)); + + // Check / cleanup + await tools.runAsync( + `${helper} getDeviceState ${localDevice} ${localRegName}`, cwd); + await tools.runAsync( + `${helper} deleteDevice ${localDevice} ${localRegName}`, cwd); + await tools.runAsync(`${helper} deleteRegistry ${localRegName}`, cwd); +}); + +test(`should send event message`, async (t) => { + const localDevice = `test-rsa-device`; + const localRegName = `${registryName}-rsa256`; + await tools.runAsync(`${helper} setupIotTopic ${topicName}`, cwd); + await tools.runAsync( + `${helper} createRegistry ${localRegName} ${topicName}`, cwd); + await tools.runAsync( + `${helper} createRsa256Device ${localDevice} ${localRegName} resources/rsa_cert.pem`, cwd); + + const output = await tools.runAsync( + `${cmd} --messageType=state --registryId="${localRegName}" --deviceId="${localDevice}" ${cmdSuffix}`, + cwd); + t.regex(output, new RegExp(`Publishing message:`)); + + // Check / cleanup + await tools.runAsync( + `${helper} getDeviceState ${localDevice} ${localRegName}`, cwd); + await tools.runAsync( + `${helper} deleteDevice ${localDevice} ${localRegName}`, cwd); + await tools.runAsync(`${helper} deleteRegistry ${localRegName}`, cwd); +});