Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/404.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 将Netatmo气象站链接到Amazon Echo(Alexa)_Javascript_Amazon Web Services_Aws Lambda_Alexa Skill_Alexa Skills Kit - Fatal编程技术网

Javascript 将Netatmo气象站链接到Amazon Echo(Alexa)

Javascript 将Netatmo气象站链接到Amazon Echo(Alexa),javascript,amazon-web-services,aws-lambda,alexa-skill,alexa-skills-kit,Javascript,Amazon Web Services,Aws Lambda,Alexa Skill,Alexa Skills Kit,[下面回答问题的完整教程。欢迎反馈!] 我正在尝试创建一个AWS Lambda函数,用于Amazon Alexa技能从我的Netatmo气象站获取天气信息。基本上,我需要通过http请求连接到Netatmo云 这是我的一段代码,http请求是针对临时访问令牌完成的,请求是ok的,但结果体是body:{“error”:“invalid_request”}。这里有什么问题 var clientId = ""; var clientSecret = ""; var userId="a@google.r

[下面回答问题的完整教程。欢迎反馈!]

我正在尝试创建一个AWS Lambda函数,用于Amazon Alexa技能从我的Netatmo气象站获取天气信息。基本上,我需要通过http请求连接到Netatmo云

这是我的一段代码,http请求是针对临时访问令牌完成的,请求是ok的,但结果体是body:{“error”:“invalid_request”}。这里有什么问题

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();
}
我让它跑起来了! 下面是一个快速演练:

  • 获得亚马逊AWS的免费帐户。只要你的技能没有持续运行(你将按照AWS服务器上使用的运行时间和资源计费,每月大约700?免费小时),你就应该是优秀的,它将保持免费。该技能每次需要1-3秒的时间运行

  • 在AmazonWebServices(AWS)中设置一个新的lambda函数。每次调用该技能时,此函数都将执行

  • 以下是技能代码:

    /**
    *   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's 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's 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's 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
        };
    }
    
  • 转到netatmo的开发者站点()并创建一个新的应用程序。这将是您与Netatmo端传感器数据的接口。应用程序将具有唯一的id(即5653769769F711515036A0B)和客户机密(即T4nHevTcRbs053TZsoLZiH1AFKLZGb83Fmw9q)。(否,这些数字不代表有效的客户端id和密码,它们仅用于演示)

  • 在上面的代码中填写所需的凭据(netatmo帐户用户和密码、客户端id和密码)

  • 转到Amazon应用程序和服务()。在菜单中,选择Alexa,然后选择Alexa技能工具包(单击开始)

  • 现在你需要创造一种新的技能。给你的技能起个名字和召唤。该名称将用于调用(或启动)应用程序。在端点字段中,您需要输入前面创建的lambda函数的ARN id。这个数字可以在右上角显示lambda函数的网页上找到。它应该类似于:arn:aws:lambda:us-east-1:255569121831:function:[您的函数名]。完成此步骤后,左侧将显示一个绿色复选标记以指示进度(进度菜单)

  • 下一阶段涉及建立交互模型。它负责将话语映射到意图和时隙。 首先,意图模式。这是我的;复制粘贴此代码(并根据需要进行修改):

  • 接下来是自定义插槽类型。单击添加插槽类型。给插槽起个名字

        LIST_OF_LOCATIONS and newline-separated : DefaultAll, Inside, Outside, Living, Bedroom, Kitchen, Bathroom, Alpha, Beta 
    
    (用换行符替换逗号)

    接下来,示例话语:

        AskTemperature what's the temperature {Location}
        AskTemperature what's the temperature in {Location}
        AskTemperature what's 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's the comfort level {Location}
        AskCarbonDioxide what's the comfort level in {Location}
        AskCarbonDioxide what's 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's the humidity {Location}
        AskHumidity what's the humidity in {Location}
        AskHumidity what's 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's the pressure
    
  • 测试、描述和发布信息可以留空,除非您计划将您的技能发送到amazon,以便公开使用。我把我的留白了。:)

  • 差不多了。你只需要启用新技能。转到并在左侧菜单中选择技能。找到您的技能并单击启用

  • 那一刻太棒了。说“Alexa,打开[你的技能名称]”。默认情况下,室内和室外温度应从netatmo云中获取,并由Alexa大声读出。你也可以说“Alexa,打开[你的技能名称],测量卧室的温度。”。正如您可能已经注意到的,部分“获取[Location]中的温度”对应于您之前填写的样本

  • 长命百岁


  • 嗯,很抱歉发了这么长的帖子。我希望这个小教程/演练有一天会对某些人有所帮助。:)

    您能从其他系统测试API调用吗(例如直接从您的计算机,而不是通过Lambda)?发送相同的
    正文
    ,看看是否有效。我可以通过使用并填写必要的参数来确认凭据是否正确。请求生成所需的令牌…是否编码错误?它可能会澄清为什么响应没有其他信息——因为原始请求甚至无法解析!谢谢你的教程。我在github上创建了一个德语版本:嗨,大卫。这是一个非常有趣的故事。丹麦和schöne Gruße,Mihai。嗨,Mihai,我已经在GitHub了,谢谢你。我不知道该怎么做。谢谢Umbennennen?嗨,大卫。大家好!丹克和快乐编码!:)我更新了doCall(),因为我有时会收到Alexa的“连接技能的响应有问题”。问题是,当响应太长时,它会被分成块。在json解析这些块之前,我们需要连接这些块,这在以前是不一样的。因此,对于单个不完整的块解析,解析将失败。
        AskTemperature what's the temperature {Location}
        AskTemperature what's the temperature in {Location}
        AskTemperature what's 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's the comfort level {Location}
        AskCarbonDioxide what's the comfort level in {Location}
        AskCarbonDioxide what's 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's the humidity {Location}
        AskHumidity what's the humidity in {Location}
        AskHumidity what's 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's the pressure