Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
| ||||||
|
Having a todler is not easy work. Especially when they start talking. Some of the first things they learn are animals. Lego Duplo Zoo Ev3luater is designed to teach my own toddler about the animals in her Duplo sets using Lego Duplo Minnie Mouse.
BuildDuplo Zoo Ev3luater uses Lego Technik sets, Lego Ev3 set and Lego Duplo sets.
Ev3 Brick ports :
Port 1 : Pixy 2 camera
Port 2: Color sensor for detecting start and end of conveyor belt
Port B: Large motor for ejector.
Port C: Large motor for conveyor belt.
Port D: Medium motor for scissors lift Minnie Mouse
EV3 programmingThe Pixy2 Camera is used as explained in this tutorial. The camera is used using i2c protocol and the Serial Protocol for the Pixy2. One note:
data = [174, 193, 32, 2, sigs, 1]
# Request block
bus.write_i2c_block_data(address, 0, data)
sigs is not the sum of all desired signatures as stated in that tutorial, but the index bits or'ed. If interested in signatures 1, 2 and 3, then sigs is not 6 (1 + 2 + 3 = 6) but sigs is 7 (1 + 2 + 4) (2^0 + 2^1 + 2^2). If we are interested in all signatures then sigs=127 (0xFF = 2^7 - 1)
This was taken directly from Pixy2CCC.h:
// Defines for sigmap:
// You can bitwise "or" these together to make a custom sigmap.
// For example if you're only interested in receiving blocks
// with signatures 1 and 5, you could use a sigmap of
// PIXY_SIG1 | PIXY_SIG5
#define CCC_SIG1 1
#define CCC_SIG2 2
#define CCC_SIG3 4
#define CCC_SIG4 8
#define CCC_SIG5 16
#define CCC_SIG6 32
#define CCC_SIG7 64
#define CCC_COLOR_CODES 128
#define CCC_SIG_ALL 0xff // all bits or'ed together
Alexa IntegrationThere are three intents defined in the interaction model:
- LearnIntent - this intent has one slot: a "DuploAnimal". It can be started by uttering "Learn about animals" or "Learn about {DuploAnimal}". After the user choses an animal, the EV3 brick will move the conveyor until the animal requested is identified by the Pixy2 Camera. Then it is lifted and shown to the user while a story about that particular animal is told.
- QuizIntent - This intent is started by uttering "Quiz", "Test", "Take test" etc. All the animals present on the conveyor are scanned by the Pixy2 Camera, one is chosen randomly and shown to the user.
- AnswerIntent - This intent is used to answer to the previous intent. It has one slot, a DuploAnimal. The provided answer is checked against the correct answer and feedback is returned to the user if it is correct or not.
#!/usr/bin/env python3
# 1. Misinea 1
# - Alexa start Zoo Ev3luator
# - Motor C merge inainte cu viteza de 15 pana senzorul de culoare citeste Albastru(pentru calibrare)
# - Aleza raspunde ca "Duplo Zoo Ev3luator is ready. You can learn about animals or take a quiz."
# 2. Misiunea 2
# - Alexa, learn
# - What animal would you like to learn about?
# - EX: Pig
# - motor C merge inapoi cu viteza 15 % pana camera detecteaza Obiect 1 sau senzorul de culoare detecteaza Rosu
# - daca camera detecteaza Obiectul_1 motor_C se opreste, Motor_B merge - 30 % cu 360 grade si apoi 30 % cu 360 . Apoi motor D merge cu viteza 50, 15 rotatii.
# Alexa spune o poveste despre porc apoi motor merge cu viteza - 50 % , 15 rotatii. Si se repeta pasii 2 si 3 de la misiunea 1.
# - daca animalul nu este pe banda, senzorul de culoare deteacteaza Rosu, alexa spune ca animalul nu e pe banda si sa incerci altceva si se repeta pasii 2 si 3 de la misiunea 1
# 3. Misiunea 3 .
# - Quiz
# - motor C merge inapoi cu viteza 15 % pana camera detecteaza cele 3 obiecte de pe banda si le asignezi o variabila(1, 2, 3) pana ajunge la Rosu.
# - apoi motor C merge inapoi cu viteza 15 % pana detecteaza unu random dintre cele 3 .
# - cand ajunge la obiect motor_C se opreste, Motor_B merge - 30 % cu 360 grade si apoi 30 % cu 360 . Apoi motor D merge cu viteza 50, 15 rotatii.
# Alexa intreaba "What animal is This". Daca nimeresti spune "well done" daca nu spune "Wrong, this is a <<Object1>> ". Apoi motor D merge cu viteza - 50 15 rotatii si se repeta pasii 2 si 3 de la misiunea 1.
# Animale:
# 1. Pig
# 2. Lion
# 3. Rabbit
# 4. Cat
# 5. Dog
# 6. Chicken
# 7. Cow
import os
import logging
import json
import threading
import random
from time import sleep
from sys import stderr, stdout
import traceback
from ev3dev2.motor import LargeMotor, MediumMotor, OUTPUT_B, OUTPUT_C, OUTPUT_D
from ev3dev2.sound import Sound
from ev3dev2.led import Leds
from ev3dev2.sensor.lego import ColorSensor
from ev3dev2.port import LegoPort
from ev3dev2.sensor import INPUT_1
from smbus import SMBus
from agt import AlexaGadget
# set logger to display on both EV3 Brick and console
logging.basicConfig(level=logging.INFO, stream=stdout,
format='%(message)s')
logging.getLogger().addHandler(logging.StreamHandler(stderr))
logger = logging.getLogger(__name__)
# Make sure the same address is set in Pixy2
ADDRESS = 0x54
class MindstormsGadget(AlexaGadget):
"""
An Mindstorms gadget that will react to the Alexa wake word.
"""
def __init__(self):
"""
Performs Alexa Gadget initialization routines and ev3dev resource allocation.
"""
super().__init__()
self.leds = Leds()
self.sound = Sound()
self.motor_conveyor = LargeMotor(OUTPUT_C)
self.motor_pusher = LargeMotor(OUTPUT_B)
self.motor_lifter = MediumMotor(OUTPUT_D)
self.color_sensor = ColorSensor()
self.lifter_up = False
self.should_reset = False
# Settings for I2C (SMBus(3) for INPUT_1)
self.bus = SMBus(3)
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_init(self, directive):
"""
Handles the Custom.Mindstorms.Gadget control directive.
:param directive: the custom directive with the matching namespace and name
"""
try:
payload = json.loads(directive.payload.decode("utf-8"))
print("Init payload: {}".format(payload), file=stderr)
self.to_start()
self.send_custom_event('Custom.Mindstorms.Gadget',
'init', {'done': True})
except KeyError:
print("Missing expected parameters: {}".format(
directive), file=stderr)
def on_custom_mindstorms_gadget_reset(self, directive):
"""
Handles the Custom.Mindstorms.Gadget control directive.
:param directive: the custom directive with the matching namespace and name
"""
try:
payload = json.loads(directive.payload.decode("utf-8"))
print("Reset payload: {}".format(payload), file=stderr)
self.to_start()
except KeyError:
print("Missing expected parameters: {}".format(
directive), file=stderr)
def on_custom_mindstorms_gadget_learn(self, directive):
"""
Handles the Custom.Mindstorms.Gadget control directive.
:param directive: the custom directive with the matching namespace and name
"""
try:
payload = json.loads(directive.payload.decode("utf-8"))
print("Learn payload: {}".format(payload), file=stderr)
found = self.find_animal(animal_index=int(payload["animal_id"]))
if(found):
self.send_custom_event('Custom.Mindstorms.Gadget',
'learn',
{'done': True, 'found': True})
else:
self.send_custom_event('Custom.Mindstorms.Gadget',
'learn',
{'done': True, 'found': False})
except KeyError:
print("Missing expected parameters: {}".format(
directive), file=stderr)
def on_custom_mindstorms_gadget_quiz(self, directive):
"""
Handles the Custom.Mindstorms.Gadget control directive.
:param directive: the custom directive with the matching namespace and name
"""
try:
payload = json.loads(directive.payload.decode("utf-8"))
print("Quiz payload: {}".format(payload), file=stderr)
self.to_start()
the_chosen_one = self.quiz_animals()
self.send_custom_event('Custom.Mindstorms.Gadget',
'quiz', {'animal_id': str(the_chosen_one)})
except KeyError:
print("Missing expected parameters: {}".format(
directive), file=stderr)
def to_start(self):
print('Back to start', file=stderr)
if self.lifter_up:
self.motor_lifter.on_for_rotations(speed=50, rotations=-15)
self.lifter_up = False
# BLUE = 2
if self.color_sensor.color != 2:
self.motor_conveyor.on(speed=15)
while self.color_sensor.color != 2:
sleep(0.1)
self.motor_conveyor.stop()
self.should_reset = False
def find_animal(self, animal_index):
if self.should_reset or self.lifter_up:
self.to_start()
self.should_reset = True
# RED = 5
if self.color_sensor.color != 5 and self.find_animal_on_camera(index=animal_index) == 0:
print('Finding animal', file=stderr)
self.motor_conveyor.on(speed=-15)
while self.color_sensor.color != 5 and self.find_animal_on_camera(index=animal_index) == 0:
sleep(0.1)
self.motor_conveyor.stop()
if self.find_animal_on_camera(index=animal_index) != 0:
self.motor_pusher.on_for_rotations(speed=-30, rotations=1)
self.motor_pusher.on_for_rotations(speed=30, rotations=1)
self.motor_lifter.on_for_rotations(speed=50, rotations=15)
self.lifter_up = True
return True
else:
return False
def find_animal_on_camera(self, index):
data = [174, 193, 32, 2, int(2**(index-1)), 1]
# Request block
self.bus.write_i2c_block_data(ADDRESS, 0, data)
# Read block
block = self.bus.read_i2c_block_data(ADDRESS, 0, 20)
# Extract data
sig = block[7]*256 + block[6]
print("Signature: {} = {},{}".format(sig,block[6],block[7]), file=stderr)
if index == sig:
sleep(0.5)
return index
return 0
# if self.color_sensor.color == 4:
# return index
# else:
# return 0
def scan_animal_on_camera(self):
data = [174, 193, 32, 2, 127, 1]
# Request block
self.bus.write_i2c_block_data(ADDRESS, 0, data)
# Read block
block = self.bus.read_i2c_block_data(ADDRESS, 0, 20)
# Extract data
sig = block[7]*256 + block[6]
if sig > 0 and sig <= 7:
return sig
return 0
# return random.choice(list([1, 2, 3]))
def quiz_animals(self):
animals = set()
if self.should_reset or self.lifter_up:
self.to_start()
self.should_reset = True
# RED = 5
if self.color_sensor.color != 5:
print('Scanning animals', file=stderr)
self.motor_conveyor.on(speed=-15)
while self.color_sensor.color != 5:
sleep(0.1)
sig = self.scan_animal_on_camera()
if sig != 0:
animals.add(sig)
self.motor_conveyor.stop()
the_chosen_one = random.choice(list(animals))
print('The chosen one is {}'.format(the_chosen_one), file=stderr)
self.find_animal(the_chosen_one)
return the_chosen_one
if __name__ == '__main__':
# Set LEGO port for Pixy2 on input port 1
in1 = LegoPort(INPUT_1)
in1.mode = 'other-i2c'
# Short wait for port to get ready
sleep(0.5)
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": "duplo zoo",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
},
{
"name": "LearnIntent",
"slots": [
{
"name": "DuploAnimal",
"type": "DuploAnimal",
"samples": [
"{DuploAnimal}"
]
}
],
"samples": [
"Learn about animals",
"Learn {DuploAnimal}",
"Learn about {DuploAnimal}",
"Learn"
]
},
{
"name": "QuizIntent",
"slots": [],
"samples": [
"take quiz",
"take test",
"let's take a quiz",
"let's take a test",
"Test",
"Quiz"
]
},
{
"name": "AnswerIntent",
"slots": [
{
"name": "DuploAnimal",
"type": "DuploAnimal",
"samples": [
"Answer is {DuploAnimal}",
"This is a {DuploAnimal}",
"The animal is {DuploAnimal}",
"{DuploAnimal}"
]
}
],
"samples": [
"The animal is a {DuploAnimal}",
"This is a {DuploAnimal}",
"Answer is {DuploAnimal}",
"It's a {DuploAnimal}",
"{DuploAnimal}"
]
}
],
"types": [
{
"name": "DuploAnimal",
"values": [
{
"id": "7",
"name": {
"value": "Cow",
"synonyms": [
"Cows"
]
}
},
{
"id": "6",
"name": {
"value": "Chicken",
"synonyms": [
"Chickens"
]
}
},
{
"id": "5",
"name": {
"value": "Dog",
"synonyms": [
"Dogs"
]
}
},
{
"id": "4",
"name": {
"value": "Cat",
"synonyms": [
"Cats"
]
}
},
{
"id": "3",
"name": {
"value": "Rabbit",
"synonyms": [
"Rabbits"
]
}
},
{
"id": "2",
"name": {
"value": "Lion",
"synonyms": [
"Lions"
]
}
},
{
"id": "1",
"name": {
"value": "Pig",
"synonyms": [
"Pigs"
]
}
}
]
}
]
},
"dialog": {
"intents": [
{
"name": "LearnIntent",
"confirmationRequired": false,
"prompts": {},
"slots": [
{
"name": "DuploAnimal",
"type": "DuploAnimal",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.559936688270.1216049997006"
},
"validations": [
{
"type": "hasEntityResolutionMatch",
"prompt": "Slot.Validation.559936688270.1216049997006.12505410713"
}
]
}
]
},
{
"name": "AnswerIntent",
"confirmationRequired": false,
"prompts": {},
"slots": [
{
"name": "DuploAnimal",
"type": "DuploAnimal",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.182725031909.777190447479"
},
"validations": [
{
"type": "hasEntityResolutionMatch",
"prompt": "Slot.Validation.182725031909.777190447479.238329023491"
}
]
}
]
}
],
"delegationStrategy": "ALWAYS"
},
"prompts": [
{
"id": "Elicit.Slot.559936688270.1216049997006",
"variations": [
{
"type": "PlainText",
"value": "What animal would you like to learn about?"
}
]
},
{
"id": "Slot.Validation.559936688270.1216049997006.12505410713",
"variations": [
{
"type": "PlainText",
"value": "What animal would you like to learn about?"
}
]
},
{
"id": "Elicit.Slot.182725031909.777190447479",
"variations": [
{
"type": "PlainText",
"value": "What animal is this?"
}
]
},
{
"id": "Slot.Validation.182725031909.777190447479.238329023491",
"variations": [
{
"type": "PlainText",
"value": "What animal is this?"
}
]
}
]
}
}
const Alexa = require('ask-sdk-core');
const Util = require('./util');
// The namespace of the custom directive to be sent by this skill
const NAMESPACE = 'Custom.Mindstorms.Gadget';
const NAME_INIT = 'init';
const NAME_LEARN = 'learn';
const NAME_QUIZ = 'quiz';
const NAME_RESET = 'reset';
const DUPLO_ANIMALS = {
1: { name: 'Pig', story: 'Story about pigs.' },
2: { name: 'Lion', story: 'Story about lions.' },
3: { name: 'Rabbit', story: 'Story about rabbits.' },
4: { name: 'Cat', story: 'Story about cats.' },
5: { name: 'Dog', story: 'Story about dogs.' },
6: { name: 'Chicken', story: 'Story about chickens.' },
7: { name: 'Cow', story: 'Story about cows.' },
}
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle: async function (handlerInput) {
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 couldn't 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);
// Set skill duration to 2 minutes (4 30-seconds interval)
Util.putSessionAttribute(handlerInput, 'duration', 4);
// Set the token to track the event handler
const token = handlerInput.requestEnvelope.request.requestId;
Util.putSessionAttribute(handlerInput, 'token', token);
let directive = Util.build(endpointId, NAMESPACE, NAME_INIT,
{
});
return handlerInput.responseBuilder
.addDirective(buildStartEventHandler(token, 30000, {}))
.addDirective(directive)
.getResponse();
}
};
const LearnIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'LearnIntent';
},
handle(handlerInput) {
const attributesManager = handlerInput.attributesManager;
let endpointId = attributesManager.getSessionAttributes().endpointId || [];
// Set skill duration to 2 minutes (4 30-seconds interval)
Util.putSessionAttribute(handlerInput, 'duration', 4);
// Set the token to track the event handler
const token = handlerInput.requestEnvelope.request.requestId;
Util.putSessionAttribute(handlerInput, 'token', token);
let animalSlot = Alexa.getSlot(handlerInput.requestEnvelope, 'DuploAnimal');
let animal = animalSlot.resolutions.resolutionsPerAuthority[0].values[0].value;
Util.putSessionAttribute(handlerInput, 'animal_id', animal.id);
console.log(animal);
let directive = Util.build(endpointId, NAMESPACE, NAME_LEARN,
{
animal_id: animal.id,
animal_name: animal.name
});
return handlerInput.responseBuilder
.addDirective(directive)
.getResponse();
}
}
const QuizIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'QuizIntent';
},
handle(handlerInput) {
const attributesManager = handlerInput.attributesManager;
let endpointId = attributesManager.getSessionAttributes().endpointId || [];
// Set skill duration to 2 minutes (4 30-seconds interval)
Util.putSessionAttribute(handlerInput, 'duration', 4);
// Set the token to track the event handler
const token = handlerInput.requestEnvelope.request.requestId;
Util.putSessionAttribute(handlerInput, 'token', token);
let directive = Util.build(endpointId, NAMESPACE, NAME_QUIZ,
{
});
return handlerInput.responseBuilder
.speak(`I will choose an animal and you have to quess it's name.`)
.addDirective(directive)
.getResponse();
}
}
const AnswerIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AnswerIntent';
},
handle(handlerInput) {
const attributesManager = handlerInput.attributesManager;
let animalSlot = Alexa.getSlot(handlerInput.requestEnvelope, 'DuploAnimal');
let animal = animalSlot.resolutions.resolutionsPerAuthority[0].values[0].value;
console.log('Answered: ' + animal);
let animal_id = attributesManager.getSessionAttributes().animal_id || [];
if(animal.id === animal_id) {
return handlerInput.responseBuilder
.speak('Correct! Well done! What should we do next?')
.reprompt('What should we do next?')
.withShouldEndSession(false)
.getResponse();
} else {
return handlerInput.responseBuilder
.speak(`Wrong answer. This is a ${DUPLO_ANIMALS[animal_id].name}. What should we do next?`)
.reprompt('What should we do next?')
.withShouldEndSession(false)
.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)}`);
}
};
// 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)
.getResponse();
}
};
const HelpIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speakOutput = 'How can I help?';
return handlerInput.responseBuilder
.speak(speakOutput)
.withShouldEndSession(false)
.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 attributesManager = handlerInput.attributesManager;
let endpointId = attributesManager.getSessionAttributes().endpointId || [];
let directive = Util.build(endpointId, NAMESPACE, NAME_RESET,
{
});
return handlerInput.responseBuilder
.addDirective(buildStopEventHandlerDirective(handlerInput))
.speak('Goodbye!')
.addDirective(directive)
.withShouldEndSession(true)
.getResponse();
}
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
},
handle(handlerInput) {
// Any cleanup logic goes here.
return handlerInput.responseBuilder
.addDirective(buildStopEventHandlerDirective(handlerInput))
.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 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;
const token = handlerInput.requestEnvelope.request.requestId;
Util.putSessionAttribute(handlerInput, 'token', token);
console.log(customEvent)
if (name === NAME_INIT) {
return handlerInput.responseBuilder
.speak("Duplo Zoo Evaluator is ready. You can learn about animals or take a quiz. What should we do?")
.reprompt('What should we do next?')
.withShouldEndSession(false)
.getResponse();
}
if (name === NAME_LEARN) {
if (payload.found === false) {
return handlerInput.responseBuilder
.speak("Animal not found. Please try a different animal. What should we do next?")
.reprompt('What should we do next?')
.withShouldEndSession(false)
.getResponse();
} else {
const attributesManager = handlerInput.attributesManager;
let animal_id = attributesManager.getSessionAttributes().animal_id || [];
return handlerInput.responseBuilder
.speak(DUPLO_ANIMALS[animal_id].story + ' What should we do next?')
.reprompt('What should we do next?')
.withShouldEndSession(false)
.getResponse();
}
}
if (name === NAME_QUIZ) {
let animal_id = payload.animal_id;
Util.putSessionAttribute(handlerInput, 'animal_id', animal_id);
console.log('Chosen animal: ' + animal_id);
return handlerInput.responseBuilder
.speak('What animal is this?')
.reprompt('What animal is this?')
.withShouldEndSession(false)
.getResponse();
}
return handlerInput.responseBuilder
.speak('unknown event')
.getResponse();
}
};
const ExpiredRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'CustomInterfaceController.Expired'
},
handle(handlerInput) {
console.log("== Custom Event Expiration Input ==");
const attributesManager = handlerInput.attributesManager;
let token = handlerInput.attributesManager.getSessionAttributes().token || '';
let duration = attributesManager.getSessionAttributes().duration || 0;
if (duration > 0) {
Util.putSessionAttribute(handlerInput, 'duration', --duration);
// Extends skill session
return handlerInput.responseBuilder
.addDirective(buildStartEventHandler(token, 30000, {}))
.getResponse();
}
else {
// End skill session
return handlerInput.responseBuilder
.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,
LearnIntentHandler,
QuizIntentHandler,
AnswerIntentHandler,
EventsReceivedRequestHandler,
ExpiredRequestHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
)
.addRequestInterceptors(RequestInterceptor)
.addErrorHandlers(
ErrorHandler,
)
.lambda();
function buildStartEventHandler(token, timeout = 30000, payload) {
return {
type: "CustomInterfaceController.StartEventHandler",
token: token,
expiration: {
durationInMilliseconds: timeout,
expirationPayload: payload
}
};
}
function buildStopEventHandlerDirective(handlerInput) {
let token = handlerInput.attributesManager.getSessionAttributes().token || '';
return {
"type": "CustomInterfaceController.StopEventHandler",
"token": token
}
}
Comments