Smart Home Dispense Trait Schema

action.devices.traits.Dispense - This trait belongs to devices that support dispensing a specified amount of one or more physical items. For example, a dog treat dispenser may dispense a number of treats, a faucet may dispense cups of water, and a pet feeder may dispense both water and pet food.

This trait also supports dispensing a preset, for example as a "cat food bowl" for a pet feeder or a "glass" for a faucet.

Device ATTRIBUTES

Attribute Definition
supportedDispenseItems Required array of objects. Contains information on all the items that the device can dispense, including the following details about each item:
  • item_name: Required string. The name or key for the item (for example, “water”, “dog treats”). This field is only used as the key for the synonyms and must also be included in the synonyms list if it is a valid name for the item.
  • item_name_synonyms: Required array of objects. Each object contains two pairs:
    • lang: String representing the language code (ISO 639-1).
    • synonyms: Array of strings of the synonyms for the item in that language. These synonyms must include all singular, plural, and gendered forms of the item. The first string in this list must be the plural form of the item in that language.
  • supported_units: Required array of strings. Indicates what set of units the device supports for that item. For example, water from a faucet would support units for liquids but not for length, whereas dog treats from a pet treat dispenser may only support NO_UNITS. See below for a list of all supported units.
  • default_portion: Required object. Indicates what is a typical amount of the item that may be dispensed (for example, the amount of water may be 2 cups). Each object contains two pairs:
    • amount: Float.
    • unit: String.

Example of the supportedDispenseItems format:

{
 "supportedDispenseItems": [{
  "item_name": "water",
  "item_name_synonyms": [{
    "lang": "en",
    "synonyms": [
     "water"
    ]
   },
   {
    "lang": "es",
    "synonyms": [
     "agua"
    ]
   },
   {
    "lang": "fr",
    "synonyms": [
     "eau"
    ]
   }
  ],
  "supported_units": [
   "TEASPOONS",
   "TABLESPOONS",
   "FLUID_OUNCES",
   "CUPS",
   "PINTS",
   "QUARTS",
   "GALLONS",
   "MILLILITERS",
   "LITERS",
   "DECILITERS"
  ],
  "default_portion": {
   "amount": 2.0,
   "unit": "CUPS"
  }
 }]
}
Attribute Definition
supportedDispensePresets Required array of objects. Contains the information on the various presets the device supports with the following details about each preset:
  • preset_name: Required string. The name or key for the item (for example, "water", "dog treats"). This field is only used as the key for the synonyms and must also be included in the synonyms list if it is a valid name for the item.
  • preset_name_synonyms: Array of objects. Each object contains two pairs:
    • lang: String representing the language code (ISO 639-1).
    • synonyms: Array of strings of the synonyms for the preset in that language. These synonyms must include all singular, plural, and gendered forms of the preset.

Example of the supportedDispensePresets format:

{
  "supportedDispensePresets": [{
    "preset_name": "cat water bowl",
    "preset_name_synonyms": [{
        "lang": "en",
        "synonyms": [
          "cat water bowl",
          "cat water dish"
        ]
      },
      {
        "lang": "es",
          "synonyms": [
            "plato de agua para gato",
            "bebedero para gato"
        ]
      },
      {
        "lang": "fr",
        "synonyms": [
          "bol d'eau de chat",
          "bac à eau pour chat",
          "tasse d'eau de chat"
        ]
      }
    ]
  }]
}

Supported units

The supported_units field can take any combination of the following units:

  • CENTIMETERS
  • CUPS
  • DECILITERS
  • FLUID_OUNCES
  • GALLONS
  • GRAMS
  • KILOGRAMS
  • LITERS
  • MILLIGRAMS
  • MILLILITERS
  • MILLIMETERS
  • NO_UNITS: When the item is countable so units are not needed (for example, dog treats).
  • OUNCES
  • PINCH
  • PINTS
  • PORTION: Generic unit of measurement to represent a “portion” of the item (device-specific).
  • POUNDS
  • QUARTS
  • TABLESPOONS
  • TEASPOONS

Sample SYNC Request and Response

This is an example using the device type and traits above. It is intended to give an idea of how to build a SYNC response. If you add or remove traits, this will need to be modified to reflect those changes.

