Joining Netatmo Weather Station to Amazon Echo (Alexa)

[Full tutorial in answer to the question below. Feedback is welcome!]

I'm trying to create an AWS Lambda function to use for Amazon Alexa's ability to get weather information from my WeatherStation weather station. Basically, I need to connect to the Netatmo cloud through an HTTP request.

Here is a snippet of my code, the http request is executed for the access token, the request is approved, but the result body is the body: {"error": "invalid_request"}. What could be the problem here?

var clientId = "";
var clientSecret = "";
var userId="a@google.ro"; 
var pass=""; 

function getNetatmoData(callback, cardTitle){
    var sessionAttributes = {};

    var formUserPass = { client_id: clientId, 
    client_secret: clientSecret, 
    username: userId, 
    password: pass, 
    scope: 'read_station', 
    grant_type: 'password' };

    shouldEndSession = false;
    cardTitle = "Welcome";
    speechOutput =""; 
    repromptText ="";

    var options = {
        host: 'api.netatmo.net',
        path: '/oauth2/token',
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'client_id': clientId,
            'client_secret': clientSecret,
            'username': userId, 
            'password': pass, 
            'scope': 'read_station', 
            'grant_type': 'password'
        }
    };
    var req = http.request(options, function(res) {
            res.setEncoding('utf8');
            res.on('data', function (chunk) {
                console.log("body: " + chunk);

            });

            res.on('error', function (chunk) {
                console.log('Error: '+chunk);
            });

            res.on('end', function() {

                speechOutput = "Request successfuly processed."
                console.log(speechOutput);
                repromptText = ""
                callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
            });

        });

        req.on('error', function(e){console.log('error: '+e)});

        req.end();
}
+4
source share
1 answer

! :

  • Amazon AWS. ( , AWS 700 ), , . 1-3 .

  • - Amazon (AWS). .

:

/**
*   Author: Mihai GALOS
*   Timestamp: 17:17:00, November 1st 2015  
*/

var http = require('https'); 
var https = require('https');
var querystring = require('querystring');

var clientId = ''; // create an application at https://dev.netatmo.com/ and fill in the generated clientId here
var clientSecret = ''; // fill in the client secret for the application
var userId= '' // your registration email address
var pass = '' // your account password


// 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) {
    try {
        console.log("event.session.application.applicationId=" + event.session.application.applicationId);

        /**
         * Uncomment this if statement and populate with your skill application ID to
         * prevent someone else from configuring a skill that sends requests to this function.
         */
        /*
        if (event.session.application.applicationId !== "amzn1.echo-sdk-ams.app.[unique-value-here]") {
             context.fail("Invalid Application ID");
         }
        */

        if (event.session.new) {
            onSessionStarted({requestId: event.request.requestId}, event.session);
        }

        if (event.request.type === "LaunchRequest") {
            onLaunch(event.request,
                     event.session,
                     function callback(sessionAttributes, speechletResponse) {
                        context.succeed(buildResponse(sessionAttributes, speechletResponse));
                     });
        }  else 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);
    }
};


function onSessionStarted(sessionStartedRequest, session) {
    console.log("onSessionStarted requestId=" + sessionStartedRequest.requestId +
            ", sessionId=" + session.sessionId);
}


function onLaunch(launchRequest, session, callback) {
    console.log("onLaunch requestId=" + launchRequest.requestId +
            ", sessionId=" + session.sessionId);

    // Dispatch to your skill launch.

    getData(callback);

}


function onIntent(intentRequest, session, callback) {
    console.log("onIntent requestId=" + intentRequest.requestId +
            ", sessionId=" + session.sessionId);

    var intent = intentRequest.intent,
        intentName = intentRequest.intent.name;
    var intentSlots ;

    console.log("intentRequest: "+ intentRequest);  
    if (typeof intentRequest.intent.slots !== 'undefined') {
        intentSlots = intentRequest.intent.slots;
    }


     getData(callback,intentName, intentSlots);


}


function onSessionEnded(sessionEndedRequest, session) {
    console.log("onSessionEnded requestId=" + sessionEndedRequest.requestId +
            ", sessionId=" + session.sessionId);
    // Add cleanup logic here
}

// --------------- Functions that control the skill behavior -----------------------

