alexa-samples / Alexa-Gadgets-Raspberry-Pi-Samples

This repository enables you to prototype an Alexa Gadget using Raspberry Pi. It includes Python-based software, sample projects, and step-by-step instructional guides that walk you through how to use key features of the Alexa Gadgets Toolkit to create new, engaging customer interactions with gadget devices.
Other
91 stars 29 forks source link

CustomInterfaceEventHandler (line 211) is getting into infinite loop #16

Open monupurohit opened 3 years ago

monupurohit commented 3 years ago

// // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // These materials are licensed under the Amazon Software License in connection with the Alexa Gadgets Program. // The Agreement is available at https://aws.amazon.com/asl/. // See the Agreement for the specific terms and conditions of the Agreement. // Capitalized terms not defined in this file have the meanings given to them in the Agreement. // 'use strict';

const Alexa = require('ask-sdk-core'); const Uuid = require('uuid/v4');

let skill; exports.handler = function(event, context) {

if (!skill) {
    skill = Alexa.SkillBuilders.custom()
        .addRequestHandlers(
            handler.LaunchRequestHandler,
            handler.YesIntentHandler,
            handler.ToyIntentHandler,
            handler.StopToyIntentHandler,
            handler.NoIntentHandler,
            handler.CustomInterfaceEventHandler,
            handler.CustomInterfaceExpirationHandler,
            handler.StopAndCancelIntentHandler,
            handler.SessionEndedRequestHandler,
            handler.DefaultHandler
        )
        .addRequestInterceptors(handler.RequestInterceptor)
        .addResponseInterceptors(handler.ResponseInterceptor)
        .addErrorHandlers(handler.ErrorHandler)
        .withApiClient(new Alexa.DefaultApiClient())
        .create();
}
return skill.invoke(event, context);

};

