Jeff Sacks
Twitter: @jeff_sacks
GitHub: jrsacks
DRW Trading Group (Chicago)
JavaScript, Ruby, Clojure, Java
Fancy speaker for $179
Always listening for 'Alexa' (or 'Amazon' or 'Echo')
June 25, 2015 Alexa Skills Kit Released
Write your own skill (app) for Alexa
Over 1,400 published skills
Pandora, Dominos Pizza, Uber, Jeff's TV
exports.handler = function (event, context) {
context.succeed({
version: "1.0",
response: {
outputSpeech: {
type: "PlainText",
text: "Hello, this is the first demo"
},
shouldEndSession: true
}
});
}
Utterances
RepeatName To say {name}
Intents
{
"intents": [
{
"intent": "RepeatName",
"slots": [{"name":"name","type":"AMAZON.US_FIRST_NAME"}]
}
]
}
exports.handler = function (event, context) {
context.succeed({
version: "1.0",
response: {
outputSpeech: {
type: "PlainText",
text: "I heard you say: " + event.request.intent.slots.name.value
},
shouldEndSession: true
}
});
}
var AWS = require('aws-sdk');
var QUEUE_URL = "https://sqs.us-east-1.amazonaws.com/664786522906/midwestJs-demo";
var sqs = new AWS.SQS({region : 'us-east-1'});
exports.handler = function (event, context) {
var value = event.request.intent.slots.name.value;
var params = { MessageBody: value, QueueUrl: QUEUE_URL };
sqs.sendMessage(params, function(error){
var text = "I sent " + value + " to SQS";
if(error){
text = "Error sending to SQS";
}
context.succeed({
version: "1.0",
response: {
outputSpeech: {
type: "PlainText",
text: text
},
shouldEndSession: true
}
});
});
}
var AWS = require('aws-sdk');
var sqs = new AWS.SQS({apiVersion: '2012-11-05', region : 'us-east-1'});
var params = {
QueueUrl: "https://sqs.us-east-1.amazonaws.com/664786522906/midwestJs-demo",
MaxNumberOfMessages : 1,
WaitTimeSeconds: 20
};
sqs.receiveMessage(params, function(err, data) {
if(data && data.Messages){
data.Messages.forEach(function(message){
console.log(message.Body);
});
}
});
COMMAND {PARAMETER} {PARAMETER}...
FORCECH 602
TELEPORT GUIDE
TELEPORT LIVETV
var express = require('express');
var net = require('net');
var tivo = net.connect({host: '192.168.1.7', port: 31339}, function(){
var app = express();
app.get('/tivo/ch/:channel', function(request, response){
tivo.write("FORCECH" + request.params.channel + "\r");
response.end();
});
app.get('/tivo/:command/:val', function(request, response){
tivo.write(request.params.command + request.params.val + "\r");
response.end();
});
app.listen(3000);
});
{COMMAND}{PARAMTER}<CR>
4 Character length Command
4 Character length Parameter
function tvConnection(socket){
var volume = 15;
function sendCommand(command, value){
if(value.length === 4){
socket.write(command + value + "\r");
} else {
sendCommand(command, value + " ");
}
};
function setVolume(vol){
volume = vol;
changeVolume(0);
};
function changeVolume(change){
volume += change;
sendCommand("VOLM", volume.toString());
};
function power(on){
if(on){
sendCommand("POWR", "1");
sendCommand("IAVD", "1");
} else {
sendCommand("POWR", "0");
}
}
function mute(on){
sendCommand("MUTE", on ? "1" : "2");
}
return {mute: mute, power : power, setVolume : setVolume, changeVolume: changeVolume};
}
function tivoConnection(socket){
function sendCommand(command, value){
socket.write(command + " " + value + "\r");
}
function setChannel(channel){
sendCommand("FORCECH", channel);
}
return {sendCommand : sendCommand, setChannel : setChannel};
}
var tvSocket = net.connect({host: '192.168.1.3', port: 10002}, function(){
var tivoSocket = net.connect({host: '192.168.1.7', port: 31339}, function(){
buildApp(tvConnection(tvSocket), tivoConnection(tivoSocket));
});
});
function buildApp(tv, tivo){
var app = express();
app.use(express.static('public'));
app.use(function (request, response, next) {
next();
response.end();
});
app.get('/tivo/ch/:channel', function(request, response){
tv.power(true);
tivo.setChannel(request.params.channel);
});
app.get('/tivo/:command/:val', function(request, response){
tivo.sendCommand(request.params.command, request.params.val);
});
app.listen(3000);
}
app.get('/tv/volume/:vol', function(request, response){
var volParam = request.params.vol;
if(volParam === 'up'){
tv.changeVolume(1);
} else if (volParam === 'down'){
tv.changeVolume(-1);
} else {
tv.setVolume(parseInt(volParam, 10));
}
});
app.get('/tv/mute', function(request, response){
tv.mute(true);
});
app.get('/tv/unmute', function(request, response){
tv.mute(false);
});
app.get('/tv/power/off', function(request, response){
tv.power(false);
});
{
"intents": [
{"intent": "ChangeChannel",
"slots": [{"name": "Channel","type": "LIST_OF_CHANNELS"}]},
{ "intent": "Off"},
{ "intent": "VolUp"},
{ "intent": "VolDown"},
{ "intent": "VolSet",
"slots": [{"name":"Volume","type":"AMAZON.NUMBER"}]},
{ "intent": "Back"},
{ "intent": "Mute"},
{ "intent": "UnMute"}
]
}
LIST_OF_CHANNELS CBS | NBC | ABC | WGN | FOX | ESPN | ESPN2 | CSN | BTN...
Off turn off
ChangeChannel turn on {Channel}
ChangeChannel change to {Channel}
VolUp turn up the volume
VolUp make it louder
VolUp be louder
VolDown turn down the volume
VolDown make it softer
VolDown make it quieter
VolSet set the volume to {Volume}
VolSet make the volume {Volume}
Back switch back
Back switch to the last channel
Back go back
Mute mute
UnMute unmute
var QUEUE_URL = 'https://sqs.us-east-1.amazonaws.com/664786522906/alexa-tv-queue';
var AWS = require('aws-sdk');
var sqs = new AWS.SQS({region : 'us-east-1'});
exports.handler = function (event, context) {
handleIntent(event.request, function(response){
context.succeed({
version: "1.0",
response: {
outputSpeech: {
type: "PlainText",
text: response
},
shouldEndSession: true
}
});
});
};
function sendToSqs(message, speech, callback){
var params = { MessageBody: message, QueueUrl: QUEUE_URL };
sqs.sendMessage(params, function(err){ callback(speech); });
}
function handleIntent(intentRequest, callback) {
var intent = intentRequest.intent;
var intentName = intent.name;
var invokeSqs = function(message, speech){
return function(){ sendToSqs(message, speech, callback); };
};
var intents = {
"Off" : invokeSqs("/tv/power/off", "Now Turning off the TV"),
"VolUp" : invokeSqs("/tv/volume/up", "Turning Volume Up"),
"VolDown" : invokeSqs("/tv/volume/down", "Turning Volume Down"),
"Mute" : invokeSqs("/tv/mute", "Muting"),
"UnMute" : invokeSqs("/tv/unmute", "Unmuting"),
"Back" : invokeSqs("/tivo/IRCODE/ENTER", "Switching Back"),
"VolSet" : function(){ setVolume(intent, callback);},
"ChangeChannel" : function(){ changeChannel(intent, callback);}
};
intents[intentName]();
}
function channelNumber(val){
if(parseInt(val, 10) > 0){
return val;
}
var channels = {
cbs: 602, nbc: 605, abc: 607,
wgn: 609, fox: 612, espn: 681,
espn2: 682
};
var lower = val.toLowerCase();
return channels[lower];
}
function changeChannel(intent, callback){
var num = channelNumber(intent.slots.Channel.value);
sendToSqs("/tivo/ch/" + num, "Changing the channel to " + num, callback);
}
function setVolume(intent, callback){
var vol = intent.slots.Volume.value;
sendToSqs("/tv/volume/" + vol, "Changing the volume to " + vol, callback);
}
#!/usr/bin/env sh
set -e
while true; do
node sqsListener.js | xargs -I{} curl http://127.0.0.1:3000{}
done