function doCall(payload, options, onResponse,
            callback, intentName, intentSlots){
    var response = ''
    var req = https.request(options, function(res) {
            res.setEncoding('utf8');

             console.log("statusCode: ", res.statusCode);
             console.log("headers: ", res.headers);


            res.on('data', function (chunk) {
                console.log("body: " + chunk);
                response += chunk;
            });

            res.on('error', function (chunk) {
                console.log('Error: '+chunk);
            });

            res.on('end', function() {
                var parsedResponse= JSON.parse(response);
                if (typeof onResponse !== 'undefined') {
                    onResponse(parsedResponse, callback, intentName, intentSlots);
                }
            });

        });

        req.on('error', function(e){console.log('error: '+e)});
        req.write(payload);

        req.end();

}

function getData(callback, intentName, intentSlots){



        console.log("sending request to netatmo...")

        var payload = querystring.stringify({
            'grant_type'    : 'password',
            'client_id'     : clientId,
            'client_secret' : clientSecret,
            'username'      : userId,
            'password'      : pass,
            'scope'         : 'read_station'
      });

        var options = {
            host: 'api.netatmo.net',
            path: '/oauth2/token',
            method: 'POST',
           headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': Buffer.byteLength(payload)
            }

        };

        //console.log('making request with data: ',options);

        // get token and set callbackmethod to get measure 
        doCall(payload, options, onReceivedTokenResponse, callback, intentName, intentSlots);
}

function onReceivedTokenResponse(parsedResponse, callback, intentName, intentSlots){

        var payload = querystring.stringify({
            'access_token'  : parsedResponse.access_token
      });

        var options = {
            host: 'api.netatmo.net',
            path: '/api/devicelist',
            method: 'POST',
           headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': Buffer.byteLength(payload)
            }

        };

    doCall(payload, options, getMeasure, callback, intentName, intentSlots);

}

function getMeasure(parsedResponse, callback, intentName, intentSlots){


         var data = {
                tempOut         : parsedResponse.body.modules[0].dashboard_data.Temperature,
                humOut          : parsedResponse.body.modules[0].dashboard_data.Humidity,
                rfStrengthOut   : parsedResponse.body.modules[0].rf_status,
                batteryOut      : parsedResponse.body.modules[0].battery_vp,

                tempIn      : parsedResponse.body.devices[0].dashboard_data.Temperature,
                humIn       : parsedResponse.body.devices[0].dashboard_data.Humidity,
                co2         : parsedResponse.body.devices[0].dashboard_data.CO2,
                press       : parsedResponse.body.devices[0].dashboard_data.Pressure,

                tempBedroom         : parsedResponse.body.modules[2].dashboard_data.Temperature,
                humBedroom          : parsedResponse.body.modules[2].dashboard_data.Temperature,
                co2Bedroom          : parsedResponse.body.modules[2].dashboard_data.CO2,
                rfStrengthBedroom   : parsedResponse.body.modules[2].rf_status,
                batteryBedroom      : parsedResponse.body.modules[2].battery_vp,

                rainGauge           : parsedResponse.body.modules[1].dashboard_data,
                rainGaugeBattery    : parsedResponse.body.modules[1].battery_vp
               };

    var repromptText = null;
    var sessionAttributes = {};
    var shouldEndSession = true;
    var speechOutput ;

    if( "AskTemperature" === intentName)  {

        console.log("Intent: AskTemperature, Slot:"+intentSlots.Location.value);

        if("bedroom" ===intentSlots.Location.value){
            speechOutput = "There are "+data.tempBedroom+" degrees in the bedroom.";

        }
        else if ("defaultall" === intentSlots.Location.value){
            speechOutput = "There are "+data.tempIn+" degrees inside and "+data.tempOut+" outside.";
        }

        if(data.rainGauge.Rain > 0) speechOutput += "It is raining.";
    } else if ("AskRain" === intentName){
        speechOutput = "It is currently ";
        if(data.rainGauge.Rain > 0) speechOutput += "raining.";
        else speechOutput += "not raining. ";

        speechOutput += "Last hour it has rained "+data.rainGauge.sum_rain_1+" millimeters, "+data.rainGauge.sum_rain_1+" in total today.";
    } else { // AskTemperature
        speechOutput = "Ok. There are "+data.tempIn+" degrees inside and "+data.tempOut+" outside.";

        if(data.rainGauge.Rain > 0) speechOutput += "It is raining.";
    }

        callback(sessionAttributes,
             buildSpeechletResponse("", speechOutput, repromptText, shouldEndSession));

}

