Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
| ||||||
|
A fun part of going to a Chinese restaurant is the Lazy Susan. It spins, it gets you food you like, what is there not to like?
Why not improve on it? My Lazy Susan isn't lazy, She is smart.
Pass left, pass right, giving people what they want, all without touching the Lazy Susan.
The new Smart Susan takes commands and spins to serve food.
The new Smart Susan is practical and fun.
She learns who is at the table and takes voice commands to pass the dishes around.
I like thinking of different ways to add an ev3 into everyday life. Until this project, they were just ideas. When I saw the challenge, I thought voice activated and a Lazy Susan were an excellent match.
The Challenges:There were many challenges along the way. I knew how to program an EV3 using block programming, but I had yet to do it with Python.Thankfully, the instructions were very helpful. I would not have been able to do it without them. Working through Mission 1 to Mission 3 showed a proof of concept. After that I worked through several Alexa tutorials to get a better understanding of the Alexa skill development needed for this project. There is still so much to learn.
How It Works:There were two types of passing logic:1) Passing in a direction (left, right, across)2) Passing a specific dish to a specific person
The logic for passing in a direction is fairly simple. The logic for passing a specific dish to a specific person is more complicated. To do that I needed to know:
- who was at the table and at which location
- what dishes were at the table and at which location
- how the table was positioned
It seemed more practical to have that code in the Alexa skill.
The Alexa javascript figures out how much to rotate and sends that directive to the EV3 brick. The EV3 python code then takes that command and rotates the motor accordingly.Inside the Mindstorm EV3 python code, only a single large motor is used. The code takes a rotate
command with the four direction options of: left
, right
, across
, and nothing
The hardest logic was to figure out how much to rotate for a specific dish to a specific person. First, I had to capture the people's name and be able to get them in another intent.The easiest way to number the dishes and the people's names.
To keep track of the dishes, I have current
which has the number of the dish in front person 0. This number gets updated after the Smart Susan spins.
1. Get dish and person name from voice command2. Get people's names from s3 bucket3. Match people name with wanted name4. Match dish name with wanted dish (similar to 3)5. Figure out which dish should be in front of person 06. Figure out how much to turn the table
Snippets of Code:If you want to build your own Smart Susan I have made a video to show you how. I also have a list of all the parts and code here. I suggest that for anyone wanting to recreate this project that they first go and complete mission 1 through 3. That will help in the set-up required and makes an easy implementation of the attached code.
#!/usr/bin/env python3
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# You may not use this file except in compliance with the terms and conditions
# set forth in the accompanying LICENSE.TXT file.
#
# THESE MATERIALS ARE PROVIDED ON AN "AS IS" BASIS. AMAZON SPECIFICALLY DISCLAIMS, WITH
# RESPECT TO THESE MATERIALS, ALL WARRANTIES, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
# THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
import os
import sys
import time
import logging
import json
import random
import threading
from enum import Enum
from agt import AlexaGadget
from ev3dev2.led import Leds
from ev3dev2.sound import Sound
from ev3dev2.motor import OUTPUT_B, SpeedPercent, LargeMotor
# Set the logging level to INFO to see messages from AlexaGadget
logging.basicConfig(level=logging.INFO, stream=sys.stdout, format='%(message)s')
logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
logger = logging.getLogger(__name__)
class Command(Enum):
"""
The list of preset commands and their invocation variation.
These variations correspond to the skill slot values.
"""
ROTATE = ['pass me','passme','rotate','serve','pass']
PASSLEFT = ['left']
PASSRIGHT = ['right']
PASSACROSS = ['across']
PASSNOTHING = ['nothing']
class MindstormsGadget(AlexaGadget):
"""
A Mindstorms gadget that performs movement based on voice commands.
Two types of commands are supported, directional movement and preset.
"""
def __init__(self):
"""
Performs Alexa Gadget initialization routines and ev3dev resource allocation.
"""
super().__init__()
# Gadget state
# self.patrol_mode = False
# Ev3dev initialization
self.leds = Leds()
self.sound = Sound()
self.server = LargeMotor(OUTPUT_B)
# Start threads
#threading.Thread(target=self._patrol_thread, daemon=True).start()
logger.info("Let's Dance")
def on_connected(self, device_addr):
"""
Gadget connected to the paired Echo device.
:param device_addr: the address of the device we connected to
"""
self.leds.set_color("LEFT", "GREEN")
self.leds.set_color("RIGHT", "GREEN")
logger.info("{} connected to Echo device".format(self.friendly_name))
def on_disconnected(self, device_addr):
"""
Gadget disconnected from the paired Echo device.
:param device_addr: the address of the device we disconnected from
"""
self.leds.set_color("LEFT", "BLACK")
self.leds.set_color("RIGHT", "BLACK")
logger.info("{} disconnected from Echo device".format(self.friendly_name))
def on_custom_mindstorms_gadget_control(self, directive):
"""
Handles the Custom.Mindstorms.Gadget control directive.
:param directive: the custom directive with the matching namespace and name
"""
try:
logger.info("Receiving Alexa Message")
payload = json.loads(directive.payload.decode("utf-8"))
print("Control payload: {}".format(payload), file=sys.stderr)
# Expected params: [command], [direction]
self._activate(payload["command"], payload["direction"])
except KeyError:
print("Missing expected parameters: {}".format(directive), file=sys.stderr)
def _activate(self, command, direction):
"""
Handles preset commands.
:param command: the preset rotate command
:param direction: left, right, across, nothing
"""
print("Activate command: ({}, {})".format(command, direction), file=sys.stderr)
if command in Command.ROTATE.value:
logger.info("Rotating " )
if direction in Command.PASSLEFT.value:
self.server.on_for_degrees(SpeedPercent(-5),90)
elif direction in Command.PASSACROSS.value:
self.server.on_for_degrees(SpeedPercent(5),180)
elif direction in Command.PASSRIGHT.value:
self.server.on_for_degrees(SpeedPercent(5),90)
if __name__ == '__main__':
gadget = MindstormsGadget()
# Set LCD font and turn off blinking LEDs
os.system('setfont Lat7-Terminus12x6')
gadget.leds.set_color("LEFT", "BLACK")
gadget.leds.set_color("RIGHT", "BLACK")
# Startup sequence
gadget.sound.play_song((('C4', 'e'), ('D4', 'e'), ('E5', 'q')))
gadget.leds.set_color("LEFT", "GREEN")
gadget.leds.set_color("RIGHT", "GREEN")
# Gadget main entry point
gadget.main()
# Shutdown sequence
gadget.sound.play_song((('E5', 'e'), ('C4', 'e')))
gadget.leds.set_color("LEFT", "BLACK")
gadget.leds.set_color("RIGHT", "BLACK")
{
"interactionModel": {
"languageModel": {
"invocationName": "tea club",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
},
{
"name": "CapturePersonIntent",
"slots": [
{
"name": "pone",
"type": "AMAZON.Animal"
},
{
"name": "ptwo",
"type": "AMAZON.Animal"
},
{
"name": "pthree",
"type": "AMAZON.Animal"
},
{
"name": "pfour",
"type": "AMAZON.Animal"
}
],
"samples": [
"{pone} and {ptwo} and {pthree} and {pfour}",
"{pone} {ptwo} {pthree} and {pfour}",
"{pone} {ptwo} {pthree} {pfour}"
]
},
{
"name": "PassDirectionIntent",
"slots": [
{
"name": "direction",
"type": "Direction"
}
],
"samples": [
"please pass {direction} ",
"{direction}",
"pass {direction} please",
"pass {direction}"
]
},
{
"name": "PassSpecificIntent",
"slots": [
{
"name": "dish",
"type": "dish"
},
{
"name": "person",
"type": "AMAZON.Animal"
}
],
"samples": [
"pass the {dish} to the {person} ",
"please pass the {dish} to the {person}",
"pass the {dish} to the {person} please",
"pass the {dish} to {person} please",
"please pass the {dish} to {person}",
"please pass {dish} to {person}",
"{dish} to {person}",
"pass {dish} to {person} "
]
},
{
"name": "WhoIsHereIntent",
"slots": [],
"samples": [
"Who do you think is here",
"Who is here"
]
}
],
"types": [
{
"name": "Direction",
"values": [
{
"name": {
"value": "across"
}
},
{
"name": {
"value": "right"
}
},
{
"name": {
"value": "left"
}
}
]
},
{
"name": "dish",
"values": [
{
"name": {
"value": "omelets"
}
},
{
"name": {
"value": "sponge cake"
}
},
{
"name": {
"value": "pancakes"
}
},
{
"name": {
"value": "buns"
}
},
{
"name": {
"value": "scones"
}
},
{
"name": {
"value": "sandwiches"
}
},
{
"name": {
"value": "puddings"
}
},
{
"name": {
"value": "dessert"
}
},
{
"name": {
"value": "pastries"
}
},
{
"name": {
"value": "muffin"
}
},
{
"name": {
"value": "jam"
}
},
{
"name": {
"value": "cookies"
}
},
{
"name": {
"value": "bread"
}
},
{
"name": {
"value": "tea"
}
},
{
"name": {
"value": "dish four"
}
},
{
"name": {
"value": "dish three"
}
},
{
"name": {
"value": "dish two"
}
},
{
"name": {
"value": "dish one"
}
}
]
}
]
}
}
}
// This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).
// Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
// session persistence, api calls, and more.
const Alexa = require('ask-sdk-core');
// using atributes attributesManager
const persistenceAdapter = require('ask-sdk-s3-persistence-adapter');
const Util = require('./util');
// adding ev3 code
const Common = require('./common');
// The namespace of the custom directive to be sent by this skill
const NAMESPACE = 'Custom.Mindstorms.Gadget';
// The name of the custom directive to be sent this skill
const NAME_CONTROL = 'control';
//*********************************** LaunchRequestHandler **************************************************
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
async handle(handlerInput) {
// get EV3 connection
let request = handlerInput.requestEnvelope;
let { apiEndpoint, apiAccessToken } = request.context.System;
let apiResponse = await Util.getConnectedEndpoints(apiEndpoint, apiAccessToken);
if ((apiResponse.endpoints || []).length === 0) {
return handlerInput.responseBuilder
.speak(`I could not find an EV3 Brick connected to this Echo device. Please check to make sure your EV3 Brick is connected, and try again.`)
.getResponse();
}
// Store the gadget endpointId to be used in this skill session
let endpointId = apiResponse.endpoints[0].endpointId || [];
Util.putSessionAttribute(handlerInput, 'endpointId', endpointId);
// Alexa voice outputs
const speakOutput = 'Hello! Welcome to the tea club! Who is here today?';
const repromptText = 'Could you repeat that? Who is here?';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
//**************************************** Custom Intent Handler ***********************************************
const CapturePersonIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'CapturePersonIntent';
},
// async for setting information
async handle(handlerInput) {
// getting names from voice command
const pone = handlerInput.requestEnvelope.request.intent.slots.pone.value;
const ptwo = handlerInput.requestEnvelope.request.intent.slots.ptwo.value;
const pthree = handlerInput.requestEnvelope.request.intent.slots.pthree.value;
const pfour = handlerInput.requestEnvelope.request.intent.slots.pfour.value;
const attributesManager = handlerInput.attributesManager;
// storing names for later use
const peopleAttributes = {
"pone" : pone,
"ptwo" : ptwo,
"pthree" : pthree,
"pfour" : pfour
};
attributesManager.setPersistentAttributes(peopleAttributes);
Util.putSessionAttribute(handlerInput, 'current', 0);
await attributesManager.savePersistentAttributes();
// Alexa voice responses
const speakOutput = `Welcome to the tea club, ${pone}, ${ptwo}, ${pthree}, and ${pfour}.`;
const reprompt = 'Please say pass left or pass right when you when to rotate the dishes. You can say stop when you are done.';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(reprompt)
.getResponse();
}
};
//**********************************************************Start*Direction*Intent*************************************************************************
const PassDirectionIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'PassDirectionIntent';
},
handle(handlerInput) {
// get direction from voice command & set default
let direction = handlerInput.requestEnvelope.request.intent.slots.direction.value;
if (direction === undefined) {
direction = 'right';
}
// get values from memory (endpoint and current location)
const attributesManager = handlerInput.attributesManager;
let endpointId = attributesManager.getSessionAttributes().endpointId || [];
let current = attributesManager.getSessionAttributes().current || 0;
// figure out which direction to rotate
let goto = current;
if (direction === 'right')
goto = (current +1 + 4)%4
else if (direction === 'left')
goto = (current -1 + 4)%4
else if (direction === 'across')
goto = (current +2 + 4)%4
// store current for later use
Util.putSessionAttribute(handlerInput, 'current', goto);
// Construct the directive to EV3 with the payload containing the move parameters
let directive = Util.build(endpointId, NAMESPACE, NAME_CONTROL,
{
command: 'rotate',
direction: direction
});
const speakOutput = `Passing ${direction}`;
const reprompt = `Let me know when you would like me to pass something else`;
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(reprompt)
.addDirective(directive)
.getResponse();
}
};
//**********************************************************End*Direction*Intent*************************************************************************
//************************************************************************* Who is Here ******************************************************************
const WhoIsHereIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'WhoIsHereIntent';
},
async handle(handlerInput) {
// remembering who is at the table
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = await attributesManager.getPersistentAttributes() || {};
const pone = sessionAttributes.hasOwnProperty('pone') ? sessionAttributes.pone : 0;
const ptwo = sessionAttributes.hasOwnProperty('ptwo') ? sessionAttributes.ptwo : 0;
const pthree = sessionAttributes.hasOwnProperty('pthree') ? sessionAttributes.pthree : 0;
const pfour = sessionAttributes.hasOwnProperty('pfour') ? sessionAttributes.pfour : 0;
const speakOutput = `We have ${pone}, ${ptwo}, ${pthree}, and ${pfour}`;
return handlerInput.responseBuilder
.speak(speakOutput)
}
};
//**********************************************************End*Who*is*Intent*************************************************************************
//**********************************************************Start*Specific*Intent*************************************************************************
const PassSpecificIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'PassSpecificIntent';
},
async handle(handlerInput) {
// take person and dish name from voice command
let wantedDish = handlerInput.requestEnvelope.request.intent.slots.dish.value;
let wantedPerson = handlerInput.requestEnvelope.request.intent.slots.person.value
// Alexa default voice response
let speakOutput = `Passing ${wantedDish} to ${wantedPerson}`;
// remembering who is at the table
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = await attributesManager.getPersistentAttributes() || {};
const pone = sessionAttributes.hasOwnProperty('pone') ? sessionAttributes.pone : 0;
const ptwo = sessionAttributes.hasOwnProperty('ptwo') ? sessionAttributes.ptwo : 0;
const pthree = sessionAttributes.hasOwnProperty('pthree') ? sessionAttributes.pthree : 0;
const pfour = sessionAttributes.hasOwnProperty('pfour') ? sessionAttributes.pfour : 0;
//lookup current dish in front of person zero
let current = attributesManager.getSessionAttributes().current || 0;
// personto - the person location where the dish is going
let personto = 0;
// matching voice command name with stored peoples names
if (wantedPerson === pone) {
personto = 0
}
else if (wantedPerson === ptwo) {
personto = 1
}
else if (wantedPerson === pthree) {
personto = 2
}
else if (wantedPerson === pfour) {
personto = 3
}
else {
personto = 0
speakOutput = `I know not of this ${wantedPerson} of whom you speak`
}
// preset food options
const fone = 'cookies';
const ftwo = 'honey';
const fthree = 'tea';
const ffour = 'milk';
// dishto - the dish location where the dish is going
let dishto = 0;
// matching voice command dish name with dish name list
if (wantedDish === fone) {
dishto = 0
}
else if (wantedDish === ftwo) {
dishto = 1
}
else if (wantedDish === fthree) {
dishto = 2
}
else if (wantedDish === ffour) {
dishto = 3
}
else {
dishto = 0
speakOutput = speakOutput + ` I know not of this ${wantedDish} which you desire`
}
// finding the dish that should be in front of person 0
let goto = (dishto-personto+4)%4;
// calculating how much to turn the table to get goto in front of person 0
let turn = (current-goto+4)%4;
//what direction to go in
let direction = "nothing";
if (turn === 0) {
speakOutput = 'Voila!'
direction = 'nothing'
}
else if (turn === 1) {
direction = 'left'
}
else if (turn === 2) {
direction = 'across'
}
else if (turn === 3) {
direction = 'right'
}
else {
speakOutput = speakOutput + ` I know not of this ${turn} which you desire`
}
// store the current position to use for the next rotation
Util.putSessionAttribute(handlerInput, 'current', goto);
// get endpoint to send the directive to the Mindstorm gadget
let endpointId = attributesManager.getSessionAttributes().endpointId || [];
// Construct the directive with the payload containing the move parameters
let directive = Util.build(endpointId, NAMESPACE, NAME_CONTROL,
{
command: 'rotate',
direction: direction
});
const reprompt = `Let me know when you would like me to pass something else`;
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(reprompt)
.addDirective(directive)
.getResponse();
}
};
//**********************************************************End*Specific*Intent*************************************************************************
//****************************** default Alexa Handlers, now found in common.js **************************************************
// The SkillBuilder acts as the entry point for your skill, routing all request and response
// payloads to the handlers above. Make sure any new handlers or interceptors you've
// defined are included below. The order matters - they're processed top to bottom.
exports.handler = Alexa.SkillBuilders.custom()
.withPersistenceAdapter(
new persistenceAdapter.S3PersistenceAdapter({bucketName:process.env.S3_PERSISTENCE_BUCKET}))
.addRequestHandlers(
LaunchRequestHandler,
CapturePersonIntentHandler,
PassDirectionIntentHandler,
WhoIsHereIntentHandler,
PassSpecificIntentHandler,
Common.HelpIntentHandler,
Common.CancelAndStopIntentHandler,
Common.SessionEndedRequestHandler,
Common.IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
)
.addErrorHandlers(
Common.ErrorHandler,
)
.lambda();
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* You may not use this file except in compliance with the terms and conditions
* set forth in the accompanying LICENSE.TXT file.
*
* THESE MATERIALS ARE PROVIDED ON AN "AS IS" BASIS. AMAZON SPECIFICALLY DISCLAIMS, WITH
* RESPECT TO THESE MATERIALS, ALL WARRANTIES, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
* THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
*/
'use strict'
const Alexa = require('ask-sdk-core');
const HelpIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speakOutput = 'You can say hello to me! How can I help?';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
|| Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
},
handle(handlerInput) {
const speakOutput = 'Goodbye!';
return handlerInput.responseBuilder
.speak(speakOutput)
.getResponse();
}
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
},
handle(handlerInput) {
// Any cleanup logic goes here.
return handlerInput.responseBuilder.getResponse();
}
};
// The intent reflector is used for interaction model testing and debugging.
// It will simply repeat the intent the user said. You can create custom handlers
// for your intents by defining them above, then also adding them to the request
// handler chain below.
const IntentReflectorHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest';
},
handle(handlerInput) {
const intentName = Alexa.getIntentName(handlerInput.requestEnvelope);
const speakOutput = `You just triggered ${intentName}`;
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt("I don't understand this command, try again")
.getResponse();
}
};
// Generic error handling to capture any syntax or routing errors. If you receive an error
// stating the request handler chain is not found, you have not implemented a handler for
// the intent being invoked or included it in the skill builder below.
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log(`~~~~ Error handled: ${error.stack}`);
const speakOutput = `Sorry, I had trouble doing what you asked. Please try again.`;
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
// The request interceptor is used for request handling testing and debugging.
// It will simply log the request in raw json format before any processing is performed.
const RequestInterceptor = {
process(handlerInput) {
let { attributesManager, requestEnvelope } = handlerInput;
let sessionAttributes = attributesManager.getSessionAttributes();
// Log the request for debug purposes.
console.log(`=====Request==${JSON.stringify(requestEnvelope)}`);
console.log(`=========SessionAttributes==${JSON.stringify(sessionAttributes, null, 2)}`);
}
};
module.exports = {
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
IntentReflectorHandler,
ErrorHandler,
RequestInterceptor
};
{
"name": "tea-club",
"version": "1.1.0",
"description": "alexa utility for quickly building skills",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Amazon Alexa",
"license": "ISC",
"dependencies": {
"ask-sdk-core": "^2.6.0",
"ask-sdk-model": "^1.18.0",
"aws-sdk": "^2.326.0",
"ask-sdk-s3-persistence-adapter": "^2.0.0"
}
}
// add to remember information across sessions
const AWS = require('aws-sdk');
const s3SigV4Client = new AWS.S3({
signatureVersion: 'v4'
});
module.exports.getS3PreSignedUrl = function getS3PreSignedUrl(s3ObjectKey) {
const bucketName = process.env.S3_PERSISTENCE_BUCKET;
const s3PreSignedUrl = s3SigV4Client.getSignedUrl('getObject', {
Bucket: bucketName,
Key: s3ObjectKey,
Expires: 60*1 // the Expires is capped for 1 minute
});
console.log(`Util.s3PreSignedUrl: ${s3ObjectKey} URL ${s3PreSignedUrl}`);
return s3PreSignedUrl;
}
//***********************************************added from alexa mindstorm gadget *********************************************************
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* You may not use this file except in compliance with the terms and conditions
* set forth in the accompanying LICENSE.TXT file.
*
* THESE MATERIALS ARE PROVIDED ON AN "AS IS" BASIS. AMAZON SPECIFICALLY DISCLAIMS, WITH
* RESPECT TO THESE MATERIALS, ALL WARRANTIES, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
* THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
*/
'use strict';
const Https = require('https');
/**
* Build a custom directive payload to the gadget with the specified endpointId
* @param {string} endpointId - the gadget endpoint Id
* @param {string} namespace - the namespace of the skill
* @param {string} name - the name of the skill within the scope of this namespace
* @param {object} payload - the payload data
* @see {@link https://developer.amazon.com/docs/alexa-gadgets-toolkit/send-gadget-custom-directive-from-skill.html#respond}
*/
exports.build = function (endpointId, namespace, name, payload) {
// Construct the custom directive that needs to be sent
// Gadget should declare the capabilities in the discovery response to
// receive the directives under the following namespace.
return {
type: 'CustomInterfaceController.SendDirective',
header: {
name: name,
namespace: namespace
},
endpoint: {
endpointId: endpointId
},
payload
};
};
/**
* A convenience routine to add the a key-value pair to the session attribute.
* @param handlerInput - the context from Alexa Service
* @param key - the key to be added
* @param value - the value be added
*/
exports.putSessionAttribute = function(handlerInput, key, value) {
const attributesManager = handlerInput.attributesManager;
let sessionAttributes = attributesManager.getSessionAttributes();
sessionAttributes[key] = value;
attributesManager.setSessionAttributes(sessionAttributes);
};
/**
* To get a list of all the gadgets that meet these conditions,
* Call the Endpoint Enumeration API with the apiEndpoint and apiAccessToken to
* retrieve the list of all connected gadgets.
*
* @param {string} apiEndpoint - the Endpoint API url
* @param {string} apiAccessToken - the token from the session object in the Alexa request
* @see {@link https://developer.amazon.com/docs/alexa-gadgets-toolkit/send-gadget-custom-directive-from-skill.html#call-endpoint-enumeration-api}
*/
exports.getConnectedEndpoints = function(apiEndpoint, apiAccessToken) {
// The preceding https:// need to be stripped off before making the call
apiEndpoint = (apiEndpoint || '').replace('https://', '');
return new Promise(((resolve, reject) => {
const options = {
host: apiEndpoint,
path: '/v1/endpoints',
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + apiAccessToken
}
};
const request = Https.request(options, (response) => {
response.setEncoding('utf8');
let returnData = '';
response.on('data', (chunk) => {
returnData += chunk;
});
response.on('end', () => {
resolve(JSON.parse(returnData));
});
response.on('error', (error) => {
reject(error);
});
});
request.end();
}));
};
Comments