const handler = { LaunchRequestHandler: { canHandle(handlerInput) { let { request } = handlerInput.requestEnvelope; console.log("LaunchRequestHandler: checking if it can handle " + request.type); return request.type === 'LaunchRequest'; }, async handle(handlerInput) { console.log("== Launch Intent =="); console.log(JSON.stringify(handlerInput.requestEnvelope));

        let response;
        try {
            // Get connected gadget endpointId.
            console.log("Checking endpoint");
            response = await getConnectedEndpointsResponse(handlerInput);
            console.log("v1/endpoints response: " + JSON.stringify(response));

            if ((response.endpoints || []).length === 0) {
                console.log('No connected gadget endpoints available');
                response = handlerInput.responseBuilder
                    .speak("No gadgets found. Please try again after connecting your gadget.")
                    .getResponse();
                return response;
            }

            let endpointId = response.endpoints[0].endpointId;

            // Store endpointId for using it to send custom directives later.
            console.log("Received endpoints. Storing Endpoint Id: " + endpointId);
            const attributesManager = handlerInput.attributesManager;
            let sessionAttributes = attributesManager.getSessionAttributes();
            sessionAttributes.endpointId = endpointId;
            attributesManager.setSessionAttributes(sessionAttributes);

            return handlerInput.responseBuilder
                .speak("Hello, Welcome to Virtual Diwali " +
                    "Today we will fire crackers in a eco friendly way. Say start the show when you are you ready?")
                .withShouldEndSession(false)
                // Send the BlindLED directive to make the LED green for 20 seconds.
                .addDirective(buildBlinkLEDDirective(endpointId, ['GREEN'], 1000, 20, false))
                .getResponse();
        }
        catch (err) {
            console.log("An error occurred while getting endpoints", err);
            response = handlerInput.responseBuilder
                .speak("I wasn't able to get connected endpoints. Please try again.")
                .withShouldEndSession(true)
                .getResponse();
            return response;
        }
    }
},
YesIntentHandler: {
    canHandle(handlerInput) {
        let { request } = handlerInput.requestEnvelope;
        let intentName = request.intent ? request.intent.name : '';
        console.log("YesIntentHandler: checking if it can handle " +
            request.type + " for " + intentName);
        return request.intent && request.intent.name === 'AMAZON.YesIntent';
    },
    handle(handlerInput) {
        // Retrieve the stored gadget endpointId from the SessionAttributes.
        const attributesManager = handlerInput.attributesManager;
        let sessionAttributes = attributesManager.getSessionAttributes();
        let endpointId = sessionAttributes.endpointId;

        // Create a token to be assigned to the EventHandler and store it
        // in session attributes for stopping the EventHandler later.
        sessionAttributes.token = Uuid();
        attributesManager.setSessionAttributes(sessionAttributes);

        console.log("YesIntent received. Starting game.");

        return handlerInput.responseBuilder
            // Send the BlindLEDDirective to trigger the cycling animation of the LED.
            .addDirective(buildBlinkLEDDirective(endpointId, ['RED', 'YELLOW', 'GREEN', 'CYAN', 'BLUE', 'PURPLE', 'WHITE'],
                1000, 2, true))
            // Start a EventHandler for 10 seconds to receive only one
            // 'Custom.ColorCyclerGadget.ReportColor' event and terminate.
            .addDirective(buildStartEventHandlerDirective(sessionAttributes.token, 10000,
                'Custom.ColorCyclerGadget', 'ReportColor', 'SEND_AND_TERMINATE',
                { 'data': "You didn't press the button. Good bye!" }))
            .getResponse();
    }
},
ToyIntentHandler: {
    canHandle(handlerInput) {
        let { request } = handlerInput.requestEnvelope;
        let intentName = request.intent ? request.intent.name : '';
        console.log("ToyIntentHandler: checking if it can handle " +
            request.type + " for " + intentName);
        return request.intent && request.intent.name === 'ToyIntent';
    },
    handle(handlerInput) {
        // Retrieve the stored gadget endpointId from the SessionAttributes.
        const attributesManager = handlerInput.attributesManager;
        let sessionAttributes = attributesManager.getSessionAttributes();
        let endpointId = sessionAttributes.endpointId;

        // Create a token to be assigned to the EventHandler and store it
        // in session attributes for stopping the EventHandler later.
        sessionAttributes.token = Uuid();
        attributesManager.setSessionAttributes(sessionAttributes);

        console.log("ToyIntent received. Starting game.");

        return handlerInput.responseBuilder
                .speak("Put the cracker on launchpad and say fire")
                .withShouldEndSession(false)
                //.addDirective(buildBlinkLEDDirective(endpointId, ['GREEN'], 1000, 20, false))
                .addDirective(buildLightDirective(endpointId, 0, true))
                .reprompt('Say Fire when you are ready')
                .getResponse();
    }
},
StopToyIntentHandler: {
    canHandle(handlerInput) {
        let { request } = handlerInput.requestEnvelope;
        let intentName = request.intent ? request.intent.name : '';
        console.log("StopToyIntentHandler: checking if it can handle " +
            request.type + " for " + intentName);
        return request.intent && request.intent.name === 'StopToyIntent';
    },
    handle(handlerInput) {
        // Retrieve the stored gadget endpointId from the SessionAttributes.
        const attributesManager = handlerInput.attributesManager;
        let sessionAttributes = attributesManager.getSessionAttributes();
        let endpointId = sessionAttributes.endpointId;

        // Create a token to be assigned to the EventHandler and store it
        // in session attributes for stopping the EventHandler later.
        sessionAttributes.token = Uuid();
        attributesManager.setSessionAttributes(sessionAttributes);

        console.log("StopToyIntent received. Stoping game.");
        //var speechText = 'Sure, here we go' + '<audio src="soundbank://soundlibrary/air/fire_extinguisher/fire_extinguisher_01"/>';
        return handlerInput.responseBuilder
                //.speak(speechText)
                .speak("Firing the selected cracker")
                .withShouldEndSession(false)
                //.addDirective(buildBlinkLEDDirective(endpointId, ['GREEN'], 1000, 20, false))
                .addDirective(buildLightStopDirective(endpointId, 1, true))
                // Start a EventHandler for 10 seconds to receive only one
               // 'Custom.ColorCyclerGadget.ReportColor' event and terminate.
                .addDirective(buildStartEventHandlerDirective(sessionAttributes.token, 10000,
                'Custom.ColorCyclerGadget', 'ReportColor', 'SEND_AND_TERMINATE',
                { 'data': "You didn't press the button. Good bye!" }))
                .getResponse(); 
    }
},
NoIntentHandler: {
    canHandle(handlerInput) {
        let { request } = handlerInput.requestEnvelope;
        let intentName = request.intent ? request.intent.name : '';
        console.log("NoIntentHandler: checking if it can handle " +
            request.type + " for " + intentName);
        return request.intent && request.intent.name === 'AMAZON.NoIntent';
    },
    handle(handlerInput) {
        console.log("Received NoIntent..Exiting.");
        const attributesManager = handlerInput.attributesManager;
        let sessionAttributes = attributesManager.getSessionAttributes();

        // Send StopLED directive to stop LED animation and end skill session.
        return handlerInput.responseBuilder
            .addDirective(buildStopLEDDirective(sessionAttributes.endpointId))
            .speak("Alright. Good bye!")
            .withShouldEndSession(true)
            .getResponse();
    }
},
CustomInterfaceEventHandler: {
    canHandle(handlerInput) {
        let { request } = handlerInput.requestEnvelope;
        console.log("CustomEventHandler: checking if it can handle " + request.type);
        return request.type === 'CustomInterfaceController.EventsReceived';
    },
    handle(handlerInput) {
        console.log("== Received Custom Event ==");

        let { request } = handlerInput.requestEnvelope;

        const attributesManager = handlerInput.attributesManager;
        let sessionAttributes = attributesManager.getSessionAttributes();

        // Validate eventHandler token 123
        if (sessionAttributes.token !== request.token) {
            console.log("EventHandler token doesn't match. Ignoring this event.");
            return handlerInput.responseBuilder
                .speak("EventHandler token doesn't match. Ignoring this event.")
                .getResponse();
        }

        let customEvent = request.events[0];
        let payload = customEvent.payload;
        let namespace = customEvent.header.namespace;
        let name = customEvent.header.name;

        let response = handlerInput.responseBuilder;
        var speechText;
        //var speechText = 'here we go' + '<audio src="soundbank://soundlibrary/impacts/amzn_sfx_fireworks_firecrackers_01"/>';
       //var speechText = 'here we go' + '<audio src="soundbank://soundlibrary/air/fire_extinguisher/fire_extinguisher_01"/>';

       if (payload.color === 'RED')
       {
           speechText = 'here we go' + '<audio src="soundbank://soundlibrary/impacts/amzn_sfx_fireworks_firecrackers_01"/>';
       }
       if (payload.color === 'BLUE')
       {
           speechText = 'here we go' + '<audio src="soundbank://soundlibrary/air/fire_extinguisher/fire_extinguisher_01"/>';
       }

        if (namespace === 'Custom.ColorCyclerGadget' && name === 'ReportColor') {
            //return response.speak(payload.color + ' is the selected color. Thank you for playing. Good bye!')
            return response.speak(payload.color + ' is the selected color' +  speechText)
                .withShouldEndSession(false)
                .reprompt('what next do you want')
                .getResponse();
        }

        //return response;
    }
},
CustomInterfaceExpirationHandler: {
    canHandle(handlerInput) {
        let { request } = handlerInput.requestEnvelope;
        console.log("CustomEventHandler: checking if it can handle " + request.type);
        return request.type === 'CustomInterfaceController.Expired';
    },
    handle(handlerInput) {
        console.log("== Custom Event Expiration Input ==");

        let { request } = handlerInput.requestEnvelope;

        const attributesManager = handlerInput.attributesManager;
        let sessionAttributes = attributesManager.getSessionAttributes();

        // When the EventHandler expires, send StopLED directive to stop LED animation
        // and end skill session.
        return handlerInput.responseBuilder
            .addDirective(buildStopLEDDirective(sessionAttributes.endpointId))
            .withShouldEndSession(true)
            .speak(request.expirationPayload.data)
            .getResponse();
    }
},
StopAndCancelIntentHandler: {
    canHandle(handlerInput) {
        const { request } = handlerInput.requestEnvelope;
        const intentName = request.intent ? request.intent.name : '';
        console.log("StopAndCancelIntentHandler: checking if it can handle " +
            request.type + " for " + intentName);
        return request.type === 'IntentRequest' &&
            (intentName === 'AMAZON.StopIntent' || intentName === 'AMAZON.CancelIntent');
    },
    handle(handlerInput) {
        console.log("Received a Stop or a Cancel Intent..");

        let { attributesManager, responseBuilder } = handlerInput;
        let sessionAttributes = attributesManager.getSessionAttributes();

        // When the user stops the skill, stop the EventHandler,
        // send StopLED directive to stop LED animation and end skill session.
        if (sessionAttributes.token) {
            console.log("Active session detected, sending stop EventHandlerDirective.");
            responseBuilder.addDirective(buildStopEventHandlerDirective(sessionAttributes.token));
        }

        return responseBuilder.speak("Alright. see you later.")
            .addDirective(buildStopLEDDirective(sessionAttributes.endpointId))
            .withShouldEndSession(true)
            .getResponse();
    }
},
SessionEndedRequestHandler: {
    canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
    },
    handle(handlerInput) {
        console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);
        return handlerInput.responseBuilder.getResponse();
    },
},
ErrorHandler: {
    canHandle(handlerInput, error) {
        let { request } = handlerInput.requestEnvelope;
        console.log("ErrorHandler: checking if it can handle " +
            request.type + ": [" + error.name + "] -> " + !!error.name);
        return !!error.name;
    },
    handle(handlerInput, error) {
        console.log("Global.ErrorHandler: error = " + error.message);

        return handlerInput.responseBuilder
            .speak("I'm sorry, something went wrong!")
            .getResponse();
    }
},
RequestInterceptor: {
    process(handlerInput) {
        let { attributesManager, requestEnvelope } = handlerInput;
        let sessionAttributes = attributesManager.getSessionAttributes();

        // Log the request for debugging purposes.
        console.log(`==Request==${JSON.stringify(requestEnvelope)}`);
        console.log(`==SessionAttributes==${JSON.stringify(sessionAttributes, null, 2)}`);
    }
},
ResponseInterceptor: {
    process(handlerInput) {

        let { attributesManager, responseBuilder } = handlerInput;
        let response = responseBuilder.getResponse();
        let sessionAttributes = attributesManager.getSessionAttributes();

        // Log the response for debugging purposes.
        console.log(`==Response==${JSON.stringify(response)}`);
        console.log(`==SessionAttributes==${JSON.stringify(sessionAttributes, null, 2)}`);
    }
},
DefaultHandler: {
    canHandle(handlerInput) {
        let { request } = handlerInput.requestEnvelope;
        let intentName = request.intent ? request.intent.name : '';
        console.log("DefaultHandler: checking if it can handle " +
            request.type + " for " + intentName);
        return true;
    },
    handle(handlerInput) {
        console.log("Unsupported Intent receive..Exiting.");
        return handlerInput.responseBuilder
            .speak("Unsupported Intent received. Exiting.")
            .getResponse();
    }
}

};

