Developer Console

DRS - Create or manage a Lambda function

An AWS Lambda function is responsible to inform the Alexa Smart Home cloud of new connected devices that have been discovered and any subsequent changes to the inventory of these devices.

Overview

Using the AWS Lambda function you can send two types of information required by the Alexa cloud for devices that have inventory sensors:

  1. A Discovery response (proactive or reactive) that adds the DRS capability for a specific device
  2. Any subsequent inventory update

If your connected devices already use Smart Home APIs within a Lambda function, you may choose to use the same function and expand the existing Discover.Response.

If you haven't read the Understanding Device Sensors (Alexa.InventorySensors) section click here before you start.

Creating the Lambda function

  1. Sign in to the AWS Management Console and open the AWS Lambda console (Opens in a new tab). Make sure you've selected:
    • N.Virginia for English (US)
    • EU (Ireland) region for English (UK), French (FR), German, Italian, Spanish (ES)
    • US West (Oregon) for Japanese skills.
    • More info available here
  2. Choose Create function.
  3. On the Create function page, choose Author from scratch
  4. Select the Function name and the runtime (in this guide we will use Node.JS). Click on Create function
  5. Select Add Trigger and search for Alexa Smart Home
  6. Go to your Alexa console (Opens in a new tab), select the Smart Home skill you created at the beginning of this process, click on the Smart Home model and copy your skill Id
    Keep this tab open.
  7. Go back to the Trigger page of your Lambda function and paste the skill Id in the Application ID field. Click Add

You may now copy the ARN visible on the top right part of the AWS console.

  1. Go back to the Alexa console, in the Smart Home section of your skill and paste the ARN in the Default endpoint field.

If you have regional endpoints for your Smart Home skill you can choose to change the endpoint to target customers who are closest to it.

1. Discovering the device

The Lambda function will be using the Smart Home Skill API to provide capability interfaces that enable you to describe your devices and the properties, events, and directives that they support. You describe a device to Alexa by sending a Discover.Response event after receiving a Discover directive from Alexa, or by proactively sending an AddOrUpdateReport to Alexa for any device already discovered.

Discovering the device upon account linking

