AWS Lambda Proxy Custom Alexa Skill when you don't have https

Hi @tarnfeather

I tried using your method as described but on AWS Lambda for the runtime there is no option for NodeJS, the only options are: node.js 4.3, C#, Java 8, Edge Node.js 4.3, Python 2.7.

Can you please tell me which one to select as I tried it with node.js 4.3 and it did not work.

Hi @weissblut

I am going through the same problem. Could you please share your code as I get an error:
{
“errorMessage”: "Exception: TypeError: Cannot read property ‘application’ of undefined”
}

I am not sure if it is the spacing issue.

Can you please help?

My setup:

  • VMWare ESXI Server
  • Home Assistant VMWare not accessible from web (only via VPN)

Is it mandatory to expose the Hass instance to the web in order to use Alexa? I have Hass only accessible via VPN from outside and I would like to keep it this way. (I’m just wondering because in the script in the first post the local IP adress is also listed)

Regarding my VPN setup: Could I run a NodeJS Server in another VMWare communicating with Hass locally?

I too am wondering about this part.

I followed the walk-through and think I have the “where are we” part working but cannot get “where is corey” to work. I have put-off the whole Alexa intent part of my setup as I keep seeing people have difficulty but I would really like to get it setup, integrated, and expand everything.

When I test on Lambda I get:

{
  "errorMessage": "Exception: TypeError: Cannot read property 'application' of undefined"
}

Here is the script:

var http = require('http');
var URLParser = require('url');

exports.handler = function (json, context) {
    try {
        // A list of URL's to call for each applicationId
        var handlers = {
            'appId':'url',
            'amzn1.ask.skill.9c72c728-a69c-44e0-9e4b-2d4d4dc4aade':'http://Redacted:Redacted/api/alexa?api_password=Redacted'
        };

        // Look up the url to call based on the appId
        var url = handlers[json.session.application.applicationId];
        if (!url) { context.fail("No url found for application id"); }
        var parts = URLParser.parse(url);

        var post_data = JSON.stringify(json);

        // An object of options to indicate where to post to
        var post_options = {
            host: parts.hostname,
            auth: parts.auth,
            port: (parts.port || 80),
            path: parts.path,
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Content-Length': post_data.length
            }
        };
        // Initiate the request to the HTTP endpoint
        var req = http.request(post_options,function(res) {
            var body = "";
            // Data may be chunked
            res.on('data', function(chunk) {
                body += chunk;
            });
            res.on('end', function() {
                // When data is done, finish the request
                context.succeed(JSON.parse(body));
            });
        });
        req.on('error', function(e) {
            context.fail('problem with request: ' + e.message);
        });
        // Send the JSON data
        req.write(post_data);
        req.end();        
    } catch (e) {
        context.fail("Exception: " + e);
    }
};

No idea mate.

I am getting the same error: “errorMessage”: "Exception: TypeError: Cannot read property ‘application’ of undefined”

Any help would be appreciated.

Hi guys,

apologies for the delay, I was traveling. Here’s my code, hope it helps.

"use strict"

var http = require('http');
var URLParser = require('url');

exports.handler = function (json, context) {
    try {
        // A list of URL's to call for each applicationId
        var handlers = {
            'appId':'url',
            'YOUR_SKILL_ID':'http://YOUR_HA_ADDRESS:8123/api/alexa?api_password=YOUR_PWD'
        };

        // Look up the url to call based on the appId
        var url = handlers[json.session.application.applicationId];
        if (!url) { context.fail("No url found for application id"); }
        var parts = URLParser.parse(url);

        var post_data = JSON.stringify(json);

        // An object of options to indicate where to post to
        var post_options = {
            host: parts.hostname,
            auth: parts.auth,
            port: (parts.port || 80),
            path: parts.path,
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Content-Length': post_data.length
            }
        };
        // Initiate the request to the HTTP endpoint
        var req = http.request(post_options,function(res) {
            var body = "";
            // Data may be chunked
            res.on('data', function(chunk) {
                body += chunk;
            });
            res.on('end', function() {
                // When data is done, finish the request
                context.succeed(JSON.parse(body));
            });
        });
        req.on('error', function(e) {
            context.fail('problem with request: ' + e.message);
        });
        // Send the JSON data
        req.write(post_data);
        req.end();        
    } catch (e) {
        context.fail("Exception: " + e);
    }
};
1 Like

Hi thanks for the code.

Can I ask you what runtime are you using?

sure - Node JS 4.3.

Make sure your router redirects the request to the right port / IP address for your HASS server (accessible from outside network).

Hi I am getting this error:

{
  "errorMessage": "Exception: TypeError: Cannot read property 'application' of undefined"
}

Any idea what could be the problem?

Thanks.

Thanks. I have tested this and it works for custom skills. However, I can’t find a way to make it works for Flash Briefing skill. The Feed URL in Configuration cannot accept arn:aws:lambda:us-east-1:XXXXXXXXXXX:function:YYYYYYYYYY.