function getConnectedEndpointsResponse(handlerInput) { return handlerInput.serviceClientFactory.getEndpointEnumerationServiceClient().getEndpoints(); }

function buildBlinkLEDDirective(endpointId, colors_list, intervalMs, iterations, startGame) { return { type: 'CustomInterfaceController.SendDirective', header: { name: 'BlinkLED', namespace: 'Custom.ColorCyclerGadget' }, endpoint: { endpointId: endpointId }, payload: { colors_list: colors_list, intervalMs: intervalMs, iterations: iterations, startGame: startGame } }; }

function buildLightDirective(endpointId, colors_state, startGame) { return { type: 'CustomInterfaceController.SendDirective', header: { name: 'BlinkLight', namespace: 'Custom.ColorCyclerGadget' }, endpoint: { endpointId: endpointId }, payload: { colors_state: colors_state, startGame: startGame } }; }

function buildLightStopDirective(endpointId, colors_state, startGame) { return { type: 'CustomInterfaceController.SendDirective', header: { name: 'StopLight', namespace: 'Custom.ColorCyclerGadget' }, endpoint: { endpointId: endpointId }, payload: { colors_state: colors_state, startGame: startGame } }; }

function buildStopLEDDirective(endpointId) { return { type: 'CustomInterfaceController.SendDirective', header: { name: 'StopLED', namespace: 'Custom.ColorCyclerGadget' }, endpoint: { endpointId: endpointId }, payload: {} }; }

