上一章介紹了開發所需的基本軟硬件,從這一章開始介紹每一步的過程。
第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 的時候要用到.