Debugging the Smart Home

1. Before you begin

As an Internet of Things (IoT) developer, you can build Cloud-to-cloud integrations that give your users the ability to control their devices through touch controls in the Google Home app and voice commands with the Google Assistant.

a4657871181b5ad2.gif

Learning the debugging tools for Cloud-to-cloud integrations is an important step to build production quality integration with Google Assistant. To facilitate easy monitoring and debugging, Google Cloud Platform (GCP) Metrics and Logging and Test Suite for smart home are available to help you identify and resolve issues for your integrations.

Prerequisites

What you'll build

In this codelab, you'll deploy a Cloud-to-cloud integration with 2 defects and connect it to the Assistant, then debug the integration's defects via Test Suite for smart home & Google Cloud Platform (GCP) Metrics and Logging.

What you'll learn

  • How to use GCP Metrics and Logging to identify and resolve production issues
  • How to use Test Suite for smart home to identify functional and API issues

What you'll need

2. Run the faulty app

Get the source code

Click the following link to download the sample for this codelab on your development machine:

...or you can clone the GitHub repository from the command line:

$ git clone https://github.com/google-home/smarthome-debug.git

About the project

The washer app contains the following subdirectories:

Connect to Firebase

Open terminal on your development machine. Navigate to the washer-faulty directory, then set up the Firebase CLI with your integration project built in the Connect smart home devices to the Google Assistant codelab:

$ cd washer-faulty
$ firebase use <firebase-project-id>

Deploy to Firebase

Navigate to the functions folder and install all the necessary dependencies using npm.

$ cd functions
$ npm install

Note: If you see the message below, you can ignore and proceed. The warning is due to some older dependencies and you can find more details here.

found 5 high severity vulnerabilities
  run `npm audit fix` to fix them, or `npm audit` for details

Now that you have installed the dependencies and configured your project, you are ready to deploy the faulty washer app.

$ firebase deploy

This is the console output you should see:

...

✔ Deploy complete!

Project Console: https://console.firebase.google.com/project/<Firebase-project-id>/overview
Hosting URL: https://<Firebase-project-id>.firebaseapp.com

Update HomeGraph