function buildStartEventHandlerDirective(token, durationMs, namespace, name, filterMatchAction, expirationPayload) { return { type: "CustomInterfaceController.StartEventHandler", token: token, eventFilter: { filterExpression: { 'and': [ { '==': [{ 'var': 'header.namespace' }, namespace] }, { '==': [{ 'var': 'header.name' }, name] } ] }, filterMatchAction: filterMatchAction }, expiration: { durationInMilliseconds: durationMs, expirationPayload: expirationPayload } }; }

function buildStopEventHandlerDirective(token) { return { type: "CustomInterfaceController.StopEventHandler", token: token }; }

monupurohit commented 3 years ago

Python code on RPi

Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.

These materials are licensed under the Amazon Software License in connection with the Alexa Gadgets Program.

The Agreement is available at https://aws.amazon.com/asl/.

See the Agreement for the specific terms and conditions of the Agreement.

Capitalized terms not defined in this file have the meanings given to them in the Agreement.

#

import json import logging import sys import threading import time

from gpiozero import RGBLED, Button, LED from colorzero import Color from agt import AlexaGadget

GPIO_PIN = 14

LED = LED(GPIO_PIN)

logging.basicConfig(stream=sys.stdout, level=logging.INFO) logger = logging.getLogger(name)

GPIO Pins