Request
{
    "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
    "inputs": [{
      "intent": "action.devices.SYNC"
    }]
}
Node.js
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onSync((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '1836.15267389',
      devices: [{
        id: '123',
        type: 'action.devices.types.FAUCET',
        traits: [
          'action.devices.traits.Dispense'
        ],
        name: {
          defaultNames: [
            'Faucet'
          ],
          name: 'Sink Faucet',
          nicknames: [
            'Faucet'
          ]
        },
        willReportState: true,
        attributes: {
          supportedDispenseItems: [{
            item_name: 'water',
            item_name_synonyms: [{
              lang: 'en',
              synonyms: ['water']
            }, {
              lang: 'es',
              synonyms: ['agua']
            }, {
              lang: 'fr',
              synonyms: ['eau']
            }],
            supported_units: [
              'TEASPOONS',
              'TABLESPOONS',
              'FLUID_OUNCES',
              'CUPS',
              'PINTS',
              'QUARTS',
              'GALLONS',
              'MILLILITERS',
              'LITERS',
              'DECILITERS'
            ],
            default_portion: {
              amount: 2.0,
              unit: 'CUPS'
            }
          }],
          supportedDispensePresets: [{
            preset_name: 'cat water bowl',
            preset_name_synonyms: [{
              lang: 'en',
              synonyms: [
                'cat water bowl',
                'cat water dish',
                'cat water cup'
              ]
            },{
              lang: 'es',
              synonyms: [
                'plato de agua para gato',
                'bebedero para gato'
              ]
            }, {
              lang: 'fr',
              synonyms: [
                `bol d'eau de chat`,
                'bac à eau pour chat',
                `tasse d'eau de chat`
              ]
            }]
          }]
        },
        customData: {
          fooValue: 42,
          barValue: true,
          catValue: "Fuzzy"
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
Java
@NotNull
@Override
public SyncResponse onSync(@NotNull SyncRequest syncRequest, @Nullable Map<?, ?> headers) {
  Payload payload = new Payload();
  payload.setAgentUserId("1836.15267389");
  payload.setDevices(new SyncResponse.Payload.Device[] {
    new SyncResponse.Payload.Device.Builder()
        .setId("123")
        .setType("action.devices.types.FAUCET")
        .addTrait("action.devices.traits.Dispense")
        .setName(
            Collections.singletonList("Faucet"),
            "Sink Faucet",
            Collections.singletonList("Faucet")
        )
        .setWillReportState(true)
        .setAttributes(new JSONObject()
            .put("supportedDispenseItems", new JSONObject[]{
                new JSONObject()
                    .put("item_name", "water")
                    .put("item_name_synonyms", new JSONObject[] {
                        new JSONObject()
                            .put("lang", "en")
                            .put("synonyms", new String[] {"water"}),
                        new JSONObject()
                            .put("lang", "es")
                            .put("synonyms", new String[] {"agua"}),
                        new JSONObject()
                            .put("lang", "fr")
                            .put("synonyms", new String[] {"eau"})
                    })
                    .put("supported_units", new String[] {
                        "TEASPOONS",
                        "TABLESPOONS",
                        "FLUID_OUNCES",
                        "CUPS",
                        "PINTS",
                        "QUARTS",
                        "GALLONS",
                        "MILLILITERS",
                        "LITERS",
                        "DECILITERS"
                    }),
                    .put("default_portion", new JSONObject()
                        .put("amount", 2.0)
                        .put("unit", "CUPS")
                    )
            })
            .put("supportedDispensePresets", new JSONObject[] {
                new JSONObject()
                    .put("preset_name", "cat water bowl")
                    .put("preset_name_synonyms", new JSONObject[] {
                        new JSONObject()
                            .put("lang", "en")
                            .put("synonyms", new String[] {
                                "cat water bowl",
                                "cat water dish",
                                "cat water cup"
                            }),
                        new JSONObject()
                            .put("lang", "es")
                            .put("synonyms", new String[] {
                                "plato de agua para gato",
                                "bebedero para gato"
                            }),
                        new JSONObject()
                            .put("lang", "fr")
                            .put("synonyms", new String[] {
                                "bol d'eau de chat",
                                "bac à eau pour chat",
                                "tasse d'eau de chat"
                            })
                    })
            })
        )
        .setDeviceInfo("Sirius Cybernetics Corporation", "492134", "3.2", "11.4")
        .setCustomData(new JSONObject()
            .put("fooValue", 42)
            .put("barValue", true)
            .put("catValue", "Fuzzy")
            .toString()
        )
        .build()
});
  return new SyncResponse(syncRequest.getRequestId(), payload);
}
JSON
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "agentUserId": "1836.15267389",
    "devices": [
      {
        "id": "123",
        "type": "action.devices.types.FAUCET",
        "traits": [
          "action.devices.traits.Dispense"
        ],
        "name": {
          "defaultNames": [
            "Faucet"
          ],
          "name": "Sink Faucet",
          "nicknames": [
            "Faucet"
          ]
        },
        "willReportState": true,
        "attributes": {
          "supportedDispenseItems": [
            {
              "item_name": "water",
              "item_name_synonyms": [
                {
                  "lang": "en",
                  "synonyms": [
                    "water"
                  ]
                },
                {
                  "lang": "es",
                  "synonyms": [
                    "agua"
                  ]
                },
                {
                  "lang": "fr",
                  "synonyms": [
                    "eau"
                  ]
                }
              ],
              "supported_units": [
                "TEASPOONS",
                "TABLESPOONS",
                "FLUID_OUNCES",
                "CUPS",
                "PINTS",
                "QUARTS",
                "GALLONS",
                "MILLILITERS",
                "LITERS",
                "DECILITERS"
              ],
              "default_portion": {
                "amount": 2,
                "unit": "CUPS"
              }
            }
          ],
          "supportedDispensePresets": [
            {
              "preset_name": "cat water bowl",
              "preset_name_synonyms": [
                {
                  "lang": "en",
                  "synonyms": [
                    "cat water bowl",
                    "cat water dish",
                    "cat water cup"
                  ]
                },
                {
                  "lang": "es",
                  "synonyms": [
                    "plato de agua para gato",
                    "bebedero para gato"
                  ]
                },
                {
                  "lang": "fr",
                  "synonyms": [
                    "bol d'eau de chat",
                    "bac à eau pour chat",
                    "tasse d'eau de chat"
                  ]
                }
              ]
            }
          ]
        },
        "customData": {
          "fooValue": 42,
          "barValue": true,
          "catValue": "Fuzzy"
        }
      }
    ]
  }
}
Validator

Device STATES

State Definition
dispenseItems Optional array of objects. Contains state information on all items that the device can dispense, including the following details for each object:
  • itemName: Optional string. The value of an item_name from the supportedDispenseItems attribute (for example, "water", "dog treats", or "cat food").
  • amountRemaining: Optional object. Indicates the amount of that item remaining in the device. If the device is currently dispensing, this reports the remaining amount or what the amount will be once the device finishes dispensing.
    • amount: Float.
    • unit: String.
  • amountLastDispensed: Optional object. Indicates the amount of that item that the device most recently dispensed. If the device is currently dispensing, this should report the amount it dispensed prior to the current dispensing amount.
    • amount: Float.
    • unit: String.
  • isCurrentlyDispensing: Optional boolean. Indicates if the device is currently dispensing this item.

amountRemaining and amountLastDispensed only need to be reported using one unit of measurement.

Multiple items can be provided within the dispenseItems array. Each item name must be unique and must correspond to an item in the supportedDispenseItems attribute.

Example of the device state format:

{
  "states": {
    "dispenseItems": [{
      "itemName": "STRING",
      "amountRemaining": {
        "amount": "FLOAT",
        "unit": "STRING"
      },
      "amountLastDispensed": {
        "amount": "FLOAT",
        "unit": "STRING"
      },
      "isCurrentlyDispensing": "BOOL"
    }]
  }
}

Sample QUERY Request and Response

UserHow many dog treats are remaining in my dispenser?
Assistant<device name> has 83 dog treats remaining.
Request
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": 'action.devices.QUERY',
    "payload": {
      "devices": [{
        "id": "123",
        "customData": {
          "fooValue": 74,
          "barValue": true,
          "bazValue": "foo"
        }
      }]
    }
  }]
}
Node.js
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          online: true,
          dispenseItems: [{
            itemName: 'dog treats',
            amountRemaining: {
              amount: 83.0,
              unit: 'NO_UNITS'
            },
            amountLastDispensed: {
              amount: 2.0,
              unit: 'NO_UNITS'
            },
            isCurrentlyDispensing: false
          }]
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
Java
put("dispenseItems", new HashMap[] {
    new HashMap<String, Object>() {{        put("itemName", "dog treats");
        put("amountRemaining", new HashMap<String, Object>() {{            put("amount", 83.0);
            put("units", "NO_UNITS");
        }});
        put("amountLastDispensed", new HashMap<String, Object>() {{            put("amount", 2.0);
            put("unit", "NO_UNITS");
        }});
        put("isCurrentlyDispensing", false);
    }}});
JSON
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "devices": {
      "123": {
        "online": true,
        "dispenseItems": [
          {
            "itemName": "dog treats",
            "amountRemaining": {
              "amount": 83,
              "unit": "NO_UNITS"
            },
            "amountLastDispensed": {
              "amount": 2,
              "unit": "NO_UNITS"
            },
            "isCurrentlyDispensing": false
          }
        ]
      },
      "status": "SUCCESS"
    }
  }
}
UserHow much water is remaining in my water cooler?
Assistant< device name> has 6.2 gallons of water remaining.
Request
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": 'action.devices.QUERY',
    "payload": {
      "devices": [{
        "id": "123",
        "customData": {
          "fooValue": 74,
          "barValue": true,
          "bazValue": "foo"
        }
      }]
    }
  }]
}
Node.js
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          online: true,
          dispenseItems: [{
            itemName: 'water',
            amountRemaining: {
              amount: 6.2,
              unit: 'GALLONS'
            },
            amountLastDispensed: {
              amount: 1.0,
              unit: 'CUPS'
            },
            isCurrentlyDispensing: false
          }]
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
Java
put("dispenseItems", new HashMap[] {
    new HashMap<String, Object>() {{        put("itemName", "water");
        put("amountRemaining", new HashMap<String, Object>() {{            put("amount", 6.2);
            put("units", "GALLONS");
        }});
        put("amountLastDispensed", new HashMap<String, Object>() {{            put("amount", 1.0);
            put("unit", "CUPS");
        }});
        put("isCurrentlyDispensing", false);
    }}});
JSON
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "devices": {
      "123": {
        "online": true,
        "dispenseItems": [
          {
            "itemName": "water",
            "amountRemaining": {
              "amount": 6.2,
              "unit": "GALLONS"
            },
            "amountLastDispensed": {
              "amount": 1,
              "unit": "CUPS"
            },
            "isCurrentlyDispensing": false
          }
        ]
      },
      "status": "SUCCESS"
    }
  }
}
UserHow much cat food did I give before?
Assistant<device name> last dispensed 2.5 cups of cat food.
Request
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": 'action.devices.QUERY',
    "payload": {
      "devices": [{
        "id": "123",
        "customData": {
          "fooValue": 74,
          "barValue": true,
          "bazValue": "foo"
        }
      }]
    }
  }]
}
Node.js
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          online: true,
          dispenseItems: [{
            itemName: 'cat food',
            amountRemaining: {
              amount: 16.5,
              unit: 'CUPS'
            },
            amountLastDispensed: {
              amount: 2.5,
              unit: 'CUPS'
            },
            isCurrentlyDispensing: false
          }]
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
Java
put("dispenseItems", new HashMap[] {
    new HashMap<String, Object>() {{        put("itemName", "cat food");
        put("amountRemaining", new HashMap<String, Object>() {{            put("amount", 16.5);
            put("units", "CUPS");
        }});
        put("amountLastDispensed", new HashMap<String, Object>() {{            put("amount", 2.5);
            put("unit", "CUPS");
        }});
        put("isCurrentlyDispensing", false);
    }}});
