Google Cloud IoT sendCommandToDevice from cloud funcctions showing Service Unavailable

310 views Asked by At

I tried sending the command from cloud functions, I am getting Error: The service is currently unavailable.

Package.JSON "dependencies": { "firebase-admin": "~6.0.0", "firebase-functions": "^2.0.3", "googleapis": "34.0.0" }

const parentName = `projects/${projectId}/locations/${cloudRegion}`;
const registryName = `${parentName}/registries/${reqData.registryId}`;
const binaryData = Buffer.from(JSON.stringify(reqData.message)).toString('base64');
const request = {
    name: `${registryName}/devices/${reqData.deviceId}`,
    binaryData: binaryData
};

google.auth.getClient().then((authClient) => {
    const discoveryUrl =
        `${DISCOVERY_API}?version=${API_VERSION}`;
    if (authClient.createScopedRequired && authClient.createScopedRequired()) {
      // Scopes can be specified either as an array or as a single,
      // space-delimited string.
      authClient = authClient.createScoped([
        'https://www.googleapis.com/auth/cloud-platform'
      ]);
    }

    google.options({
      auth: authClient
    });

    google.discoverAPI(discoveryUrl).then((client, err) => {
      if (err) {
        console.log('Error during API discovery', err);
        return undefined;
      }
      client.projects.locations.registries.devices.sendCommandToDevice(request,
        (err, data) => {
          if (err) {
            console.log('Could not send command:', request);
            console.log('Message: ', err);
          } else {
            console.log('Success :', data.statusText);
          }
        });
    });
  });

Logs: { Error: The service is currently unavailable. at createError (/user_code/node_modules/googleapis/node_modules/axios/lib/core/createError.js:16:15) at settle (/user_code/node_modules/googleapis/node_modules/axios/lib/core/settle.js:18:12) at Unzip.handleStreamEnd (/user_code/node_modules/googleapis/node_modules/axios/lib/adapters/http.js:201:11) at emitNone (events.js:91:20) at Unzip.emit (events.js:185:7) at endReadableNT (_stream_readable.js:974:12) at _combinedTickCallback (internal/process/next_tick.js:80:11) at process._tickDomainCallback (internal/process/next_tick.js:128:9)

2

There are 2 answers

0
Alex Hong On

I'm not too familiar with Firebase cloud functions, but I didn't get the error using the inline editor for Cloud Functions (https://console.cloud.google.com/functions). Can you tell me when you started getting this error (and if you're still encountering it)?

For reference, here was the code that I used (basically what you had but with more explicit definitions for projectId, cloudRegion.

const {google} = require('googleapis');

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

exports.sendCommand = (req, res) => {
  let reqData = req.body;

  const projectId = reqData.projectId || process.env.GCLOUD_PROJECT;
  const cloudRegion = reqData.cloudRegion || process.env.GCLOUD_REGION;

  const parentName = `projects/${projectId}/locations/${cloudRegion}`;
  const registryName = `${parentName}/registries/${reqData.registryId}`;
  const binaryData = Buffer.from(JSON.stringify(reqData.message)).toString('base64');
  const request = {
      name: `${registryName}/devices/${reqData.deviceId}`,
      binaryData: binaryData
  };

  google.auth.getClient().then((authClient) => {
      const discoveryUrl =
          `${DISCOVERY_API}?version=${API_VERSION}`;
      if (authClient.createScopedRequired && authClient.createScopedRequired()) {
        // Scopes can be specified either as an array or as a single,
        // space-delimited string.
        authClient = authClient.createScoped([
          'https://www.googleapis.com/auth/cloud-platform'
        ]);
      }

      google.options({
        auth: authClient
      });

      google.discoverAPI(discoveryUrl).then((client, err) => {
        if (err) {
          console.log('Error during API discovery', err);
          return undefined;
        }
        client.projects.locations.registries.devices.sendCommandToDevice(request,
          (err, data) => {
            if (err) {
              console.log('Could not send command:', request);
              console.log('Message: ', err);
            } else {
              console.log('Success :', data.statusText);
            }
          });
      });
    });
  res.status(200).send(reqData.message);
};
1
sMyles On

The problem is that subfolder MUST be specified, and MUST not be an empty string.

As I was using this in a Firebase function, I just use the firebase subfolder for any commands being sent that do not have a specific subfolder

const request = {
    name: `${registryName}/devices/${deviceId}`,
    binaryData: Buffer.from(JSON.stringify(commandMessage)).toString("base64"),
    subfolder: 'firebase'
}

Here's functions deps:

  "dependencies": {
    "firebase-admin": "^6.4.0",
    "firebase-functions": "^2.1.0",
    "fs-extra": "^7.0.0",
    "googleapis": "^36.0.0",
  },

This is probably due to

  1. bug in the node library
  2. bug in Google's endpoint
  3. Lack of testing on Google's part

Seems that Google's "IoT" is still very young and needs a lot of work