GPIO_LED_RED = 2 GPIO_LED_GREEN = 3 GPIO_LED_BLUE = 4 GPIO_BUTTON = 15

Setup RGB LED

set active_high to False for common anode RGB LED

else set to True for common cathode RGB LED

RGB_LED = RGBLED(GPIO_LED_RED, GPIO_LED_GREEN, GPIO_LED_BLUE, active_high=False, initial_value=(0, 0, 0))

Setup Button

BUTTON = Button(GPIO_BUTTON)

words_to_yell = 1 #any default value

class ColorCyclerGadget(AlexaGadget): """ An Alexa Gadget that cycles through colors using RGB LED and reports the color to the skill upon button press """

def __init__(self):
    super().__init__()

    # Color animation states
    self.color = None
    self.words_to_yell = None
    self.colors_list = []
    self.interval_ms = 0
    self.iterations = 0
    self.cycle_count = 0
    self.keep_cycling = False
    self.game_active = False

    # Setup a lock to be used for avoiding race conditions
    # during color animation state updates
    self.lock = threading.Lock()
    # Setup a separate thread for LED to cycle through colors
    self.led_thread = threading.Thread(target=self._led_blink)
    self.led_thread.start()
    BUTTON.when_pressed = self._button_pressed
    #self._yell_this()