// --------------- Helpers that build all of the responses -----------------------

function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
    return {
        outputSpeech: {
            type: "PlainText",
            text: output
        },
        card: {
            type: "Simple",
            title: "SessionSpeechlet - " + title,
            content: "SessionSpeechlet - " + output
        },
        reprompt: {
            outputSpeech: {
                type: "PlainText",
                text: repromptText
            }
        },
        shouldEndSession: shouldEndSession
    };
}

function buildResponse(sessionAttributes, speechletResponse) {
    return {
        version: "1.0",
        sessionAttributes: sessionAttributes,
        response: speechletResponse
    };
}
  1. netatmo (https://dev.netatmo.com/) . Netatmo. (.. 5653769769f7411515036a0b) (.. T4nHevTcRbs053TZsoLZiH1AFKLZGb83Fmw9q). (, , )

  2. ( netatmo, ) .

  3. Amazon (https://developer.amazon.com/edw/home.html). Alexa, Alexa Skills Kit ( "" )

  4. . . ( ) . ARN -, . -, , . - : arn: aws: lambda: us-east-1: 255569121831: function: [ ]. , , , ( ).

  5. . . -, . ; ( ):

        {
    "intents": 
        [
            {
                "intent": "AskTemperature",
                "slots": [
                        {
                        "name": "Location",
                        "type": "LIST_OF_LOCATIONS"
                        }
                ]
            },
    
            {
                "intent": "AskCarbonDioxide",
                "slots": [
                        {
                        "name": "Location",
                        "type": "LIST_OF_LOCATIONS"
                        }
                ]
            },
             {
                "intent": "AskHumidity",
                "slots": [
                        {
                        "name": "Location",
                        "type": "LIST_OF_LOCATIONS"
                        }
                ]
            },
    
            {
                "intent": "AskRain",
                "slots": []
            },
    
            {
                "intent": "AskSound",
                "slots": []
            },
            {
                "intent": "AskWind",
                "slots": []
            },
    
            {
                "intent": "AskPressure",
                "slots": []
            }
    
    
        ]
    }
    

, . " ".

    LIST_OF_LOCATIONS and newline-separated : DefaultAll, Inside, Outside, Living, Bedroom, Kitchen, Bathroom, Alpha, Beta 

( )

, :

    AskTemperature what the temperature {Location}
    AskTemperature what the temperature in {Location}
    AskTemperature what the temperature in the {Location}
    AskTemperature get the temperature {Location}
    AskTemperature get the temperature in {Location}
    AskTemperature get the temperature in the {Location}

    AskCarbonDioxide what the comfort level {Location}
    AskCarbonDioxide what the comfort level in {Location}
    AskCarbonDioxide what the comfort level in the {Location}

    AskCarbonDioxide get the comfort level {Location}
    AskCarbonDioxide get the comfort level in {Location}
    AskCarbonDioxide get the comfort level in the {Location}


    AskHumidity what the humidity {Location}
    AskHumidity what the humidity in {Location}
    AskHumidity what the humidity in the {Location}
    AskHumidity get the humidity {Location}
    AskHumidity get the humidity from {Location}
    AskHumidity get the humidity in {Location}
    AskHumidity get the humidity in the {Location}
    AskHumidity get humidity


    AskRain is it raining 
    AskRain did it rain
    AskRain did it rain today
    AskRain get rain millimeter count
    AskRain get rain

    AskSound get sound level
    AskSound tell me how loud it is

    AskWind is it windy 
    AskWind get wind
    AskWind get wind measures
    AskWind get direction
    AskWind get speed

    AskPressure get pressure
    AskPressure what the pressure
  1. , ​​, , . .:)

  2. . . http://alexa.amazon.com/ . "".

  3. . "Alexa, [ ]". netatmo . "Alexa, [ ] ". , " []" uttereces, .

, . , / - -.:)

+8

Source: https://habr.com/ru/post/1616972/


All Articles