Open the Hosting URL in your browser (https://<firebase-project-id>.firebaseapp.com) to view the web app. On the web UI, click the Refresh ae8d3b25777a5e30.png button to update HomeGraph via Request Sync with the latest device metadata from the faulty washer app:

6f2b1344179977cf.png

Open the Google Home app and verify that you can see the washer device named Faulty Washer.

e357de6a7faff925.png

3. Test your Integration

After you deploy your project, test that your integration controls the washer.

Test the washer

Check the value change when you try any of the following voice commands through your phone:

"Hey Google, turn on my washer."

"Hey Google, start my washer."

"Hey Google, pause my washer."

"Hey Google, resume my washer."

"Hey Google, stop my washer."

You will notice Assistant responds that something is wrong via voice when you pause / resume the washer:

"Sorry, I couldn't reach <project display name>."

To debug this issue, you first need more information on the error to narrow down and identify the root cause.

Smarthome Analytics dashboard

A good place to inspect errors is Smarthome Analytics dashboard, which aggregates charts of Usage and Health metrics for your cloud fulfillment:

  • The Usage metrics reflects the usage trend of your Cloud-to-cloud integration, including the number of daily active users and total request count to your fulfillment.
  • The Health metrics helps you monitor anomaly occurrence on your Cloud-to-cloud integration, covering request latency, success percentage, and error breakdown.

To narrow down the cause of the error, follow the steps below to access the project dashboard.

  1. In the Developer Console, go to the Projects page.
  2. Select your smart home project.
  3. Click the Analytics tab on the left menu.

b1735bbe11a7aff8.png

  1. This will lead you to a list of dashboards for your project on Google Cloud. Select the Google Home Analytics - Cloud Integration dashboard.

5edd3751323176dd.png

  1. Scroll down to Cloud Fulfillment Errors - Status Breakdown chart to view the error codes for the highlighted time range.

c468743c20a11c15.png

The PARTNER_RESPONSE_MISSING_DEVICE error code provides a hint to the root cause. Next, retrieve event logs based on the error code for more details.

Access event logs

In order to gain more details on the error, access event logs for your Cloud-to-cloud integration via Cloud Logging.

Open the Navigation Menu in Google Cloud Platform, and under Operations, select Logging > Logs Explorer to access the event logs for your project. Alternatively, you can search for Logs Explorer in the search box.

In the Search all fields input field, enter the query PARTNER_RESPONSE_MISSING_DEVICE and click Run Query. The logs matching the query are displayed in the Results section.

747cca0f1249a5a.png

The error log shows a smart home event with error details indicating:

  • The user action taken is "resuming washer" (actionType: "STARTSTOP_UNPAUSE"), corresponding to the recent failed voice command.
  • The associated debugging message is "JSON response does not include device."

Based on the debugging message, you should check why the washer app doesn't include the correct device in EXECUTE response.

Identify the root cause of the error

In functions/index.js, find the EXECUTE handler (in the onExecute array) that returns the status of each command and the new device state. The insertion of device IDs into an EXECUTE response depends on the resolving of updateDevice function:

index.js

app.onExecute(async (body) => {
 ...

 for (const command of intent.payload.commands) {
   for (const device of command.devices) {
     for (const execution of command.execution) {
       executePromises.push(
           updateDevice(execution, device.id)
               .then((data) => {
                 result.ids.push(device.id);
                 Object.assign(result.states, data);
               })
               .catch((e) =>
                 functions.logger.error('EXECUTE',
                     device.id, e.message)));
     }
   }
 }

Further check how the updateDevice function handles pause / resume on the washer, and you will find the string to match for the pause / resume command is incorrect:

index.js

const updateDevice = async (execution, deviceId) => {
 const {params, command} = execution;
 let state; let ref;
 switch (command) {
   ...
   case 'action.devices.commands.PauseUnpausePause':
     state = {isPaused: params.pause};
     if (params.pause) state.isRunning = false;
     ref = firebaseRef.child(deviceId).child('StartStop');
     break;
 }

 return ref.update(state)
     .then(() => state);
};

Fix the error

Now that you have identified the root cause of the error, you can correct the string for the pause / resume command:

index.js

const updateDevice = async (execution, deviceId) => {
 const {params, command} = execution;
 let state; let ref;
 switch (command) {
   ...
   case 'action.devices.commands.PauseUnpause':
     state = {isPaused: params.pause};
     if (params.pause) state.isRunning = false;
     ref = firebaseRef.child(deviceId).child('StartStop');
     break;
 }

 return ref.update(state)
     .then(() => state);
};

Test your fix

Deploy the updated code using the Firebase CLI:

firebase deploy --only functions

Retry the following voice commands, and you will find Assistant responds correctly now when you pause / resume the washer.

"Hey Google, pause my washer."

=>

"Sure, pausing the washer."

"Hey Google, resume my washer."

=>

"Got it, resuming the washer."

You can also test the current state of your washer by asking questions.

"Hey Google, is my washer on?"

"Hey Google, is my washer running?"

"Hey Google, what cycle is my washer on?"

4. Test your integration with Test Suite

In addition to testing manually, you can use the automated Test Suite for smart home to validate use cases based on the device types and traits associated with your integration. The Test Suite runs a series of tests to detect issues in your integration, and shows informative messages for failed test cases to expedite your debugging before diving into event logs.

Run Test Suite for smart home

Follow these instructions to test your Cloud-to-cloud integration by Test Suite:

  1. In your web browser, open the Test Suite for smart home.
  2. Sign in to Google using the button in the top-right corner. This allows the Test Suite to send the commands directly to Google Assistant.
  3. In the Project ID field, enter the project ID of your Cloud-to-cloud integration. And then click NEXT to proceed.
  4. In the Test Settings step, you will see Test Suite list the device type and traits of the washer.

78ed6a1ebdb581bf.png

  1. Disable the Test Request Sync option since the sample washer app has no UI to add / remove / rename the washer. In a production system, you must trigger Request Sync whenever the user adds / removes / renames devices.
  2. Click NEXT to start running the test.

After Test Suite completes running, view the results of test cases. You will notice two failed test cases caught with respective error message:

5838d10631c98ed2.png

To debug your Cloud-to-cloud integration for the failure, you will need to identify the root cause of the error by first analyzing the error message.

Analyze error message

In order to help developers identify the root cause, Test Suite shows error messages for each failed test case that indicate the reason for the failure.

For the first failed test case above,

99e4e5d06965a8a7.png

its error message indicates Test Suite expects "isPause": true in the states reported from your Cloud-to-cloud integration, but the actual states include only "isPause": false.

In addition, the second failed test case's error message indicates the states in QUERY response from your Cloud-to-cloud integration include "isPause": true, which differs from "isPause": false in the states reported from your Cloud-to-cloud integration:

fdb5124102e3a37.png

According to both error messages, you should then check whether your integration reports state isPaused with the correct value.

Identify the root cause of the error

Open functions/index.js, which contains the reportstate function that posts state changes to Home Graph via Report State. Inspect the Report State payload, and you will find the payload is missing the isPaused state, which is exactly what the Test Suite checked for in the failed test cases.

index.js

exports.reportstate = functions.database.ref('{deviceId}').onWrite(
    async (change, context) => {
      ...

      const requestBody = {
        requestId: 'ff36a3cc', /* Any unique ID */
        agentUserId: USER_ID,
        payload: {
          devices: {
            states: {
              /* Report the current state of our washer */
             [context.params.deviceId]: {
                online: true,
                on: snapshot.OnOff.on,
                isRunning: snapshot.StartStop.isRunning,
                currentRunCycle: [{
                  currentCycle: 'rinse',
                  nextCycle: 'spin',
                  lang: 'en',
                }],
                currentTotalRemainingTime: 1212,
                currentCycleRemainingTime: 301,
              },
            },
          },
        },
      };

      const res = await homegraph.devices.reportStateAndNotification({
        requestBody,
      });
      ...
    });

Fix the error

Now that you have identified the root cause of the error, revise functions/index.js by adding the isPaused state to the Report State payload:

index.js

exports.reportstate = functions.database.ref('{deviceId}').onWrite(
    async (change, context) => {
      ...

      const requestBody = {
        requestId: 'ff36a3cc', /* Any unique ID */
        agentUserId: USER_ID,
        payload: {
          devices: {
            states: {
              /* Report the current state of our washer */
             [context.params.deviceId]: {
                online: true,
                on: snapshot.OnOff.on,
                isPaused: snapshot.StartStop.isPaused,
                isRunning: snapshot.StartStop.isRunning,
                currentRunCycle: [{
                  currentCycle: 'rinse',
                  nextCycle: 'spin',
                  lang: 'en',
                }],
                currentTotalRemainingTime: 1212,
                currentCycleRemainingTime: 301,
              },
            },
          },
        },
      };
      ...
    });

Test your fix

Deploy the updated code using the Firebase CLI:

$ firebase deploy --only functions

Re-run the Test Suite for smart home, and you will find that all test cases have passed.

148837f85d377dd6.png

5. Congratulations

17d485868a6771bc.png

Congratulations! You successfully learned how to troubleshoot Cloud-to-cloud integration issues via Test Suite for smart home & GCP Metrics and Logging.

Learn more

Building off this Codelab, try the following exercises and explore additional resources:

You can also learn more about testing and submitting an integration for review, including the certification process to publish your integration to users.