def on_custom_colorcyclergadget_blinkled(self, directive):
    """
    Handles Custom.ColorCyclerGadget.BlinkLED directive sent from skill
    by triggering LED color cycling animations based on the received parameters
    """
    payload = json.loads(directive.payload.decode("utf-8"))

    logger.info('BlinkLED directive received: LED will cycle through ' + str(payload['colors_list']) + ' colors')

    self.lock.acquire()
    # Initialize the color animation states based on parameters received from skill
    self.colors_list = payload['colors_list']
    self.interval_ms = payload['intervalMs']
    self.iterations = payload['iterations']
    self.cycle_count = 0
    self.game_active = bool(payload['startGame'])
    self.keep_cycling = True
    self.lock.release()
def on_custom_colorcyclergadget_blinklight(self, directive):
    """
    Handles Custom.ColorCyclerGadget.BlinkLED directive sent from skill
    by triggering LED color cycling animations based on the received parameters
    """
    payload = json.loads(directive.payload.decode("utf-8"))

    logger.info('BlinkLight directive received: Light state will be ' + str(payload['colors_state']))

    self.lock.acquire()
    # Initialize the color animation states based on parameters received from skill
    self.game_active = bool(payload['startGame'])
    self.lock.release() 
    LED.on() 
    self._yell_this()       

def on_custom_colorcyclergadget_stoplight(self, directive):
    """
    Handles Custom.ColorCyclerGadget.BlinkLED directive sent from skill
    by triggering LED color cycling animations based on the received parameters
    """
    payload = json.loads(directive.payload.decode("utf-8"))

    logger.info('StopLight directive received: Light state will be ' + str(payload['colors_state']))

    self.lock.acquire()
    # Initialize the color animation states based on parameters received from skill
    self.game_active = bool(payload['startGame'])
    self.lock.release() 
    LED.off()
    print(self.words_to_yell)
    #payload = {'color': self.words_to_yell}
    #self.send_custom_event('Custom.ColorCyclerGadget', 'ReportColor', payload)
    payload = {'color': self.words_to_yell}
    self.send_custom_event('Custom.ColorCyclerGadget', 'ReportColor', payload)

def on_custom_colorcyclergadget_stopled(self, directive):
    """
    Handles Custom.ColorCyclerGadget.StopLED directive sent from skill
    by stopping the LED animations
    """
    logger.info('StopLED directive received: Turning off LED')

    # Turn off the LED and disable the color animation states to stop the LED cycling animation
    RGB_LED.off()
    self.lock.acquire()
    self.keep_cycling = False
    self.game_active = False
    self.lock.release()

def _button_pressed(self):
    """
    Callback to report the LED color to the skill when the button is pressed
    """
    if self.game_active:
        logger.info('Button Pressed: Current color = ' + self.color)

        # Send custom event to skill with the color of the LED
        payload = {'color': self.color}
        self.send_custom_event(
            'Custom.ColorCyclerGadget', 'ReportColor', payload)

        self.lock.acquire()
        # Stop the LED cycling animation
        self.keep_cycling = False
        self.lock.release()

def _yell_this(self):
  self.words_to_yell=input("enter words to yell:")
  print(self.words_to_yell)

def _led_blink(self):
    """
    Plays the LED cycling animation based on the color animation states
    """
    while True:
        # If cycling animation is still active
        if self.keep_cycling and self.cycle_count < len(self.colors_list) * self.iterations:
            self.lock.acquire()
            self.color = self.colors_list[self.cycle_count
                                          % len(self.colors_list)]
            self.cycle_count = self.cycle_count + 1
            self.lock.release()

            # Set the color for the LED
            RGB_LED.color = Color(self.color.lower())

            # Display the color for specified interval before switching again
            time.sleep(self.interval_ms/1000)

        # If button is pressed, display the current color for 5 seconds
        elif not self.keep_cycling and self.game_active:
            time.sleep(5)
            RGB_LED.off()
            self.lock.acquire()
            self.game_active = False
            self.lock.release()
        else:
            RGB_LED.off()
            time.sleep(0.1)

if name == 'main': try: ColorCyclerGadget().main() finally: RGB_LED.close() BUTTON.close()