The initial configuration established through a Discover.Response can be triggered by the Discover directives upon account linking (i.e. when a customer enables the manufacturer's Smart Home skill). In simple terms, this means that when the customer enables your Smart Home skill, Alexa will send a Discover directive to your Lambda function which will trigger a Discover.Response back.

The following diagram illustrates the exchange between the Alexa cloud and the manufacturer cloud (your cloud) when a customer enables your Smart Home skill.

Building the main logic

We will start by building the basic structure to handle our reactive Discover.Response.

  1. In the Lambda function code section make sure Edit code inline is selected. Leave the Runtime and Handler set to their defaults.
  2. Paste in the following code, completely replacing the code in index.js.
exports.handler = function (request, context) {
    if (request.directive.header.namespace === 'Alexa.Discovery' && request.directive.header.name === 'Discover') {
        log("DEBUG:", "Discover request",  JSON.stringify(request));
        handleDiscovery(request, context);
    }

    function handleDiscovery(request, context) {
        var payload = {
          "endpoints": [{
              "endpointId": "appliance-001", // This can be a device serial number to help you identify it uniquely
              "friendlyName": "Printer",
              "description": "Printer by Sample Manufacturer",
              "manufacturerName": "Sample Manufacturer",
              "displayCategories": [
                "OTHER"
              ],
              "cookie": {},
              "capabilities": [
                // Capabilities array
              ]
            }]
        };
        var header = request.directive.header;
        header.name = "Discover.Response";
        log("DEBUG:", "Discovery Response: ", JSON.stringify({ header: header, payload: payload }));
        context.succeed({ event: { header: header, payload: payload } });
    }

    function log(message, message1, message2) {
        console.log(message + message1 + message2);
    }
};
  1. Click Save

The above sample code will do two things:

  1. Listen for a Discover directive
  2. Respond with an Alexa.Discovery response

The content of the Reactive Discovery response will include a predefined header that will help the Alexa cloud identify the intent of the request:

{
  "event": {
    "header": {
      "namespace": "Alexa.Discovery",
      "name": "Discover.Response",
      "payloadVersion": "3",
      "messageId": "00000000-0000-0000-0000-000000000000"
    },
    "payload": {
      //Device-specific payload
    }
  }
}

The payload object will include an array of endpoints you wish Alexa to discover, each including device-specific attributes to help the Alexa cloud display the appropriate device information within the Alexa app. A single Discovery Response may contain multiple endpoints (e.g. a customer with two printers) that can be bundled in one single request:

  "endpoints": [{
      "endpointId": "appliance-001", // This can be a device serial number to help you identify it uniquely
      "friendlyName": "Printer",
      "description": "Printer by Sample Manufacturer",
      "manufacturerName": "Sample Manufacturer",
      "displayCategories": [
        "OTHER"
      ],
      "cookie": {},
      "capabilities": [
        // Capabilities array
      ]
    }]

And finally, the capabilities array will include one or more capability objects that describe Alexa.InventoryLevelSensor, Alexa.InventoryUsageSensor or Alexa.InventoryLevelUsageSensor available for replenishment:

{
          "type": "AlexaInterface",
          "interface": "Alexa.InventoryLevelSensor", // The default type of sensors
          "version": "3",
          "instance": "InventoryLevelSensor-1", // A unique name to reference your sensor. It has to be the same for this sensor across all customers.

          "properties": {
            "supported": [{
              "name": "level" // Property that defines the type of sensor
            }],
            "proactivelyReported": true,
            "retrievable": true
          },

          "capabilityResources": {
            "friendlyNames": [{
              "@type": "text",
              "value": {
                "text": "Magenta Ink", // Localized slot name that will be displayed inside the Alexa app
                "locale": "en-US"
              }
            },
            {
              "@type": "text",
              "value": {
                "text": "Encre Magenta", // Localized slot name that will be displayed inside the Alexa app
                "locale": "fr-FR"
              }
            }]
          },

          // Capability configuration.
          "configuration": {
            "replenishment": {
              "@type": "DashReplenishmentId",
              "value": "{DRS_ARN}" // This is the replenishmentId you obtained for each slot when you created the device
            },

            "measurement": {
              "@type": "Volume", // The type of measurement the Alexa cloud should expect every time you update the inventory
              "unit": "LITER" // The type of unit the Alexa cloud should expect every time you update the inventory
            }
          }
        },

You can view what an entire Discovery Response looks like here:

Discovering a new device for an active customer

There may be scenarios where your cloud needs to proactively inform the Alexa cloud of a new device such as:

  • You want to add the DRS capability to a device already connected to Alexa
  • You want to add a new device for an existing account, where the customer has already performed account linking with an auth grant

There are two main differences in the JSON being sent in a proactive Discovery:

  1. Your header will use AddOrUpdateReport name instead of Discover.Response
  2. Your payload will contain the scope and access token of the customer you are referring to:

Example:

"endpoint": {
    "scope": {
      "type": "BearerToken",
      "token": "access-token-from-Amazon"
    },
    "endpointId": "appliance-001" //Where endpointId is the device serialnumber you are referring to
  },
  "payload": {
    //ChangeReport payload
  }

2. Sending an inventory update

When a smart device consumes inventory, you must inform the Alexa cloud of the change so that DRS can update and accurately track the overall inventory for the customer. There are currently two ways to perform this action, depending on the type of sensor you wish to update:

  1. Level sensor can send the level through a ChangeReport (proactively)
  2. Usage and LevelUsage sensors can use the InventoryConsumed event .

Level Sensor (Alexa.InventoryLevelSensor)

If you want to proactively update the Alexa cloud of a state change in the consumption of your level sensor, you can use a ChangeReport.

The JSON for this request must include the following header:

{
"context": {},
"event": {
  "header": {
    "namespace": "Alexa",
    "name": "ChangeReport",
    "payloadVersion": "3",
    "messageId": "00000000-0000-0000-0000-000000000000"
  },
  // Endpoint details
 }
}

The endpoint details will include the access token your cloud already exchanged for that customer:

"endpoint": {
    "scope": {
      "type": "BearerToken",
      "token": "access-token-from-Amazon"
    },
    "endpointId": "appliance-001" // This can be a device serial number to help you identify it uniquely
  },
  "payload": {
    //ChangeReport payload
  }

And finally the payload includes any of the deltas you want to report. In this example, we are updating a level sensor with a new absolute volume:

"change": {
    "cause": {
      "type": "PERIODIC_POLL"
    },

    // A ChangeReport contains any pro-actively reported changes to capability properties. You state what type of capability and what instance of that capability has changed with the property that has changed. Below shows sample payloads for various measurement types.
    "properties": [
      {
        "namespace": "Alexa.InventoryLevelSensor",
        "instance": "InventoryLevelSensor-1",
        "name": "level",
        "value": {
          "@type": "Volume",
          "value": 2.5,
          "unit": "LITER"
        },
        "timeOfSample": "2018-02-03T16:20:50.52Z",
        "uncertaintyInMilliseconds": 0
      },
      {
        //Other property that has changed if more than one
      }
    ]
  }

You can view what an entire ChangeReport looks like here:

Usage sensor (Alexa.InventoryUsageSensor)

An InventoryConsumed event can be used for InventoryUsageSensor and must always contain the following information:

  • The device details including namespace, name and endpointId
  • The customer access token
  • The payload with the inventory update

A typical InventoryConsumed event will look like the following:

{  
  "event": {
    "header": {
      "namespace": "Alexa.InventoryUsageSensor",
      "name": "InventoryConsumed",
      "instance": "Sensor.CoffeePod",
      "messageId": "<message id>",
      "payloadVersion": "3"
    },
    "endpoint": {
      "scope": {
        "type": "BearerToken",
        "token": "<an OAuth2 bearer token>"
      },
      "endpointId": "<endpoint id>"
    },
    "payload": {
      "usage": {
        "@type": "Count",
        "value": 1
      },
      "timeOfSample": "2020-09-23T16:20:50Z"
    }
  }
}

LevelUsage Sensor (Alexa.InventoryLevelUsageSensor)

With InventoryLevelUsageSensor, InventoryConsumed and InventoryReplaced events are sent to update inventory information.

A typical InventoryConsumed event for LevelUsage sensor will look like the following:

{  
  "event": {
    "header": {
      "namespace": "Alexa.InventoryLevelUsageSensor",
      "name": "InventoryConsumed",
      "instance": "Sensor.DustFilter",
      "messageId": "<a unique identifier, preferably a version 4 UUID>",
      "payloadVersion": "3"
    },
    "endpoint": {
      "scope": {
        "type": "BearerToken",
        "token": "<an OAuth2 bearer token>"
      },
      "endpointId": "<endpoint id>"
    },
    "payload": {
      "usage": {
        "@type": "Duration",
        "value": "PT30H"
    },
    "timeOfSample": "2020-07-02T16:20:50.52Z"
    }
  }
}

A typical InventoryReplaced event for LevelUsage sensor will look like the following:

{  
  "event": {
    "header": {
      "namespace": "Alexa.InventoryLevelUsageSensor",
      "name": "InventoryReplaced",
      "instance": "Sensor.DustFilter",
      "messageId": "<a unique identifier, preferably a version 4 UUID>",
      "payloadVersion": "3"
    },
    "endpoint": {
      "scope": {
        "type": "BearerToken",
        "token": "<an OAuth2 bearer token>"
      },
      "endpointId": "<endpoint id>"
    },
    "payload": {
      "replacedDate": "2020-01-15T14:30Z"
    }
  }
}  

Measurement Type and Unit

For the Inventory Sensors, you must specify your device's capable measurement type and unit as:

{
  "@type": "Percentage"
}

or

{
  "@type": "Volume",
  "unit": "LITER"
}

When reporting the device's inventory or consumption, you must specify:

{
  "@type": "Percentage",
  "value": 80
}

or

{
  "@type": "Volume",
  "value": 3.2,
  "unit": "LITER"
}

The following table shows the values accepted for each type.

@type unit
Percentage[No Unit]
Count[No Unit]
Volume LITER, MILLILITER, TEASPOON, UK_GALLON, US_FLUID_GALLON, US_FLUID_OUNCE etc. (see this table)
Weight GRAM, KILOGRAM, OUNCE, POUND etc. (see this table)

Reference

Next steps

You are ready to test the integration. Follow the instructions in the next page to begin.


Last updated: Feb 14, 2022