Steve,
I don’t know anything about Haaska, as I haven’t used it, but when a few of us were using EventGhost, we achieved it. I have pasted a modified AWS Lambda function here that might help you/others. This lambda function will return the session id, request id, spoken text, echo device id, and person id (if voice profiling is enabled). Currently, the way I use this, is I have a text helper entity created in HA. Then, by setting the values at the top of this Lambda function - an Alexa skill calls this lambda - and passes the information to the HA text helper entity. I would add that it would be trivial to change this to fire an event instead of updating an entity… I suppose having it fire an event would probably make more sense.
/**
* EchoToHA - A custom Amazon Alexa Skill Kit that updates the state/attributes of a text helper entity in Home Assistant
* v1.0(20210405)
*
* Ryan Gerken (rdgerken@<googlesemailservice>.com)
*
* Original Credits go to (from the EventGhost Project):
* Brandon Simonsen (m19brandon.shop@<googlesemailservice>.com)
* https://github.com/m19brandon/EchoToEventGhost
* EventGhost Thread: http://www.eventghost.net/forum/viewtopic.php?f=2&t=7429
*/
// --------------- Variables that need to be set ----------------
var https = require('https');
var HA_ip = '<HOMEASSISTANTEXTERNALURL>';
var HA_Port = '<HOMEASSISTANTEXTERNALPORT>';
var HA_token = '<HOMEASSISTNATLONGLIVETOKEN>';
var HA_entityid = '<HOMEASSISTANTENTITYID>';
// Adding Security if you want.
//This ID is can be found under the Alexa tab on the amazon developer console page
//Goto https://developer.amazon.com/edw/home.html#/skills > Click 'View Skill ID'
//var Alexa_Skill_ID = 'amzn1.ask.skill.#';
var HA_uri = '';
var deviceId = '';
var personId = '';
// --------------- Main handler -----------------------
// Route the incoming request based on type (LaunchRequest, IntentRequest,
// etc.) The JSON body of the request is provided in the event parameter.
exports.handler = function (event, context) {
console.log(event);
try {
console.log('event.session.application.applicationId=' + event.session.application.applicationId);
/**
* Adding Security if you want.
* Uncomment this if statement and populate with your skill's application ID to
* prevent someone else from configuring a skill that sends requests to this function.
*/
/*
if (event.session.application.applicationId !== Alexa_Skill_ID) {
console.log('Check the Alexa_Skill_ID value, it did not match')
console.log('event.session.application.applicationId=' + event.session.application.applicationId);
console.log('versus');
console.log('Alexa_Skill_ID=' + Alexa_Skill_ID);
context.fail('Invalid Application ID');
}
*/
// get the deviceId if present
try {
deviceId="";
if( typeof event.context.System.device.deviceId !== 'undefined' ) {
deviceId = event.context.System.device.deviceId;
console.log('deviceId='+deviceId);
} else {
console.log('deviceId not found');
}
}
catch (err) {
console.log('deviceId not found');
}
// get the personId if present
try {
personId="";
if( typeof event.context.System.person.personId !== 'undefined' ) {
personId = event.context.System.person.personId;
console.log('personId='+personId);
} else {
console.log('personId not found');
}
}
catch (err) {
console.log('personId not found');
}
if (event.session.new) {
onSessionStarted({requestId: event.request.requestId}, event.session);
}
if (event.request.type === 'IntentRequest') {
onIntent(event.request,
event.session,
function callback(sessionAttributes, speechletResponse) {
context.succeed(buildResponse(sessionAttributes, speechletResponse));
});
} else if (event.request.type === 'SessionEndedRequest') {
onSessionEnded(event.request, event.session);
context.succeed();
}
} catch (e) {
context.fail('Exception: ' + e);
}
};
// --------------- Helpers that build all of the responses -----------------------
function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
return {
outputSpeech: {
type: 'PlainText',
text: output
},
card: {
type: 'Simple',
title: 'Echo To HA - ' + title,
content: output
},
reprompt: {
outputSpeech: {
type: 'PlainText',
text: repromptText
}
},
shouldEndSession: shouldEndSession
};
}
function buildResponse(sessionAttributes, speechletResponse) {
return {
version: '1.0',
sessionAttributes: sessionAttributes,
response: speechletResponse
};
}
// --------------- Events -----------------------
/**
* Called when the session starts.
*/
function onSessionStarted(sessionStartedRequest, session) {
console.log('onSessionStarted requestId=' + sessionStartedRequest.requestId + ', sessionId=' + session.sessionId);
}
/**
* Called when the user specifies an intent for this skill.
*/
function onIntent(intentRequest, session, callback) {
console.log('onIntent requestId=' + intentRequest.requestId + ', sessionId=' + session.sessionId);
var intent = intentRequest.intent;
//var intentName = intentRequest.intent.name;
// Whatever the Intent is, pass it straight through to Home Assistant
callEchoToHA(intent,intentRequest.requestId,session,callback);
}
/**
* Called when the user ends the session.
* Is not called when the skill returns shouldEndSession=true.
*/
function onSessionEnded(sessionEndedRequest, session) {
console.log('onSessionEnded requestId=' + sessionEndedRequest.requestId + ', sessionId=' + session.sessionId);
// Add cleanup logic here
}
// --------------- Functions that control the skill's behavior -----------------------
/**
* Determines if a session should end based on string search in EG response
*/
function Get_shouldEndSession(string, def)
{
var end = def;
if(string.indexOf('EndSession: ') > -1) {
var lines = string.split('\n');
for(var i = 0;i < lines.length;i++){
if(lines[i].indexOf('EndSession: ') > -1) {
var es = lines[i].split('EndSession:')[1].trim();
if (es === 'yes') {
end = true;
}
if (es === 'no') {
end = false;
}
}
}
}
return end;
}
/**
* This is the main function, this is what sends the intent to HA and then
* determine what to do based on the response HA's response
*/
function callEchoToHA(intent,requestId,session,callback)
{
this.cb = callback;
var cardTitle = 'Home Automation';
var shouldEndSession = true;
console.log('SessionEnd='+shouldEndSession.toString());
var speechOutput = '';
var repromptText = 'I could not understand, please try again';
//Pull the spoken text and format
var actionSlot = intent.slots.Action;
var setAction = actionSlot.value.toLowerCase();
var setActionURI = require('querystring').escape(setAction);
console.log('callEchoToHA - Intent name = ' + intent.name);
console.log('callEchoToHA - Intent = ' + setAction);
console.log('callEchoToHA - Intent = ' + setActionURI);
HA_uri = '/api/states/' + HA_entityid;
console.log('Full URI: ' + HA_uri);
//Build body for the POST data to Home Assistant
var postbody = '{"state": "' + decodeURIComponent(setActionURI) + '","attributes": {"Session": "' + session.sessionId + '","Request": "' + requestId + '","Device": "' + deviceId + '","Person": "' + personId + '"}}';
sendToHA(HA_uri,postbody,function(body) {
var ha_results = body;
if (ha_results != 'Error') {
console.log('Success Body: ' + ha_results);
//Parse the Body results from HA, if the command was unknown we will reprompt
if((ha_results.indexOf('intent: UNKNOWN') > -1)||(ha_results.indexOf('cmd is unknown:') > -1)) {
console.log('callEchoToHA - Results were a unknown command, we will reprompt');
speechOutput = 'Sorry but I did not understand, ' +setAction + ', please try again';
shouldEndSession = Get_shouldEndSession(ha_results,shouldEndSession);
} else {
console.log('callEchoToHA - Results were a known command, all is good');
//Return speech to Alexa not currently implemented-always drops to Else below
if(ha_results.indexOf('Return Msg: ') > -1) {
var lines = ha_results.split('\n');
for(var ix = 0;ix < lines.length;ix++){
if(lines[ix].indexOf('Return Msg: ') > -1) {
var rtn_msg = lines[ix].split('Msg:')[1].trim();
if (rtn_msg !== '') {
setAction = rtn_msg;
shouldEndSession = Get_shouldEndSession(ha_results,shouldEndSession);
}
}
}
speechOutput = setAction;
} else {
speechOutput = 'Got it, working on the command, '+setAction;
shouldEndSession = Get_shouldEndSession(ha_results,shouldEndSession);
}
}
} else {
console.log('callEchoToHA - Error, Home Assistant response error');
speechOutput = 'Error, Home Assistant response error';
}
console.log('SessionEnd='+shouldEndSession.toString());
this.cb({},buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}.bind(this));
}
/**
* Reusable function that handle the post to Home Assistant
*/
function sendToHA(uri,postdata,cb) {
// Options included where we should send the request to with or without basic auth
HA_uri = uri;
var post_options = '';
post_options = {
host: HA_ip,
port: HA_Port,
path: HA_uri,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + HA_token
}
};
console.log('sendToHA - Sending request to ' + post_options.host + ':' + post_options.port + post_options.path);
// Set up the post request
var body='';
var reqPost = https.request(post_options, function(res) {
console.log("statusCode: ", res.statusCode);
res.setEncoding('utf8');
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
console.log("Result", body.toString());
cb(body);
});
res.on('error', function () {
console.log("Result Error", body.toString());
});
});
console.log('sendToHA - Posting:', postdata);
reqPost.write(postdata);
reqPost.end();
}
// --------------- End ----------------