上一章介紹了開發所需的基本軟硬件,從這一章開始介紹每一步的過程。
第2章、如何建立 Lambda 功能
1) 打開 aws.amazon.com, 登入到控制臺 (賬號區域需要選擇US East (N. Virginia) 才可以正常使用)
2) 選擇 Lambda
3) 點擊 Create Lambda function
4) 輸入篩選字符 smart , 點擊 alexa-smart-home-skill-adapter
5) 配置觸發器, 這一欄填 alexa skill 的 application id, 但目前我們還沒有建好 alexa skill , 所以先隨便輸入 test001,直接點 Next.
6) 配置 Lambda 服務, 輸入名稱, Rutime 欄位選 Node.js 4.3
7) 將以下代碼填入 CODE 區域, 用來提供基礎框架(發現設備、控制設備的命令清單)和 2個燈泡名稱 Bedroom light 和 Kitchen light,
語言類別:NodeJS
var https = require('https');
var kitchenLightApplianceId = "A146-3456-b31d-8ec4c146c5ea";
var bedroomLightApplianceId = "A146-3456-b31d-8ec4c146c5eb";
var particleServer = "api.particle.io";
var particlePath = "/v1/devices/";
/**
* Main entry point.
* Incoming events from Alexa Lighting APIs are processed via this method.
*/
exports.handler = function(event, context) {
log('Input', event);
switch (event.header.namespace) {
/**
* The namespace of "Discovery" indicates a request is being made to the lambda for
* discovering all appliances associated with the customer's appliance cloud account.
* can use the accessToken that is made available as part of the payload to determine
* the customer.
*/
case 'Alexa.ConnectedHome.Discovery':
handleDiscovery(event, context);
break;
/**
* The namespace of "Control" indicates a request is being made to us to turn a
* given device on, off or brighten. This message comes with the "appliance"
* parameter which indicates the appliance that needs to be acted on.
*/
case 'Alexa.ConnectedHome.Control':
handleControl(event, context);
break;
/**
* We received an unexpected message
*/
default:
log('Err', 'No supported namespace: ' + event.header.namespace);
context.fail('Something went wrong');
break;
}
};
/**
* This method is invoked when we receive a "Discovery" message from Alexa Connected Home Skill.
* We are expected to respond back with a list of appliances that we have discovered for a given
* customer.
*/
function handleDiscovery(accessToken, context) {
/**
* Crafting the response header
*/
var headers = {
namespace: 'Alexa.ConnectedHome.Discovery',
name: 'DiscoverAppliancesResponse',
payloadVersion: '2'
};
/**
* Response body will be an array of discovered devices.
*/
var appliances = [];
var kitchenLight = {
applianceId: kitchenLightApplianceId,
manufacturerName: 'KRV',
modelName: 'ParticleLight',
version: 'VER01',
friendlyName: 'Kitchen Light',
friendlyDescription: 'Particle light in kitchen',
isReachable: true,
actions:[
"incrementPercentage",
"decrementPercentage",
"setPercentage",
"turnOn",
"turnOff"
],
additionalApplianceDetails: {
/**
* OPTIONAL:
* We can use this to persist any appliance specific metadata.
* This information will be returned back to the driver when user requests
* action on this appliance.
*/
fullApplianceId: '2cd6b650-c0h0-4062-b31d-7ec2c146c5ea',
deviceId: "39003d000447343232363230"
}
};
var bedroomLight = {
applianceId: bedroomLightApplianceId,
manufacturerName: 'KRV',
modelName: 'ParticleLight',
version: 'VER01',
friendlyName: 'Bedroom Light',
friendlyDescription: 'Particle light in bedroom',
isReachable: true,
actions:[
"incrementPercentage",
"decrementPercentage",
"setPercentage",
"turnOn",
"turnOff"
],
additionalApplianceDetails: {
/**
* OPTIONAL:
* We can use this to persist any appliance specific metadata.
* This information will be returned back to the driver when user requests
* action on this appliance.
*/
fullApplianceId: '2cd6b650-c0h0-4062-b31d-7ec2c146c5eb',
deviceId: "39003d000447343232363230"
}
};
appliances.push(kitchenLight);
appliances.push(bedroomLight);
/**
* Craft the final response back to Alexa Connected Home Skill. This will include all the
* discoverd appliances.
*/
var payloads = {
discoveredAppliances: appliances
};
var result = {
header: headers,
payload: payloads
};
log('Discovery', result);
context.succeed(result);
}
/**
* Control events are processed here.
* This is called when Alexa requests an action (IE turn off appliance).
*/
function handleControl(event, context) {
if (event.header.namespace === 'Alexa.ConnectedHome.Control') {
/**
* Retrieve the appliance id and accessToken from the incoming message.
*/
var accessToken = event.payload.accessToken;
var applianceId = event.payload.appliance.applianceId;
var deviceid = event.payload.appliance.additionalApplianceDetails.deviceId;
var message_id = event.header.messageId;
var param = "";
var index = "0";
var state = 0;
var confirmation;
var funcName;
log("Access Token: ", accessToken);
log("DeviceID: ", deviceid);
if(event.header.name == "TurnOnRequest"){
state = 1;
confirmation = "TurnOnConfirmation";
funcName = "onoff";
}
else if(event.header.name == "TurnOffRequest"){
state = 0;
confirmation = "TurnOffConfirmation";
funcName = "onoff";
}
else if(event.header.name == "SetPercentageRequest"){
state = event.payload.percentageState.value;
confirmation = "SetPercentageConfirmation";
funcName = "setvalue";
}
else if(event.header.name == "IncrementPercentageRequest"){
var increment = event.payload.deltaPercentage.value;
state += increment;
if(state > 100){
state = 100;
}
confirmation = "IncrementPercentageConfirmation";
funcName = "setvalue";
}
else if(event.header.name == "DecrementPercentageRequest"){
var decrement = event.payload.deltaPercentage.value;
state -= decrement;
if(state < 0){
state = 0;
}
confirmation = "DecrementPercentageConfirmation";
funcName = "setvalue";
}
log('applianceId', applianceId);
if(applianceId == kitchenLightApplianceId){
index = "0";
}
else if(applianceId == bedroomLightApplianceId){
index = "1";
}
param = index + "=" + state;
var options = {
hostname: particleServer,
port: 443,
path: particlePath + deviceid + "/" + funcName,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
log(options);
accessToken = "35311bb780d44f27724e58d66353e3c1d1f9c7e4";
var data = "access_token=" + accessToken + "&" + "args=" + param;
log(data);
var serverError = function (e) {
log('Error', e.message);
context.fail(generateControlError('TurnOnRequest', 'DEPENDENT_SERVICE_UNAVAILABLE', 'Unable to connect to server'));
};
var callback = function(response) {
var str = '';
response.on('data', function(chunk) {
str += chunk.toString('utf-8');
});
response.on('end', function() {
log('Return Value');
log(str);
var headers = {
namespace: 'Alexa.ConnectedHome.Control',
name: confirmation,
payloadVersion: '2',
messageId: message_id
};
var payloads = {
};
var result = {
header: headers,
payload: payloads
};
context.succeed(result);
});
response.on('error', serverError);
};
var req = https.request(options, callback);
req.on('error', serverError);
req.write(data);
req.end();
}
}
/**
* Utility functions.
*/
function log(title, msg) {
console.log(title + ": " + msg);
}
function generateControlError(name, code, description) {
var headers = {
namespace: 'Control',
name: name,
payloadVersion: '1'
};
var payload = {
exception: {
code: code,
description: description
}
};
var result = {
header: headers,
payload: payload
};
return result;
}
8) Role 選擇 lambda_basic_execution, 高級設置不做修改, 點擊 Next
9) 點擊 Create function
10) 模擬測試, 點擊 Test
11)Sample event template 選擇 Alexa Smart Home - Control, 將 "namespace" 的值改為 Alexa.ConnectedHome.Control
以下畫面表示 Alexa.ConnectedHome.Control 輸入測試OK, 當然可也可以測試 Alexa.ConnectedHome.Discovery, (通過修改 "namespace" 的值)
建立完成后, 在Lambda function 列表中可以看到
到此, Lambda 功能建立完成, 其中的觸發器我們暫時命名為 test001, 要等建立好 alexa skill 后重新進行綁定.
另外需要保存 ARN 的值,等到建立 alexa skill 的時候要用到.