Hardware components | ||||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
|
People find card games fun for different reasons. Playing card games such as UNO and Monopoly Deal are good ways to spend family time and challenge your mind. Families always love to play card games as a strong bonding event. However, no one likes to be the card shuffler and distributor. The person in charge of distributing and shuffling needs to distribute cards to the fellow players every time. It is quite tiring and time consuming. Sometimes we have to do the paper, rock and scissors to choose who will be the next shuffler. Hmmm, waste of time and takes away the mood.
Fantastic Card fills the shuffler gap, now everyone- yes everyone can play and let the robot do the job with voice command! Imagine, with just voice command, you get your next card distributed to you in a fair and clean game. No one will blame the robot for uh, unfair shuffling… Fantastic Card fulfill this and allows full focus on the game even for the shuffler eh, ex-shuffler.
Description:Fantastic Card robotic mechanism includes base, shuffler, dispenser (main bay), shuffled cards bay and dispensed cards bay. It has 1 EV3 brain, 3 large motors, 1 medium motor, 1 color sensor and 1 touch sensor.
Shuffler and dispenser has a top wheel gearing mechanism which equipped with a jamming function. It turns freely one way and jams the other. Thus, it can dispense cards and raise the wheel up when not in need with just one motor drive. The touch sensor located at the back of Fantastic Card is used to detect whether the top wheel is raised or not. Fantastic Card shuffle cards by turning the top and bottom wheels together, which dispense 1 to 3 cards at a time. In the python code, playing cards, Monopoly Deal cards and UNO cards are separated as this three card games has different amount of cards in a deck to be shuffled. In shuffling mode, the cards is dispensed to shuffled cards bay. In dispense mode, cards is dispense to dispensed cards bay.
After successful shuffling, the shuffled cards is transferred back to shuffler and dispenser (main bay) from shuffled cards bay. The large motor has various type of gears for gearing down to lift the heavy weight of the cards. A color sensor is dedicated to detect the presence of the shuffled cards bay.
In dispense mode, cards are dispensed by the bottom wheel while the top wheel applies pressure on the cards to ensure it dispenses the cards one by one. Rotations is tuned precisely to dispense a single card to more than 10 cards. The card is dispensed to the dispensed cards bay.
Fantastic Card's base has a medium motor that turns a single wheel. The other side of the base has a fixed wheel. It allows 360 degree turn of the entire robot so it can distribute the cards to every single player. Alexa collects data of how many players in the card game and uses it to calculate beforehand how many rotations needed after dividing 360 degree by amount of players. It can also be modified to move forward instead to dispense cards.
Commands to activate:
"Alexa, open fantastic card." - Open Fantastic Card skill
>>Alexa prompt: Welcome to Fantastic Card, what card games is in your mood and how many player?
"Playing card for X Players"
"Dispense X cards" - Card will distribute
"Shuffle cards" - Card will shuffle
"Next player" - Turn to next player
How Fantastic Card works? Watch the video below:
Journey creating this project:
Week 1: Completed the missions example provided by Amazon and Hackster
Week 2: Brainstorming idea
Week 3: Fantastic Card robot building
Week 4: Python and skill source code programming
Week 5: Filming, disassembling of robot and rebuilding for LEGO building instructions
Week 6: Video editing and completed PowerPoint for LEGO building instructions and coding instructions
Week 7: Submission
The video below is a walk-through showing how I created this project:
The LEGO building instructions, part list and coding instructions are under Schematics and circuit diagrams.
The python programming code and skill source code are under Code category.
Thank you.
# 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 time
import logging
import json
import random
import threading
import random
from enum import Enum
from agt import AlexaGadget
from ev3dev2.led import Leds
from ev3dev2.sound import Sound
from ev3dev2.motor import LargeMotor, MediumMotor, OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D, MoveTank, SpeedPercent
from ev3dev2.sensor.lego import ColorSensor, TouchSensor
# Set the logging level to INFO to see messages from AlexaGadget
logging.basicConfig(level=logging.INFO)
global cardfig
global prevcrd
global plyrs
class Card(Enum):
"""
The list of card games and their variations.
These variations correspond to the skill slot values.
"""
POKER = ['playing', 'poker']
MONOPOLY = ['monopoly', 'monopoly deal']
UNO = ['uno']
class Orders(Enum):
"""
The list of orders and their variations.
These variations correspond to the skill slot values.
"""
ENABLE = ['enable super mode']
DISABLE = ['disable super mode']
SHUFFLE = ['shuffle', 'rearrange', 'mix']
NEXT = ['next','next player']
class State(Enum):
"""
The list of status and their variations.
These variations correspond to the skill slot values.
"""
DISPENSE = ['dispense', 'distribute', 'give out']
class EventName(Enum):
"""
The list of custom event name sent from this gadget
"""
SENTRY = "Sentry"
PROXIMITY = "Proximity"
SPEECH = "Speech"
class MindstormsGadget(AlexaGadget):
def __init__(self):
super().__init__()
# Gadget state
self.patrol_mode = False
# Ev3dev initialization
self.leds = Leds()
self.sound = Sound()
self.drive = MoveTank(OUTPUT_C, OUTPUT_D)
# Start threads
threading.Thread(target=self._patrol_thread, daemon=True).start()
def on_connected(self, device_addr):
self.leds.set_color("LEFT", "GREEN")
self.leds.set_color("RIGHT", "GREEN")
print("{} connected to Echo device".format(self.friendly_name))
def on_disconnected(self, device_addr):
self.leds.set_color("LEFT", "BLACK")
self.leds.set_color("RIGHT", "BLACK")
print("{} disconnected from Echo device".format(self.friendly_name))
def on_custom_mindstorms_gadget_control(self, directive):
try:
payload = json.loads(directive.payload.decode("utf-8"))
print("Control payload: {}".format(payload))
control_type = payload["type"]
if control_type == "move":
# Expected params: [direction, duration, speed]
self._move(payload["card"], int(payload["players"]))
if control_type == "orders":
# Expected params: [orders]
self._orders(payload["orders"])
if control_type == "dispense":
# Expected params: [state,digit]
self._dispense(payload["state"], int(payload["digit"]))
except KeyError:
print("Missing expected parameters: {}".format(directive))
def _move(self, card, players: int, is_blocking=True):
global cardfig
ts = TouchSensor()
if ts.value() != 1:
motorB=LargeMotor(OUTPUT_B)
motorB.on_for_rotations(SpeedPercent(-30),0.3)
cl = ColorSensor()
cl.mode='COL-COLOR'
colors=('unknown','black','blue','green','yellow','red','white','brown')
if colors[cl.value()] == 'red':
motorC=LargeMotor(OUTPUT_C)
motorC.on_for_seconds(SpeedPercent(-50),1.8)
if card in Card.POKER.value:
cardfig = 26
self._send_event(EventName.SPEECH, {'speechOut': "Let's play poker!"})
if card in Card.MONOPOLY.value:
cardfig = 55
self._send_event(EventName.SPEECH, {'speechOut': "Let's play monopoly deal!"})
if card in Card.UNO.value:
cardfig = 54
self._send_event(EventName.SPEECH, {'speechOut': "Let's play uno!"})
global plyrs
plyrs = players
def _orders(self, orders, is_blocking=True):
global plyrs
global cardfig
if orders in Orders.NEXT.value:
global plyrs
motorA=MediumMotor(OUTPUT_A)
motorA.on_for_rotations(SpeedPercent(100),42/plyrs)
cl = ColorSensor()
cl.mode='COL-COLOR'
colors=('unknown','black','blue','green','yellow','red','white','brown')
if colors[cl.value()] != 'red':
motorC=LargeMotor(OUTPUT_C)
motorC.on_for_seconds(SpeedPercent(50),1.8)
if orders in Orders.SHUFFLE.value:
cl = ColorSensor()
cl.mode='COL-COLOR'
colors=('unknown','black','blue','green','yellow','red','white','brown')
if colors[cl.value()] == 'red':
motorC=LargeMotor(OUTPUT_C)
motorC.on_for_seconds(SpeedPercent(-50),1.8)
motorD=LargeMotor(OUTPUT_D)
motorD.on_for_rotations(SpeedPercent(30),0.5)
motorB=LargeMotor(OUTPUT_B)
motorB.on_for_rotations(SpeedPercent(30),0.3)
q = cardfig-1
for x in range(cardfig):
motorD=LargeMotor(OUTPUT_D)
motorD.on_for_rotations(SpeedPercent(-70),0.8)
time.sleep(0.3)
motorB=LargeMotor(OUTPUT_B)
motorB.on_for_rotations(SpeedPercent(100),0.9)
time.sleep(0.1)
print(x)
if x == q:
time.sleep(1)
motorB=LargeMotor(OUTPUT_B)
motorB.on_for_rotations(SpeedPercent(-30),0.3)
motorC=LargeMotor(OUTPUT_C)
motorC.on_for_rotations(SpeedPercent(50),2.2)
motorD.on_for_rotations(SpeedPercent(70),1)
time.sleep(1.5)
motorC.on_for_rotations(SpeedPercent(-50),2.2)
time.sleep(1)
motorC.on_for_rotations(SpeedPercent(50),2.2)
motorD.on_for_rotations(SpeedPercent(100),2)
time.sleep(1)
motorB=LargeMotor(OUTPUT_B)
motorB.on_for_rotations(SpeedPercent(50),0.6)
time.sleep(1)
self._send_event(EventName.SPEECH, {'speechOut': "Card is ready to dispense"})
def _dispense(self, state, digit: int, is_blocking=True):
global prevcrd
cl = ColorSensor()
cl.mode='COL-COLOR'
colors=('unknown','black','blue','green','yellow','red','white','brown')
if colors[cl.value()] != 'red':
motorC=LargeMotor(OUTPUT_C)
motorC.on_for_seconds(SpeedPercent(50),1.8)
ts = TouchSensor()
if ts.value() == 1:
motorB=LargeMotor(OUTPUT_B)
motorB.on_for_rotations(SpeedPercent(30),0.3)
if digit != 0:
prevcrd = digit
for x in range(digit):
motorD=LargeMotor(OUTPUT_D)
motorD.on_for_rotations(SpeedPercent(-70),0.6)
else:
if prevcrd != 0:
motorD=LargeMotor(OUTPUT_D)
motorD.on_for_rotations(SpeedPercent(-70),0.6)
self._send_event(EventName.SPEECH, {'speechOut': "Collect your card now!"})
time.sleep(1.5)
motorD.on_for_rotations(SpeedPercent(100),1)
def _patrol_thread(self):
time.sleep(1)
def _send_event(self, name: EventName, payload):
self.send_custom_event('Custom.Mindstorms.Gadget', name.value, payload)
if __name__ == '__main__':
# Startup sequence
gadget = MindstormsGadget()
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")
/*
* 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.
*/
// This skill sample demonstrates how to send directives and receive events from an Echo connected gadget.
// This skill uses 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');
const Util = require('./util');
const Common = require('./common');
// The audio tag to include background music
const BG_MUSIC = '<audio src="soundbank://soundlibrary/ui/gameshow/amzn_ui_sfx_gameshow_waiting_loop_30s_01"></audio>';
// 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';
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle: async function(handlerInput) {
const request = handlerInput.requestEnvelope;
const { apiEndpoint, apiAccessToken } = request.context.System;
const apiResponse = await Util.getConnectedEndpoints(apiEndpoint, apiAccessToken);
if ((apiResponse.endpoints || []).length === 0) {
return handlerInput.responseBuilder
.speak(`Oops, Fantastic Card is offline!`)
.getResponse();
}
// Store the gadget endpointId to be used in this skill session
const endpointId = apiResponse.endpoints[0].endpointId || [];
Util.putSessionAttribute(handlerInput, 'endpointId', endpointId);
// Set players to 3 as default
Util.putSessionAttribute(handlerInput, 'players', 3);
// Set skill duration to 5 minutes (ten 30-seconds interval)
Util.putSessionAttribute(handlerInput, 'duration', 10);
// Set the token to track the event handler
const token = handlerInput.requestEnvelope.request.requestId;
Util.putSessionAttribute(handlerInput, 'token', token);
let speechOutput = "Welcome to fantastic card, what card game is in your mood and how many players?";
return handlerInput.responseBuilder
.speak(speechOutput + BG_MUSIC)
.addDirective(Util.buildStartEventHandler(token,60000, {}))
.getResponse();
}
};
const MoveIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'MoveIntent';
},
handle: function (handlerInput) {
const request = handlerInput.requestEnvelope;
const card = Alexa.getSlotValue(request, 'Card');
// Players use default if not available
const players = Alexa.getSlotValue(request, 'Players') || "2";
// Get data from session attribute
const attributesManager = handlerInput.attributesManager;
const speed = attributesManager.getSessionAttributes().speed || "50";
const endpointId = attributesManager.getSessionAttributes().endpointId || [];
// Construct the directive with the payload containing the move parameters
let directive = Util.build(endpointId, NAMESPACE, NAME_CONTROL,
{
type: 'move',
card: card,
players: players
});
const speechOutput = "Place your cards in Fantastic Card Robot to shuffle and distribute";
return handlerInput.responseBuilder
.speak(speechOutput + BG_MUSIC)
.addDirective(directive)
.getResponse();
}
};
const ShuffleIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'ShuffleIntent';
},
handle: function (handlerInput) {
const request = handlerInput.requestEnvelope;
const orders = Alexa.getSlotValue(request, 'Orders');
// Get data from session attribute
const attributesManager = handlerInput.attributesManager;
const endpointId = attributesManager.getSessionAttributes().endpointId || [];
// Construct the directive with the payload containing the move parameters
let directive = Util.build(endpointId, NAMESPACE, NAME_CONTROL,
{
type: 'orders',
orders: orders
});
const speechOutput = (orders === "next") || (orders === "next player")
? "Serving next player"
: "Shuffling";
return handlerInput.responseBuilder
.speak(speechOutput + BG_MUSIC)
.addDirective(directive)
.getResponse();
}
};
const DispenseIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'DispenseIntent';
},
handle: function (handlerInput) {
const request = handlerInput.requestEnvelope;
const state = Alexa.getSlotValue(request, 'State');
const digit = Alexa.getSlotValue(request, 'Digit');
// Get data from session attribute
const attributesManager = handlerInput.attributesManager;
const endpointId = attributesManager.getSessionAttributes().endpointId || [];
// Construct the directive with the payload containing the move parameters
let directive = Util.build(endpointId, NAMESPACE, NAME_CONTROL,
{
type: 'dispense',
state: state,
digit: digit
});
const speechOutput = "Dispensing";
return handlerInput.responseBuilder
.speak(speechOutput + BG_MUSIC)
.addDirective(directive)
.getResponse();
}
};
const EventsReceivedRequestHandler = {
// Checks for a valid token and endpoint.
canHandle(handlerInput) {
let { request } = handlerInput.requestEnvelope;
console.log('Request type: ' + Alexa.getRequestType(handlerInput.requestEnvelope));
if (request.type !== 'CustomInterfaceController.EventsReceived') return false;
const attributesManager = handlerInput.attributesManager;
let sessionAttributes = attributesManager.getSessionAttributes();
let customEvent = request.events[0];
// Validate event token
if (sessionAttributes.token !== request.token) {
console.log("Event token doesn't match. Ignoring this event");
return false;
}
// Validate endpoint
let requestEndpoint = customEvent.endpoint.endpointId;
if (requestEndpoint !== sessionAttributes.endpointId) {
console.log("Event endpoint id doesn't match. Ignoring this event");
return false;
}
return true;
},
handle(handlerInput) {
console.log("== Received Custom Event ==");
let customEvent = handlerInput.requestEnvelope.request.events[0];
let payload = customEvent.payload;
let name = customEvent.header.name;
let speechOutput;
if (name === 'Proximity') {
let distance = parseInt(payload.distance);
if (distance < 10) {
let speechOutput = "Intruder detected! What would you like to do?";
return handlerInput.responseBuilder
.speak(speechOutput, "REPLACE_ALL")
.withShouldEndSession(false)
.getResponse();
}
} else if (name === 'Sentry') {
if ('fire' in payload) {
speechOutput = "Threat eliminated";
}
} else if (name === 'Speech') {
speechOutput = payload.speechOut;
} else {
speechOutput = "Event not recognized. Awaiting new command.";
}
return handlerInput.responseBuilder
.speak(speechOutput + BG_MUSIC, "REPLACE_ALL")
.getResponse();
}
};
const ExpiredRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'CustomInterfaceController.Expired'
},
handle(handlerInput) {
console.log("== Custom Event Expiration Input ==");
// Set the token to track the event handler
const token = handlerInput.requestEnvelope.request.requestId;
Util.putSessionAttribute(handlerInput, 'token', token);
const attributesManager = handlerInput.attributesManager;
let duration = attributesManager.getSessionAttributes().duration || 0;
if (duration > 0) {
Util.putSessionAttribute(handlerInput, 'duration', --duration);
// Extends skill session
const speechOutput = `Looks like somebody is winning`;
return handlerInput.responseBuilder
.addDirective(Util.buildStartEventHandler(token, 60000, {}))
.speak(speechOutput + BG_MUSIC)
.getResponse();
}
else {
// End skill session
return handlerInput.responseBuilder
.speak("It is a pleasure serving you guys. Have fun!")
.withShouldEndSession(true)
.getResponse();
}
}
};
// 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()
.addRequestHandlers(
LaunchRequestHandler,
MoveIntentHandler,
ShuffleIntentHandler,
DispenseIntentHandler,
EventsReceivedRequestHandler,
ExpiredRequestHandler,
Common.HelpIntentHandler,
Common.CancelAndStopIntentHandler,
Common.SessionEndedRequestHandler,
Common.IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
)
.addRequestInterceptors(Common.RequestInterceptor)
.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 Https = require('https');
const AWS = require('aws-sdk');
const Escape = require('lodash/escape');
const s3SigV4Client = new AWS.S3({
signatureVersion: 'v4'
});
/**
* Get the authenticated URL to access the S3 Object. This URL expires after 60 seconds.
* @param s3ObjectKey - the S3 object key
* @returns {string} the pre-signed S3 URL
*/
exports.getS3PreSignedUrl = function getS3PreSignedUrl(s3ObjectKey) {
const bucketName = process.env.S3_PERSISTENCE_BUCKET;
return Escape(s3SigV4Client.getSignedUrl('getObject', {
Bucket: bucketName,
Key: s3ObjectKey,
Expires: 60 // the Expires is capped for 1 minute
}));
};
/**
* Builds a directive to start the EventHandler.
* @param token - a unique identifier to track the event handler
* @param {number} timeout - the duration to wait before sending back the expiration
* payload to the skill.
* @param payload - the expiration json payload
* @see {@link https://developer.amazon.com/docs/alexa-gadgets-toolkit/receive-custom-event-from-gadget.html#start}
*/
exports.buildStartEventHandler = function (token, timeout = 30000, payload) {
return {
type: "CustomInterfaceController.StartEventHandler",
token: token,
expiration : {
durationInMilliseconds: timeout,
expirationPayload: payload
}
};
};
/**
*
* Builds a directive to stops the active event handler.
* The event handler is identified by the cached token in the session attribute.
* @param {string} handlerInput - the context from Alexa Service
* @see {@link https://developer.amazon.com/docs/alexa-gadgets-toolkit/receive-custom-event-from-gadget.html#stop}
*/
exports.buildStopEventHandlerDirective = function (handlerInput) {
let token = handlerInput.attributesManager.getSessionAttributes().token || '';
return {
"type": "CustomInterfaceController.StopEventHandler",
"token": token
}
};
/**
* 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();
}));
};
{
"name": "agt-mindstorms",
"version": "1.1.0",
"description": "A sample skill demonstrating how to use AGT with Lego Mindstorms",
"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",
"request": "^2.81.0",
"lodash": "^4.17.11"
}
}
{
"interactionModel": {
"languageModel": {
"invocationName": "fantastic card",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
},
{
"name": "MoveIntent",
"slots": [
{
"name": "Card",
"type": "CardType",
"samples": [
"{Card} game",
"{Card} card",
"{Card}"
]
},
{
"name": "Players",
"type": "AMAZON.NUMBER",
"samples": [
"{Players}",
"{Players} people",
"{Players} players"
]
}
],
"samples": [
"{Card} cards for {Players}",
"{Card} cards with {Players} players",
"{Card} card with {Players} players",
"{Card} card for {Players}",
"{Card} card for {Players} players"
]
},
{
"name": "ShuffleIntent",
"slots": [
{
"name": "Orders",
"type": "OrderType",
"samples": [
"{Orders} the cards",
"{Orders}"
]
}
],
"samples": [
"{Orders}",
"{Orders} the cards",
"{Orders} the playing deck",
"{Orders} the deck",
"{Orders} cards"
]
},
{
"name": "DispenseIntent",
"slots": [
{
"name": "State",
"type": "StateType",
"samples": [
"{State}"
]
},
{
"name": "Digit",
"type": "AMAZON.NUMBER",
"samples": [
"{Digit}"
]
}
],
"samples": [
"{State} {Digit} cards",
"{State} "
]
}
],
"types": [
{
"name": "CardType",
"values": [
{
"name": {
"value": "playing"
}
},
{
"name": {
"value": "poker"
}
},
{
"name": {
"value": "uno"
}
},
{
"name": {
"value": "monopoly deal"
}
},
{
"name": {
"value": "monopoly"
}
}
]
},
{
"name": "CommandType",
"values": [
{
"name": {
"value": "sentry"
}
},
{
"name": {
"value": "circle"
}
},
{
"name": {
"value": "square"
}
},
{
"name": {
"value": "patrol"
}
},
{
"name": {
"value": "cannon"
}
},
{
"name": {
"value": "all shot"
}
},
{
"name": {
"value": "one shot"
}
}
]
},
{
"name": "OrderType",
"values": [
{
"name": {
"value": "disable super mode"
}
},
{
"name": {
"value": "enable super mode"
}
},
{
"name": {
"value": "next player"
}
},
{
"name": {
"value": "next"
}
},
{
"name": {
"value": "shuffle"
}
},
{
"name": {
"value": "mix"
}
},
{
"name": {
"value": "rearrange"
}
}
]
},
{
"name": "StateType",
"values": [
{
"name": {
"value": "dispense"
}
},
{
"name": {
"value": "give out"
}
},
{
"name": {
"value": "distribute"
}
}
]
}
]
},
"dialog": {
"intents": [
{
"name": "MoveIntent",
"confirmationRequired": false,
"prompts": {},
"slots": [
{
"name": "Card",
"type": "CardType",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.601979929656.903135104927"
}
},
{
"name": "Players",
"type": "AMAZON.NUMBER",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.601979929656.250204792515"
}
}
]
},
{
"name": "DispenseIntent",
"confirmationRequired": false,
"prompts": {},
"slots": [
{
"name": "State",
"type": "StateType",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.961862731677.130688258329"
}
},
{
"name": "Digit",
"type": "AMAZON.NUMBER",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.961862731677.1527713793444"
}
}
]
},
{
"name": "ShuffleIntent",
"confirmationRequired": false,
"prompts": {},
"slots": [
{
"name": "Orders",
"type": "OrderType",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.277306889975.1312600386358"
}
}
]
}
],
"delegationStrategy": "ALWAYS"
},
"prompts": [
{
"id": "Elicit.Slot.601979929656.250204792515",
"variations": [
{
"type": "PlainText",
"value": "I am sorry. How many players?"
}
]
},
{
"id": "Elicit.Slot.601979929656.903135104927",
"variations": [
{
"type": "PlainText",
"value": "Hold on. What card game are you playing today?"
}
]
},
{
"id": "Elicit.Slot.961862731677.130688258329",
"variations": [
{
"type": "PlainText",
"value": "Can you repeat that?"
}
]
},
{
"id": "Elicit.Slot.277306889975.1312600386358",
"variations": [
{
"type": "PlainText",
"value": "I didn't catch that. Can you repeat?"
}
]
},
{
"id": "Elicit.Slot.961862731677.1527713793444",
"variations": [
{
"type": "PlainText",
"value": "Hold it right there, how many cards to dispense?"
}
]
}
]
}
}
/*
* 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
};
Comments