JSON
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "devices": {
      "123": {
        "online": true,
        "dispenseItems": [
          {
            "itemName": "cat food",
            "amountRemaining": {
              "amount": 16.5,
              "unit": "CUPS"
            },
            "amountLastDispensed": {
              "amount": 2.5,
              "unit": "CUPS"
            },
            "isCurrentlyDispensing": false
          }
        ]
      },
      "status": "SUCCESS"
    }
  }
}
UserIs the faucet still dispensing water?
AssistantThe faucet is currently dispensing water.
Request
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": 'action.devices.QUERY',
    "payload": {
      "devices": [{
        "id": "123",
        "customData": {
          "fooValue": 74,
          "barValue": true,
          "bazValue": "foo"
        }
      }]
    }
  }]
}
Node.js
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          online: true,
          dispenseItems: [{
            itemName: 'water',
            isCurrentlyDispensing: true
          }]
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
Java
put("dispenseItems", new HashMap[] {
    new HashMap<String, Object>() {{        put("itemName", "water");
        put("isCurrentlyDispensing", true);
    }}});
JSON
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "devices": {
      "123": {
        "online": true,
        "dispenseItems": [
          {
            "itemName": "water",
            "isCurrentlyDispensing": true
          }
        ]
      },
      "status": "SUCCESS"
    }
  }
}

Device COMMANDS

Command Parameters/Definition
action.devices.commands.Dispense

This command supports dispensing items by a given amount or preset, or without parameters.

Specification Type Parameters
Amount
  • amount: Required float.
  • unit: Required string from set of supported units.
  • item: Optional string from reported supported items.
Preset presetName: Required string from reported supported presets.
Generic If the user requests to dispense from the device without providing any presets or items, no parameters are provided.

Sample EXECUTE Request and Response

