[下面回答问题中的完整教程。欢迎反馈!】
我正在尝试创建一个 AWS Lambda 函数,用于 Amazon Alexa 技能从我的 Netatmo 气象站获取天气信息。基本上,我需要通过 http 请求连接到 Netatmo 云。
这是我的代码片段,http 请求是针对临时访问 token 完成的,请求没问题,但结果正文是 body: {"error":"invalid_request"}。这里可能有什么问题?
var clientId = "";
var clientSecret = "";
var userId="[email protected]";
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();
}
最佳答案
我让它运行了!
这是一个快速演练:
这是技能的代码:
/**
* 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
};
}
首先,意图模式。这是我的;复制粘贴此代码(并根据需要进行修改):
{
"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'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
好吧,抱歉这篇长文章。我希望这个小教程/演练有一天会对某人有所帮助。 :)
关于javascript - 将 Netatmo 气象站连接到 Amazon Echo (Alexa),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33859826/