Please help.

If you goto AWS lambda -> functions -> <your_funcation> -> Test and you get the below error, I don’t think that is exactly a true error. I have another working lambda function that spits out the same message.

{
  "errorMessage": "Exception: TypeError: Cannot read property 'application' of undefined"
}

I’m getting this error when asking where a person is. Errors is from hass.

17-02-18 13:20:43 WARNING (MainThread) [homeassistant.core] Unable to find service notify/notify

I think something is missing in the config or the example is incomplete?

Update:
So I got the example working of where are we and where is [name]

I commented out action with “notify.notify”. I think the point of that is a notification example. Not literally notify.notify. Now to expand on it…

I am able to make it work using ‘http’, but how do we make this work with ‘https’ Home Assistant URL.

i am getting below avs lambda error while using ‘https’

The remote endpoint could not be called, or the response it returned was invalid.

var http = require('http');
var URLParser = require('url');

exports.handler = function (json, context) {
    try {
        // A list of URL's to call for each applicationId
        var handlers = {
            'appId':'url',
            'amzn1.ask.skill.xxxx-xxxx-xxxx-xxxx-xxx':'https://xxxx.duckdns.org/api/alexa?api_password=xxxx'
        };

        // Look up the url to call based on the appId
        var url = handlers[json.session.application.applicationId];
        if (!url) { context.fail("No url found for application id"); }
        var parts = URLParser.parse(url);

        var post_data = JSON.stringify(json);

        // An object of options to indicate where to post to
        var post_options = {
            host: parts.hostname,
            auth: parts.auth,
            port: (parts.port || 80),
            path: parts.path,
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Content-Length': post_data.length
            }
        };
        // Initiate the request to the HTTP endpoint
        var req = http.request(post_options,function(res) {
            var body = "";
            // Data may be chunked
            res.on('data', function(chunk) {
                body += chunk;
            });
            res.on('end', function() {
                // When data is done, finish the request
                context.succeed(JSON.parse(body));
            });
        });
        req.on('error', function(e) {
            context.fail('problem with request: ' + e.message);
        });
        // Send the JSON data
        req.write(post_data);
        req.end();        
    } catch (e) {
        context.fail("Exception: " + e);
    }
};

I got it working by changing ‘http’ to ‘https’ and port to 443.

var https = require('https');
var URLParser = require('url');

exports.handler = function (json, context) {
    try {
        // A list of URL's to call for each applicationId
        var handlers = {
            'appId':'url',
            'amzn1.ask.skill.xxxx-xxxx-xxxx-xxxx-xxxx':'https://xxxx.duckdns.org/api/alexa?api_password=xxxx'
        };

        // Look up the url to call based on the appId
        var url = handlers[json.session.application.applicationId];
        if (!url) { context.fail("No url found for application id"); }
        var parts = URLParser.parse(url);

        var post_data = JSON.stringify(json);

        // An object of options to indicate where to post to
        var post_options = {
            host: parts.hostname,
            auth: parts.auth,
            port: (parts.port || 443),
            path: parts.path,
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Content-Length': post_data.length
            }
        };
        // Initiate the request to the HTTPS endpoint
        var req = https.request(post_options,function(res) {
            var body = "";
            // Data may be chunked
            res.on('data', function(chunk) {
                body += chunk;
            });
            res.on('end', function() {
                // When data is done, finish the request
                context.succeed(JSON.parse(body));
            });
        });
        req.on('error', function(e) {
            context.fail('problem with request: ' + e.message);
        });
        // Send the JSON data
        req.write(post_data);
        req.end();        
    } catch (e) {
        context.fail("Exception: " + e);
    }
};

This requires that you have set up SSL and have a signed certificate, I suppose? (In that case, you can also use Home Assistant’s Alexa component directly (see here), right?)

Do you know what the securtiy implications of this approach are? I would think that anyone who could intercept the HTTP request could read your password (since that’s somewhere in the URL that is sent unencrypted), which seems to make it very insecure, but I’m not an expert, so I hope someone can explain why I’m wrong here.

No. It does not require SSL (as the thread title states). I think the security implications are relatively clear when not using SSL. I also think other options require port 443 but I’m not sure so this solution may have that advantage as well.

Thanks for the reply. My question about needing SSL was actually meant for @hareeshmu’s approach, so I edited my question to make that clearer. (Sorry, it wasn’t so clear before. I don’t understand why it is not showing up as a reply to his comment.)

Regarding the security of not using SSL: so I am right in saying that the password is sent in plain text when not using SSL? Then it would be nice to warn people about that.

I dont have any Echo device, I’m using the software Alexa AVS Service
So, I believe HA Alexa built-in component will not work with AVS Service. Hence chose the custom skill method.
I am running HA in SSL using LetsEncrypt certificates.

Has anyone tried setting this up using ngork?