UserDispense 3 cups of cat food.
AssistantSure, dispensing 3 cups of cat food from the pet feeder.
Request
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "lambtwirl"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.Dispense",
          "params": {
            "amount": 3.0,
            "unit": "CUPS",
            "item": "cat food"
          }
        }]
      }]
    }
  }]
}
Node.js
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              dispenseItems: [{
                itemName: 'cat food',
                amountRemaining: {
                  amount: 17.0,
                  unit: 'CUPS'
                },
                amountLastDispensed: {
                  amount: 3.0,
                  unit: 'CUPS'
                },
                isCurrentlyDispensing: true
              }]
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('dispense_execute_2', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.Dispense",
              "params": {
                "amount": 3.0,
                "unit": "NO_UNITS",
                "item": "dog treats"
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              dispenseItems: [{
                itemName: 'dog treats',
                amountRemaining: {
                  amount: 27.0,
                  unit: 'NO_UNITS'
                },
                amountLastDispensed: {
                  amount: 3.0,
                  unit: 'NO_UNITS'
                },
                isCurrentlyDispensing: true
              }]
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('dispense_execute_3', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [
        {
          "intent": "action.devices.EXECUTE",
          "payload": {
            "commands": [
              {
                "devices": [
                  {
                    "id": "123",
                    "customData": {
                      "fooValue": 74,
                      "barValue": true,
                      "bazValue": "lambtwirl"
                    }
                  }
                ],
                "execution": [
                  {
                    "command": "action.devices.commands.Dispense",
                    "params": {
                      "amount": 1.0,
                      "unit": "CUPS",
                      "item": "water"
                    }
                  }
                ]
              }
            ]
          }
        }
      ]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              dispenseItems: [{
                itemName: 'water',
                amountLastDispensed: {
                  amount: 1.0,
                  unit: 'CUPS'
                },
                isCurrentlyDispensing: true
              }]
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('dispense_execute_4', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.Dispense",
              "params": {
                "presetName": "cat food bowl"
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              dispenseItems: [{
                itemName: 'cat food',
                amountLastDispensed: {
                  amount: 14.5,
                  unit: 'CUPS'
                },  
                isCurrentlyDispensing: true
              }]
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.Dock
   * http://developers.google.com/actions/smarthome/traits/dock
   */
  it('dock_sync', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onSync((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '1836.15267389',
      devices: [{
        id: '123',
        type: 'action.devices.types.VACUUM',
        traits: [
          'action.devices.traits.Dock'
        ],
        name: {
          defaultNames: ['AAA Cybernetics Corp Vacuum'],
          name: 'vacuum',
          nicknames: []
        },
        willReportState: true,
        deviceInfo: {
          manufacturer: 'sirius',
          model: '442',
          hwVersion: '3.2',
          swVersion: '11.4'
        },
        customData: {
          fooValue: 74,
          barValue: true,
          bazValue: 'lambtwirl'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('dock_query', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          online: true,
          isDocked: true
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('dock_execute', function() {
    const executeRequest =
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "sheepdip"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.Dock"
        }]
      }]
    }
  }]
}
    ;
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
            isDocked: true
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(executeRequest);
  });

    /*
   * action.devices.traits.FanSpeed
   * http://developers.google.com/actions/smarthome/traits/fanspeed
   */
  it('fanspeed_sync', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onSync((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '1836.15267389',
      devices: [{
        id: '123',
        type: 'action.devices.types.FAN',
        traits: [
          'action.devices.traits.FanSpeed'
        ],
        name: {
          defaultNames: ['Sirius Cybernetics Corporation 33321'],
          name: 'Fan',
          nicknames: ['wall fan']
        },
        willReportState: true,
        attributes: {
          availableFanSpeeds: {
            speeds: [{
              speed_name: 'Low',
              speed_values: [{
                speed_synonym: ['low', 'slow'],
                lang: 'en'
              }, {
                speed_synonym: ['low', 'slow'],
                lang: 'de'
              }]
            }, {
              speed_name: 'High',
              speed_values: [{
                speed_synonym: ['high'],
                lang: 'en'
              }, {
                speed_synonym: ['high'],
                lang: 'de'
              }]
            }],
            ordered: true
          },
          reversible: true
        },
        deviceInfo: {
          manufacturer: 'Sirius Cybernetics Corporation',
          model: '233451',
          hwVersion: '3.2',
          swVersion: '11.4'
        },
        customData: {
          fooValue: 74,
          barValue: true,
          bazValue: 'lambtwirl'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('fanspeed_query', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          on: true,
          online: true,
          currentFanSpeedSetting: 'S2'
        },
        456: {
          on: true,
          online: true,
          currentFanSpeedSetting: 'S1'
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('fanspeed_execute_1', function() {
    const executeRequest =
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "lambtwirl"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.SetFanSpeed",
          "params": {
            "fanSpeed": "S1"
          }
        }]
      }]
    }
  }]
}
    ;
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
          currentFanSpeedSetting: 'S1'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('fanspeed_execute_2', function() {
    const executeRequest =
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "lambtwirl"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.SetFanSpeed",
          "params": {
            "fanSpeed": "S2"
          }
        }]
      }]
    }
  }]
}
    ;
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
          currentFanSpeedSetting: 'S2'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.Fill
   * https://devsite.googleplex.com/actions/smarthome/traits/eap/fill
   */
  it('fill_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.BATHTUB',
            traits: [
              'action.devices.traits.OnOff',
              'action.devices.traits.Fill'
            ],
            name: {
              defaultNames: ['Smart Tub'],
              name: 'Tub',
              nicknames: ['Master Bathtub', 'Rubber Duck Pond']
            },
            willReportState: true,
            attributes: {
              availableFillLevels: {
                levels: [{
                  level_name: 'half_key',
                  level_values: [{
                    level_synonym: ['half', 'halfway', 'one half'],
                    lang: 'en'
                  }]
                }],
                ordered: true
              }
            },
            deviceInfo: {
              manufacturer: 'Sirius Cybernetics Corporation',
              model: '492134',
              hwVersion: '3.2',
              swVersion: '11.4'
            },
            customData: {
              fooValue: 74,
              barValue: true,
              bazValue: 'lambtwirl'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('fill_query', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              on: true,
              online: true,
              currentFillLevel: 'half',
              isFilled: true
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('fill_execute', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.Fill",
              "params": {
                "fill": true,
                "fillLevel": "half"
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              currentFillLevel: 'half',
              isFilled: true
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.HumiditySetting
   * https://devsite.googleplex.com/actions/smarthome/traits/eap/humiditysetting
   */
  it('humiditysetting_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.HUMIDIFIER',
            traits: [
              'action.devices.traits.StartStop',
              'action.devices.traits.FanSpeed',
              'action.devices.traits.HumiditySetting'
            ],
            name: {
              defaultNames: ['Sirius Cybernetics Corporation Humidifer'],
              name: 'Humidifier',
              nicknames: ['Bedroom Humidifier']
            },
            roomHint: 'Bedroom',
            willReportState: true,
            attributes: {
              pausable: true,
              availableFanSpeeds: {
                speeds: [{
                  speed_name: 'Low',
                  speed_values: [{
                    speed_synonym: ['low', 'slow'],
                    lang: 'en'
                  }, {
                    speed_synonym: ['niedrig', 'schleppend'],
                    lang: 'de'
                  }]
                }, {
                  speed_name: 'High',
                  speed_values: [{
                    speed_synonym: ['high'],
                    lang: 'en'
                  }, {
                    speed_synonym: ['hoch'],
                    lang: 'de'
                  }]
                }],
                ordered: true
              },
              reversible: true,
              humiditySetpointRange: {
                minPercent: 30,
                maxPercent: 100
              }
            },
            deviceInfo: {
              manufacturer: 'Sirius Cybernetics Corporation',
              model: '8675309',
              hwVersion: '3.2',
              swVersion: '11.4'
            },
            customData: {
              fooValue: 74,
              barValue: true,
              bazValue: 'goatjive'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('humiditysetting_query', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              humiditySetpointPercent: 20,
              humidityAmbientPercent: 15
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('humiditysetting_execute', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.SetHumidity",
              "params": {
                "humidity": 20
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              humiditySetpointPercent: 20,
              humidityAmbientPercent: 15
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.InputSelector
   * https://devsite.googleplex.com/actions/smarthome/traits/eap/inputselector
   */
  it('inputselector_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.TV',
            traits: [
              'action.devices.traits.OnOff',
              'action.devices.traits.InputSelector'
            ],
            name: {
              defaultNames: ['television'],
              name: 'Livingroom Television',
              nicknames: ['tv']
            },
            willReportState: true,
            attributes: {
              availableInputs: [{
                key: 'hdmi_1',
                names: [{
                  name_synonym: ['hdmi 1', '1st hdmi'],
                  lang: 'en'
                }, {
                  name_synonym: ['hdml 1', 'zuerst hdmi'],
                  lang: 'de'
                }]
              }, {
                key: 'usb_1',
                names: [{
                  name_synonym: ['usb 1', '1st usb'],
                  lang: 'en'
                }]
              }],
              orderedInputs: true
            },
            customData: {
              fooValue: 74,
              barValue: true,
              bazValue: 'foo bar'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('inputselector_query', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              on: true,
              online: true,
              currentInput: 'hdmi_1'
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('inputselector_execute', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.SetInput",
              "params": {
                "newInput": "hdmi_1"
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              currentInput: 'hdmi_1'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.LightEffects
   * http://developers.google.com/actions/smarthome/traits/lighteffects
   */
  it('lighteffects_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.LIGHT',
            traits: [
              'action.devices.traits.OnOff',
              'action.devices.traits.LightEffects'
            ],
            name: {
              defaultNames: ['AAA bulb A19 color hyperglow'],
              name: 'Lamp',
              nicknames: ['Reading lamp']
            },
            willReportState: true,
            attributes: {
              supportedEffects: ['colorLoop'],
            },
            deviceInfo: {
              manufacturer: 'BrandX',
              model: 'hg11',
              hwVersion: '1.2',
              swVersion: '5.4'
            },
            customData: {
              fooValue: 12,
              barValue: false,
              bazValue: 'goatjive'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('lighteffects_query', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              on: true,
              activeLightEffect: 'colorLoop',
              online: true
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('lighteffects_execute', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "goatjive"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.ColorLoop"
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              on: true,
              activeLightEffect: 'colorLoop'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.Locator
   * http://developers.google.com/actions/smarthome/traits/locator
   */
  it('locator_sync', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onSync((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '1836.15267389',
      devices: [{
        id: '123',
        type: 'action.devices.types.VACUUM',
        traits: [
          'action.devices.traits.Locator'
        ],
        name: {
          defaultNames: ['Smart Vacuum'],
          name: 'SmartVac',
          nicknames: ['Vac']
        },
        willReportState: true,
        deviceInfo: {
          manufacturer: 'Smart Home Provider',
          model: 'g1340',
          swVersion: '1.0.31',
          hwVersion: '1.1'
        },
        customData: {
          smartHomeProviderId: 'fiGgZ0vrhFRgwf5XJkna2GRwO8z80J'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('locator_execute', function() {
    const executeRequest =
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "lambtwirl"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.Locate",
          "params": {
            "silence": false,
            "lang": "en"
          }
        }]
      }]
    }
  }]
}
    ;
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
          generatedAlert: true
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.LockUnlock
   * https://devsite.googleplex.com/actions/smarthome/traits/eap/lockunlock
   */
  it('lockunlock_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.LOCK',
            traits: [
              'action.devices.traits.LockUnlock'
            ],
            name: {
              defaultNames: ['Sirius Cybernetics Corporation 33321'],
              name: 'Lock',
              nicknames: ['front door']
            },
            willReportState: true,
            deviceInfo: {
              manufacturer: 'Sirius Cybernetics Corporation',
              model: '492135',
              hwVersion: '3.2',
              swVersion: '11.4'
            },
            customData: {
              fooValue: 74,
              barValue: true,
              bazValue: 'lambtwirl'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('lockunlock_query', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              on: true,
              online: true
            },
            456: {
              online: true,
              isLocked: true,
              isJammed: false
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('lockunlock_execute', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
               }
             }],
            "execution": [{
              "command": "action.devices.commands.LockUnlock",
              "params": {
                "lock": true
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              isLocked: true,
              isJammed: false
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.Modes
   * http://developers.google.com/actions/smarthome/traits/modes
   */
  it('modes_sync', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onSync((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '1836.15267389',
      devices: [{
        id: '123',
        type: 'action.devices.types.WASHER',
        traits: [
          'action.devices.traits.Modes'
        ],
        name: {
          defaultNames: ['AAA Cybernetics Corporation Washer 3421'],
          name: 'Washer',
          nicknames: ['clothes washer']
        },
        willReportState: true,
        attributes: {
          availableModes: [{
            name: 'load',
            name_values: [{
              name_synonym: ['load', 'size', 'load size'],
              lang: 'en'
            }],
            settings: [{
              setting_name: 'small',
              setting_values: [{
                setting_synonym: ['small', 'half'],
                lang: 'en'
              }]
            }, {
              setting_name: 'large',
              setting_values: [{
                setting_synonym: ['large', 'full'],
                lang: 'en'
              }]
            }],
            ordered: true
          }]
        },
        deviceInfo: {
          manufacturer: 'AAA Cybernetics Corporation',
          model: '233451',
          hwVersion: '3.2',
          swVersion: '11.4'
        },
        customData: {
          fooValue: 74,
          barValue: true,
          bazValue: 'lambtwirl'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('modes_query', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          on: true,
          online: true,
          currentModeSettings: {
            drytype: 'permpress',
            load: 'large'
          }
        },
        456: {
          on: true,
          online: true,
          isPaused: true,
          isRunning: false,
          currentModeSettings: {
            temperature: 'hot',
            load: 'large'
          }
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('modes_execute', function() {
    const executeRequest =
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "lambtwirl"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.SetModes",
          "params": {
            "updateModeSettings": {
              "drytype": "permpress"
            }
          }
        }]
      }]
    }
  }]
}
    ;
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
           currentModeSettings: {
             drytype: 'permpress',
             load: 'large'
           }
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.NetworkControl
   * https://devsite.googleplex.com/actions/smarthome/traits/eap/networkcontrol
   */
  it('networkcontrol_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.ROUTER',
            traits: [
              'action.devices.traits.Reboot',
              'action.devices.traits.SoftwareUpdate',
              'action.devices.traits.NetworkControl',
              'action.devices.traits.Modes',
              'action.devices.traits.Toggles'
            ],
            name: {
              defaultNames: ['Sirius Cybernetics Router 2331'],
              name: 'Router',
              nicknames: ['Upstairs Router']
            },
            willReportState: true,
            attributes: {
              supportsGuestNetwork: true,
              availableToggles: [{
                name: 'Power Saver Mode',
                values: [{
                  name_synonym: ['Power Saver', 'Low Power Mode'],
                  lang: 'en'
                }]
              }],
              availableModes: [{
                name: 'Parental Control',
                name_values: [{
                  name_synonym: ['Parental Restriction', 'Restriction'],
                  lang: 'en'
                }],
                settings: [{
                  setting_name: 'off',
                  setting_values: [{
                    setting_synonym: ['Paused'],
                    lang: 'en'
                  }]
                }, {
                  setting_name: 'High',
                  setting_values: [{
                    setting_synonym: ['High'],
                    lang: 'en'
                  }]
                }],
                ordered: true
              }]
            },
            deviceInfo: {
              manufacturer: 'Sirius Cybernetics',
              model: '2331B',
              hwVersion: '11.2',
              swVersion: '11.4'
            },
            customData: {
              fooValue: 74,
              barValue: true,
              bazValue: 'lambtwirl'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('networkcontrol_query', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              on: true,
              online: true,
              networkEnabled: true,
              networkSettings: {
                ssid: 'home-network-123',
                password: 'home-password-123'
              },
              numConnectedDevices: 4,
              networkSpeedMbps: 100.8
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('networkcontrol_execute_1', function() {
    // Turn on the Internet
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
               }
             }],
            "execution": [{
              "command": "action.devices.commands.EnableDisableNetwork",
              "params": {
                "enable": true
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              networkEnabled: true
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('networkcontrol_execute_2', function() {
    // Turn on the guest network
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
               }
             }],
            "execution": [{
              "command": "action.devices.commands.EnableDisableGuestNetwork",
              "params": {
                "enable": true
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              guestNetworkEnabled: true
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('networkcontrol_execute_3', function() {
    // Disabled a network profile
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
               }
             }],
            "execution": [{
              "command": "action.devices.commands.EnableDisableNetworkProfile",
              "params": {
                "profile": "Lucas",
                "enable": false
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              guestNetworkEnabled: true
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('networkcontrol_execute_4', function() {
    // Turn on the guest network
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
               }
             }],
            "execution": [{
              "command": "action.devices.commands.ScanNetwork",
              "params": {
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {}
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.OnOff
   * http://developers.google.com/actions/smarthome/traits/onoff
   */
  it('onoff_sync', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onSync((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '1836.15267389',
      devices: [{
        id: '123',
        type: 'action.devices.types.LIGHT',
        traits: [
          'action.devices.traits.OnOff'
        ],
        name: {
          defaultNames: ['AAA bulb A19 color hyperglow'],
          name: 'lamp1',
          nicknames: ['reading lamp']
        },
        willReportState: true,
        attributes: {
          commandOnlyOnOff: false
        },
        deviceInfo: {
          manufacturer: 'BrandX',
          model: 'hg11',
          hwVersion: '1.2',
          swVersion: '5.4'
        },
        customData: {
          fooValue: 12,
          barValue: false,
          bazValue: 'dancing alpaca'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('onoff_query', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          on: true,
          online: true
        },
        456: {
          on: false,
          online: true
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('onoff_execute', function() {
    const executeRequest =
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "sheepdip"
          }
        }, {
          "id": "456",
          "customData": {
            "fooValue": 36,
            "barValue": false,
            "bazValue": "moarsheep"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.OnOff",
          "params": {
            "on": true
          }
        }]
      }]
    }
  }]
}
    ;
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
          on: true,
          online: true
        }
      }, {
        ids: ['456'],
        status: 'ERROR',
        errorCode: 'deviceTurnedOff'
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.Reboot
   * https://devsite.googleplex.com/actions/smarthome/traits/eap/reboot
   */
  it('reboot_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.LIGHT',
            traits: [
              'action.devices.traits.OnOff',
              'action.devices.traits.Reboot',
            ],
            name: {
              defaultNames: ['Sirius Cybernetics Light 2331'],
              name: 'Light',
              nicknames: ['Upstairs Light']
            },
            willReportState: true,
            deviceInfo: {
              manufacturer: 'Sirius Cybernetics',
              model: '2331B',
              hwVersion: '11.2',
              swVersion: '11.4'
            },
            customData: {
              fooValue: 2019,
              barValue: true,
              bazValue: 'goatjive'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('reboot_execute', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.Reboot",
              "params": {
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {}
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.Rotation
   * https://devsite.googleplex.com/actions/smarthome/traits/eap/rotation
   */
  it('rotation_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.BLINDS',
            traits: [
              'action.devices.traits.Rotation'
            ],
            name: {
              defaultNames: ['Blinds window'],
              name: 'Kitchen window',
              nicknames: ['Sink window']
            },
            willReportState: true,
            attributes: {
              supportsDegrees: true,
              supportsPercent: true,
              rotationDegreesRange: {
                rotationDegreesMin: 0.0,
                rotationDegreesMax: 180.0
              }
            },
            deviceInfo: {
              manufacturer: 'AAA',
              model: 'xyz',
              hwVersion: '3.2',
              swVersion: '11.4'
            },
            customData: {
              fooValue: 74,
              barValue: true,
              bazValue: 'lambtwirl'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('rotation_query_1', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              online: true,
              rotationPercent: 25.0
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('rotation_query_2', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              online: true,
              rotationDegrees: 90.0
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('rotation_execute_1', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "456",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.RotateAbsolute",
              "params": {
                "rotationPercent": 50.0
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              rotationPercent: 50.0
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('rotation_execute_2', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.RotateAbsolute",
              "params": {
                "rotationDegrees": 15.0
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              rotationDegrees: 15.0,
              online: true
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.RunCycle
   * http://developers.google.com/actions/smarthome/traits/runcycle
   */
  it('runcycle_sync', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onSync((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '1836.15267389',
      devices: [{
        id: '123',
        type: 'action.devices.types.DRYER',
        traits: [
          'action.devices.traits.RunCycle'
        ],
        name: {
          defaultNames: ['AAA Super Dryer'],
          name: 'Washer',
          nicknames: ['clothes dryer']
        },
        willReportState: true,
        deviceInfo: {
          manufacturer: 'AAA Corporation',
          model: '492134',
          hwVersion: '3.2',
          swVersion: '11.4'
        },
        customData: {
          fooValue: 74,
          barValue: true,
          bazValue: 'lambtwirl'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('runcycle_query', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          currentRunCycle: [{
            currentCycle: 'rinse',
            nextCycle: 'spin',
            lang: 'en'
          }],
          currentTotalRemainingTime: 1212,
          currentCycleRemainingTime: 301
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  /*
   * action.devices.traits.Scene
   * http://developers.google.com/actions/smarthome/traits/scene
   */
  it('scene_sync', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onSync((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '1836.15267389',
      devices: [{
        id: '123',
        type: 'action.devices.types.SCENE',
        traits: [
          'action.devices.traits.Scene'
        ],
        name: {
          defaultNames: [],
          name: 'Party Mode',
          nicknames: []
        },
        willReportState: true, // should be false, as there's no state
        attributes: {
          sceneReversible: true
        },
        // Note -- probably no device info for virtual devices.
        customData: {
          fooValue: 74,
          barValue: true,
          bazValue: 'lambtwirl'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('scene_query', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          online: true
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('scene_execute', function() {
    const executeRequest =
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "lambtwirl"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.ActivateScene",
          "params": {
            "deactivate": false
          }
        }]
      }]
    }
  }]
}
    ;
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
          // Note -- scenes are stateless.
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.SensorState
   * https://devsite.googleplex.com/actions/smarthome/traits/eap/sensorstate
   */
  it('sensorstate_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.SENSOR',
            traits: [
              'action.devices.traits.SensorState'
            ],
            name: {
              defaultNames: ['Default sensor name'],
              name: 'Front door',
              nicknames: ['front door sensor']
            },
            attributes: {
              sensorStatesSupported: [{
                name: "CarbonMonoxideLevel",
                descriptiveCapabilities: {
                  availableStates: ['carbon monoxide detected', 'high', 'no carbon monoxide detected', 'unknown'],
                  trackHistory: false
                },
                numericCapabilities: {
                  rawValueUnit: 'PARTS_PER_MILLION'
                }
              }]
            },
            deviceInfo: {
              manufacturer: 'nest',
              model: 'c110',
              hwVersion: '3.2',
              swVersion: '11.4'
            },
            customData: {
              fooValue: 42,
              barValue: true,
              bazValue: 'sheepdip'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('sensorstate_query', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              on: true,
              online: true,
              currentSensorStateData: [{
                name: 'CarbonMonoxideLevel',
                currentSensorState: 'high',
                rawValue: 100.0
              }]
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  /*
   * action.devices.traits.SoftwareUpdate
   * https://devsite.googleplex.com/actions/smarthome/traits/eap/softwareupdate
   */
  it('softwareupdate_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.LIGHT',
            traits: [
              'action.devices.traits.OnOff',
              'action.devices.traits.SoftwareUpdate',
            ],
            name: {
              defaultNames: ['Sirius Cybernetics Light 2331'],
              name: 'Light',
              nicknames: ['Upstairs Light']
            },
            willReportState: true,
            deviceInfo: {
              manufacturer: 'Sirius Cybernetics',
              model: '2331B',
              hwVersion: '11.2',
              swVersion: '11.4'
            },
            customData: {
              fooValue: 2019,
              barValue: true,
              bazValue: 'goatjive'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('softwareupdate_query', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              on: true,
              online: true,
              lastSoftwareUpdateUnixTimestampSec: 1542059207
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('softwareupdate_execute', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.SoftwareUpdate",
              "params": {
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {}
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.StartStop
   * http://developers.google.com/actions/smarthome/traits/startstop
   */
  it('startstop_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.WASHER',
            traits: [
              'action.devices.traits.StartStop'
            ],
            name: {
              defaultNames: ['Sirius Cybernetics Corporation Washer 3421'],
              name: 'Washer',
              nicknames: ['clothes washer']
            },
            willReportState: true,
            attributes: {
              pausable: true,
              availableZones: ['kitchen', 'living room', 'master bedroom']
            },
            deviceInfo: {
              manufacturer: 'AAA Cybernetics Corporation',
              model: '233451',
              hwVersion: '3.2',
              swVersion: '11.4'
            },
            customData: {
              fooValue: 74,
              barValue: true,
              bazValue: 'lambtwirl'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('startstop_query', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              online: true,
              isPaused: true,
              isRunning: false,
              activeZones: ['kitchen']
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('startstop_execute_1', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "sheepdip"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.StartStop",
              "params": {
                "start": true
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              isRunning: true,
              isPaused: false
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('startstop_execute_2', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "sheepdip"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.StartStop",
              "params": {
                "start": true,
                "zone": "kitchen"
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              isRunning: true,
              isPaused: false,
              activeZones: ['kitchen']
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('startstop_execute_3', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "sheepdip"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.PauseUnpause",
              "params": {
                "pause": true
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              isRunning: true,
              isPaused: true
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.StatusReport
   * https://developers.google.com/actions/smarthome/traits/statusreport
   */
  it('statusreport_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.SECURITYSYSTEM',
            traits: [
              'action.devices.traits.StatusReport'
            ],
            name: {
              defaultNames: ['Sirius Cybernetics Corp Security System'],
              name: 'Security system',
              nicknames: ['Security system']
            },
            willReportState: true,
            attributes: {},
            deviceInfo: {
              manufacturer: 'Sirius Cybernetics Corporation',
              model: '442',
              hwVersion: '3.2',
              swVersion: '11.4'
            },
            customData: {
              fooValue: 74,
              barValue: true,
              bazValue: 'lambtwirl'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('statusreport_query', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              on: true,
              online: true,
              currentStatusReport: [{
                blocking: false,
                deviceTarget: '123',
                priority: 0,
                statusCode: 'lowBattery'
              }, {
                blocking: false,
                deviceTarget: 'front_window_id',
                priority: 1,
                statusCode: 'deviceOpen'
              }, {
                blocking: false,
                deviceTarget: 'back_window_id',
                priority: 1,
                statusCode: 'deviceOpen'
              }]
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  /*
   * action.devices.traits.Timer
   * https://devsite.googleplex.com/actions/smarthome/traits/eap/timer
   */
  it('timer_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.OVEN',
            traits: [
              'action.devices.traits.TemperatureControl',
              'action.devices.traits.StartStop',
              'action.devices.traits.Timer'
            ],
            name: {
              defaultNames: ['AAA Smart Oven'],
              name: 'Oven',
              nicknames: ['conventional oven']
            },
            willReportState: true,
            attributes: {
              temperatureRange: {
                minThresholdCelsius: 65.5,
                maxThresholdCelcius: 288
              },
              temperatureUnitForUX: 'F',
              pausable: false,
              maxTimerLimitSec: 7200,
              commandOnlyTimer: false
            },
            deviceInfo: {
              manufacturer: 'Smart Oven Manufacturers',
              model: 'F600G',
              hwVersion: '3.2',
              swVersion: '11.4'
            },
            customData: {
              fooValue: 74,
              barValue: true,
              bazValue: 'lambtwirl'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('timer_query_1', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              online: true,
              timerRemainingSec: 60
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('timer_query_2', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              online: true,
              timerRemainingSec: 60,
              timerPaused: true
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('timer_query_3', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          errorCode: 'noTimerExists'
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('timer_execute_1', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerStart",
              "params": {
                "timerTimeSec": 300
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              timerRemainingSec: 300
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('timer_execute_1_error', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerStart",
              "params": {
                "timerTimeSec": 300
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          errorCode: 'valueOutOfRange'
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('timer_execute_2', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerAdjust",
              "params": {
                "timerTimeSec": 60
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              /* Added 60s to a timer with 32s remaining */
              timerRemainingSec: 92
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('timer_execute_2_error', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerAdjust",
              "params": {
                "timerTimeSec": 60
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          errorCode: 'noTimerExists'
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('timer_execute_3', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerAdjust",
              "params": {
                "timerTimeSec": -30
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              /* Removed 30s from a timer with 45s remaining */
              timerRemainingSec: 15
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('timer_execute_3_error', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerAdjust",
              "params": {
                "timerTimeSec": -30
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          errorCode: 'valueOutOfRange'
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('timer_execute_4', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerPause"
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              timerRemainingSec: 240,
              timerPaused: true
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('timer_execute_5', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerResume"
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              timerRemainingSec: 129
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('timer_execute_5_error', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerResume"
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          errorCode: 'noTimerExists'
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('timer_execute_6', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerCancel"
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS',
            states: {
              timerRemainingSec: 0
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('timer_execute_6_error', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerCancel"
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          errorCode: 'noTimerExists'
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('timer_execute_6_error_2', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.TimerCancel"
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          errorCode: 'unknownError'
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.TemperatureControl
   * http://developers.google.com/actions/smarthome/traits/temperaturecontrol
   */
  it('temperaturecontrol_sync', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onSync((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '1836.15267389',
      devices: [{
        id: '123',
        type: 'action.devices.types.KETTLE',
        traits: [
          'action.devices.traits.OnOff',
          'action.devices.traits.TemperatureControl'
        ],
        name: {
          defaultNames: ['Smart Kettle 1'],
          name: 'Smart Kettle 1',
          nicknames: ['Little teapot']
        },
        willReportState: true,
        attributes: {
          temperatureRange: {
            minThresholdCelsius: 30,
            maxThresholdCelsius: 100
          },
          temperatureStepCelsius: 1,
          temperatureUnitForUX: 'C'
        },
        deviceInfo: {
          manufacturer: 'Smart Home Provider',
          model: 'g1340',
          swVersion: '1.0.31',
          hwVersion: '1.1'
        },
        customData: {
          smartHomeProviderId: 'fiGgZ0vrhFRgwf5XJkna2GRwO8z80J'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('temperaturecontrol_query', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          on: true,
          online: true,
          temperatureSetpointCelsius: 177,
          temperatureAmbientCelsius: 150
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('temperaturecontrol_execute', function() {
    const executeRequest =
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "lambtwirl"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.SetTemperature",
          "params": {
            "temperature": 176.67
          }
        }]
      }]
    }
  }]
}
    ;
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
          temperatureSetpointCelsius: 177,
          temperatureAmbientCelsius: 140
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.TemperatureSetting
   * http://developers.google.com/actions/smarthome/traits/temperaturesetting
   */
  it('temperaturesetting_sync', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onSync((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '1836.15267389',
      devices: [{
        id: '123',
        type: 'action.devices.types.THERMOSTAT',
        traits: [
          'action.devices.traits.TemperatureSetting'
        ],
        name: {
          defaultNames: ['Honeywell Thermostat T-1000'],
          name: 'Homer Simpson Thermostat',
          nicknames: ['living room thermostat']
        },
        willReportState: true,
        attributes: {
          availableThermostatModes: 'off,heat,cool,on',
          thermostatTemperatureUnit: 'F'
        },
        deviceInfo: {
          manufacturer: 'honeywell',
          model: 't-1000',
          hwVersion: '3.2',
          swVersion: '11.4'
        },
        customData: {
          fooValue: 74,
          barValue: true,
          bazValue: 'lambtwirl'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('temperaturesetting_query', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          online: true,
          thermostatMode: 'cool',
          thermostatTemperatureSetpoint: 23.0,
          thermostatTemperatureAmbient: 25.1,
          thermostatHumidityAmbient: 45.3
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('temperaturesetting_execute', function() {
    const executeRequest =
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "sheepdip"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.ThermostatTemperatureSetpoint",
          "params": {
            "thermostatTemperatureSetpoint": 22.0
          }
        }]
      }]
    }
  }]
}
    ;
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
          thermostatMode: 'cool',
          thermostatTemperatureSetpoint: 22.0,
          thermostatTemperatureAmbient: 25.1,
          thermostatHumidityAmbient: 43.2
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.Toggles
   * http://developers.google.com/actions/smarthome/traits/toggles
   */
  it('toggles_sync', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onSync((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: '1836.15267389',
      devices: [{
        id: '123',
        type: 'action.devices.types.DRYER',
        traits: [
          'action.devices.traits.Toggles'
        ],
        name: {
          defaultNames: ['AAA Super Dryer'],
          name: 'Washer',
          nicknames: ['clothes dryer']
        },
        willReportState: true,
        attributes: {
          availableToggles: [{
            name: 'sterilization',
            name_values: [{
              name_synonym: ['bio-clean', 'ultrasound'],
              lang: 'en'
            }]
          }, {
            name: 'energysaving',
            name_values: [{
              name_synonym: ['normal', 'medium', 'high'],
              lang: 'en'
            }]
          }]
        },
        deviceInfo: {
          manufacturer: 'AAA Corporation',
          model: '492134',
          hwVersion: '3.2',
          swVersion: '11.4'
        },
        customData: {
          fooValue: 74,
          barValue: true,
          bazValue: 'lambtwirl'
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('toggles_query', function() {
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onQuery((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      devices: {
        123: {
          on: true,
          online: true,
          currentToggleSettings: {
            sterilization: true
          }
        },
        456: {
          on: true,
          online: true,
          currentToggleSettings: {
            sterilization: false
          }
        },
        status: 'SUCCESS'
      }
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('toggles_execute', function() {
    const executeRequest =
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "lambtwirl"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.SetToggles",
          "params": {
            "updateToggleSettings": {
              "sterilization": true
            }
          }
        }]
      }]
    }
  }]
}
    ;
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
           currentToggleSettings: {
             sterilization: true,
             energysaving: false
           }
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
  this.test.app = app;
  this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  /*
   * action.devices.traits.Volume
   * https://devsite.googleplex.com/actions/smarthome/traits/eap/volume
   */
  it('volume_sync', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onSync((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          agentUserId: '1836.15267389',
          devices: [{
            id: '123',
            type: 'action.devices.types.SPEAKER',
            traits: [
              'action.devices.traits.OnOff',
              'action.devices.traits.Volume'
            ],
            name: {
              defaultNames: ['Speaker'],
              name: 'Livingroom speaker',
              nicknames: ['Speaker']
            },
            willReportState: true,
            attributes: {
              volumeMaxLevel: 11,
              volumeCanMuteAndUnmute: true,
              volumeDefault: 6,
              levelStepSize: 2,
              commandOnlyVolume: false,
            },
            customData: {
              fooValue: 74,
              barValue: true,
              bazValue: 'foo bar'
            }
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(syncRequest);
  });

  it('volume_query', function() {
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onQuery((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          devices: {
            123: {
              on: true,
              online: true,
              currentVolume: 8,
              isMuted: false
            },
            status: 'SUCCESS'
          }
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(queryRequest);
  });

  it('volume_execute_1', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.setVolume",
              "params": {
                "volumeLevel​": 6
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'SUCCESS'
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

  it('volume_execute_2', function() {
    const executeRequest =
    {
      "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
      "inputs": [{
        "intent": "action.devices.EXECUTE",
        "payload": {
          "commands": [{
            "devices": [{
              "id": "123",
              "customData": {
                "fooValue": 74,
                "barValue": true,
                "bazValue": "lambtwirl"
              }
            }],
            "execution": [{
              "command": "action.devices.commands.volumeRelative",
              "params": {
                "volumeRelativeLevel": -1
              }
            }]
          }]
        }
      }]
    }
    ;
    'use strict';

    const {smarthome} = require('actions-on-google');
    const functions = require('firebase-functions');

    const app = smarthome();

    app.onExecute((body, headers) => {
      return {
        requestId: body.requestId,
        payload: {
          commands: [{
            ids: ['123'],
            status: 'PENDING'
          }]
        }
      };
    });

    // ...

    exports.smarthome = functions.https.onRequest(app);
    this.test.app = app;
    this.test.body = SmartHome.createRequestBody(executeRequest);
  });

});

Java
put("dispenseItems", new HashMap[] {
    new HashMap<String, Object>() {{        put("itemName", "cat food");
        put("amountRemaining", new HashMap<String, Object>() {{            put("amount", 17.0);
            put("units", "CUPS");
        }});
        put("amountLastDispensed", new HashMap<String, Object>() {{            put("amount", 3.0);
            put("unit", "CUPS");
        }});
        put("isCurrentlyDispensing", true);
    }}});
JSON
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [
      {
        "ids": [
          "123"
        ],
        "status": "SUCCESS",
        "states": {
          "dispenseItems": [
            {
              "itemName": "cat food",
              "amountRemaining": {
                "amount": 17,
                "unit": "CUPS"
              },
              "amountLastDispensed": {
                "amount": 3,
                "unit": "CUPS"
              },
              "isCurrentlyDispensing": true
            }
          ]
        }
      }
    ]
  }
}
UserGive me 3 dog treats.
AssistantSure, dispensing 3 of those from the pet feeder.
Request
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "lambtwirl"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.Dispense",
          "params": {
            "amount": 3.0,
            "unit": "NO_UNITS",
            "item": "dog treats"
          }
        }]
      }]
    }
  }]
}
Node.js
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
          dispenseItems: [{
            itemName: 'dog treats',
            amountRemaining: {
              amount: 27.0,
              unit: 'NO_UNITS'
            },
            amountLastDispensed: {
              amount: 3.0,
              unit: 'NO_UNITS'
            },
            isCurrentlyDispensing: true
          }]
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
Java
put("dispenseItems", new HashMap[] {
    new HashMap<String, Object>() {{        put("itemName", "dog treats");
        put("amountRemaining", new HashMap<String, Object>() {{            put("amount", 27.0);
            put("units", "NO_UNITS");
        }});
        put("amountLastDispensed", new HashMap<String, Object>() {{            put("amount", 3.0);
            put("unit", "NO_UNITS");
        }});
        put("isCurrentlyDispensing", true);
    }}});
JSON
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [
      {
        "ids": [
          "123"
        ],
        "status": "SUCCESS",
        "states": {
          "dispenseItems": [
            {
              "itemName": "dog treats",
              "amountRemaining": {
                "amount": 27,
                "unit": "NO_UNITS"
              },
              "amountLastDispensed": {
                "amount": 3,
                "unit": "NO_UNITS"
              },
              "isCurrentlyDispensing": true
            }
          ]
        }
      }
    ]
  }
}
UserGive me 1 cup of water.
AssistantSure, dispensing 1 cup of water from the faucet.
Request
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [
    {
      "intent": "action.devices.EXECUTE",
      "payload": {
        "commands": [
          {
            "devices": [
              {
                "id": "123",
                "customData": {
                  "fooValue": 74,
                  "barValue": true,
                  "bazValue": "lambtwirl"
                }
              }
            ],
            "execution": [
              {
                "command": "action.devices.commands.Dispense",
                "params": {
                  "amount": 1.0,
                  "unit": "CUPS",
                  "item": "water"
                }
              }
            ]
          }
        ]
      }
    }
  ]
}
Node.js
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
          dispenseItems: [{
            itemName: 'water',
            amountLastDispensed: {
              amount: 1.0,
              unit: 'CUPS'
            },
            isCurrentlyDispensing: true
          }]
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
Java
put("dispenseItems", new HashMap[] {
    new HashMap<String, Object>() {{        put("itemName", "water");
        put("amountLastDispensed", new HashMap<String, Object>() {{            put("amount", 1.0);
            put("unit", "CUPS");
        }});
        put("isCurrentlyDispensing", true);
    }}});
JSON
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [
      {
        "ids": [
          "123"
        ],
        "status": "SUCCESS",
        "states": {
          "dispenseItems": [
            {
              "itemName": "water",
              "amountLastDispensed": {
                "amount": 1,
                "unit": "CUPS"
              },
              "isCurrentlyDispensing": true
            }
          ]
        }
      }
    ]
  }
}
UserFill the cat food bowl.
AssistantSure, filling the cat food bowl.
Request
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "inputs": [{
    "intent": "action.devices.EXECUTE",
    "payload": {
      "commands": [{
        "devices": [{
          "id": "123",
          "customData": {
            "fooValue": 74,
            "barValue": true,
            "bazValue": "lambtwirl"
          }
        }],
        "execution": [{
          "command": "action.devices.commands.Dispense",
          "params": {
            "presetName": "cat food bowl"
          }
        }]
      }]
    }
  }]
}
Node.js
'use strict';

const {smarthome} = require('actions-on-google');
const functions = require('firebase-functions');

const app = smarthome();

app.onExecute((body, headers) => {
  return {
    requestId: body.requestId,
    payload: {
      commands: [{
        ids: ['123'],
        status: 'SUCCESS',
        states: {
          dispenseItems: [{
            itemName: 'cat food',
            amountLastDispensed: {
              amount: 14.5,
              unit: 'CUPS'
            },  
            isCurrentlyDispensing: true
          }]
        }
      }]
    }
  };
});

// ...

exports.smarthome = functions.https.onRequest(app);
Java
put("dispenseItems", new HashMap[] {
    new HashMap<String, Object>() {{        put("itemName", "dog treats");
        put("amountRemaining", new HashMap<String, Object>() {{            put("amount", 27.0);
            put("units", "NO_UNITS");
        }});
        put("amountLastDispensed", new HashMap<String, Object>() {{            put("amount", 3.0);
            put("unit", "NO_UNITS");
        }});
        put("isCurrentlyDispensing", true);
    }}});
JSON
{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [
      {
        "ids": [
          "123"
        ],
        "status": "SUCCESS",
        "states": {
          "dispenseItems": [
            {
              "itemName": "cat food",
              "amountLastDispensed": {
                "amount": 14.5,
                "unit": "CUPS"
              },
              "isCurrentlyDispensing": true
            }
          ]
        }
      }
    ]
  }
}

Device ERRORS

See the full list of errors and exceptions.

  • dispenseAmountRemainingExceeded: The user tried to dispense an item or amount from a device that does not have enough amount remaining.
  • dispenseAmountAboveLimit: The user tried to dispense an amount that is beyond the limits of what they can ask for in a single query. This is to prevent accidentally dispensing an excessive amount (for example, 500,000 cups of water).
  • dispenseAmountBelowLimit: The user tried to dispense an item or amount from the device the device that is below the minimum amount it can dispense.
  • dispenseFractionalAmountNotSupported: The user tried to dispense a fractional amount of an item that the device cannot split (for example, countable items like dog treats may not be divisible by the device).
  • genericDispenseNotSupported: The user tries to dispense from a device without specifying an item or preset, but the device does not support such functionality (for example, a default dispense action).
  • dispenseUnitNotSupported: The user tries to dispense from a device with a unit not supported for that case (for example, the item is not provided so supported_unit validation was skipped).
  • dispenseFractionalUnitNotSupported: The user tried to dispense a fractional amount of an item which can be split but not for the particular unit specified (for example, a faucet may be able to dispense 2.7 cups but not 2.7 mL).
  • deviceCurrentlyDispensing: The users tries to dispense an item but the device is already dispensing.
  • deviceClogged: The users tries to dispense an item but the device is clogged.
  • deviceBusy: The users tries to dispense an item but the device is busy (generic).

Device EXCEPTIONS

These exceptions can be reported as an exceptionCode in the states object of your response:

  • amountRemainingLow - The user dispenses an item or amount from the device which brings the amount remaining to a low level. You are responsible for defining what constitutes a "low" level.
  • userNeedsToWait - When the user has to wait before the requested item or amount can be successfully dispensed (for example, a faucet will dispense hot water but the user needs to wait for the water to heat up before it begins dispensing).