'use strict';
var AWS = require('aws-sdk'); // AWS SDKを使用
AWS.config.update({region: "us-west-2"});
// IRKitデバイス情報
const IRKit_DEVICE = process.env.My_IRKit_NAME;
const clientkey = process.env.My_ClientKey;
const deviceid = process.env.My_DeviceID;
const IRKit_Light_ID = IRKit_DEVICE + '-light-' + deviceid; // 照明デバイスの識別子
// エラー情報
const sendError = Error("Send error"); // 赤外線信号の送信が失敗
const internalError = Error("Internal error"); // 内部エラー
// エラーメッセージ
const InvDeviceMsg = "そのようなデバイスは登録されていません。";
const InvValueMsg = "指定範囲外の値のため、設定できません。";
const UnSupportMsg = "その操作には対応していません。";
// リモコン信号
const LightIR = {
on: { "format":"raw",
"freq":38,
"data":[18031,8755,1190,1037,1190,3228,1190,1037,1190,1037,1190,1037,
1190,1037,1190,1037,1190,3228,1190,3228,1190,1037,1190,3228,
1190,3228,1190,1037,1190,3228,1190,3228,1190,1037,1190,3228,
1190,3228,1190,3228,1190,3228,1190,3228,1190,1037,1190,1037,
1190,1037,1190,1037,1190,1037,1190,1037,1190,1037,1190,1037,
1190,3228,1190,3228,1190,3228,1190,65535,0,18031,18031,4400,
1190,65535,0,65535,0,60108,17421,4400,1190]
},
off: {"format":"raw",
"freq":38,
"data":[18031,8755,1190,1037,1190,3228,1190,1037,1190,1037,1190,1037,
1190,1037,1190,1037,1190,3228,1190,3228,1190,1037,1190,3228,
1190,3228,1190,1037,1190,3228,1190,3228,1190,1037,1190,1037,
1190,3228,1190,3228,1190,3228,1190,3228,1190,1037,1190,1037,
1190,1037,1190,3228,1190,1037,1190,1037,1190,1037,1190,1037,
1190,3228,1190,3228,1190,3228,1190,65535,0,18031,18031,4400,
1190,65535,0,65535,0,60108,17421,4400,1190]
},
dim: {"format":"raw",
"freq":38,
"data":[18031,8755,1190,1037,1190,3228,1190,1037,1190,1037,1190,1037,
1190,1037,1190,1037,1190,3228,1190,3228,1190,1037,1190,3228,
1190,3228,1190,1037,1190,3228,1190,3228,1190,1037,1190,1037,
1190,3228,1190,3228,1190,1037,1190,3228,1190,1037,1190,1037,
1190,1037,1190,3228,1190,1037,1150,1037,1150,3228,1190,1037,
1150,3228,1190,3228,1190,3228,1190,65535,0,18031,18031,4400,
1190,65535,0,65535,0,60108,17421,4400,1190]
}
};
// リモコン信号の送信
function sendIR(ir) {
var deferred = Promise.defer(); // Deferredオブジェクトを作る
var https = require("https");
var qs = require("querystring");
var postData = qs.stringify({
clientkey: clientkey,
deviceid: deviceid,
message: JSON.stringify(ir)
});
var req = https.request({
host: "api.getirkit.com",
path: "/1/messages",
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"Content-Length": postData.length
}
}, (res) => {
var result = res.statusCode;
if (result == 200) {
console.log("res.statusCode=" + result); // ログ出力
deferred.resolve(null, "success"); // 送信成功
}
else {
console.log(postData); // ログ出力
console.log("res.statusCode=" + result); // ログ出力
deferred.reject(sendError); // 送信エラーを通知
}
});
req.write(postData);
req.end();
return deferred.promise; // Promiseを返す
}
exports.handler = function (request, context, callback) {
var header = request.directive.header;
var req_name = header.name;
switch (header.namespace) {
case 'Alexa.Discovery':
if (req_name === 'Discover') {
log("DEGUG: ", "Discover request", JSON.stringify(request));
handleDiscovery(request, context, callback);
}
break;
case 'Alexa.PowerController':
if (req_name === 'TurnOn' || req_name === 'TurnOff') {
log("DEBUG: ", "TurnOn or TurnOff Request", JSON.stringify(request));
handlePowerControl(request, context, callback);
}
break;
case 'Alexa.BrightnessController':
if (req_name === 'AdjustBrightness' || req_name === 'SetBrightness') {
log("DEBUG: ", "Brightness Request", JSON.stringify(request));
handleBrightnessControl(request, context, callback);
}
break;
case 'Alexa':
if (req_name === 'ReportState') {
log("DEBUG: ", "Report State", JSON.stringify(request));
handleState(request, callback);
}
break;
default: {
// サポートしていない操作
log("DEBUG: ", "Unsupported request", JSON.stringify(request));
callback(null, generateNotSupportResponse(request));
}
}
// 照明デバイスの検出
function handleDiscovery(request, context, callback) {
var payload = {
"endpoints":
[
{
"endpointId": IRKit_Light_ID,
"manufacturerName": "IRKit",
"friendlyName": "リビングの照明", // このデバイス名で検出されます
"description": "リビングの照明をIRKitで操作できます。",
"displayCategories": ["LIGHT"], // 照明器具
"cookie": {},
"capabilities":
[
{
"type": "AlexaInterface",
"interface": "Alexa",
"version": "3"
},
{
"type": "AlexaInterface",
"interface": "Alexa.PowerController",
"version": "3",
"properties": {
"supported": [{
"name": "powerState" // 電源のオン・オフ
}],
"retrievable": true
}
},
{
"type": "AlexaInterface",
"interface": "Alexa.BrightnessController",
"version": "3",
"properties": {
"supported": [{
"name": "brightness" // 輝度
}],
"retrievable": true
}
},
{
"type": "AlexaInterface",
"interface": "Alexa.EndpointHealth",
"version": "3",
"properties":{
"supported":[{
"name":"connectivity" // 動作状態
}],
"retrievable": true
}
}
]
}
]
};
var header = request.directive.header;
header.name = "Discover.Response";
log("DEBUG: ", "handleDiscovery", JSON.stringify({ header: header, payload: payload }));
callback(null, { event: { header: header, payload: payload } });
}
// 照明の電源操作
function handlePowerControl(request, context, callback) {
var requestMethod = request.directive.header.name;
var endpoint = request.directive.endpoint;
var deviceId = endpoint.endpointId; // 電源操作対象のデバイス識別子
var powerState, brightness;
var ir;
var response;
if (requestMethod === "TurnOn") {
log("DEBUG: ", "handlePowerControl: ", "Light on");
powerState = "ON";
brightness = 100;
ir = LightIR.on;
}
else if (requestMethod === "TurnOff") {
log("DEBUG: ", "handlePowerControl: ", "Light off");
powerState = "OFF";
brightness = 0;
ir = LightIR.off;
}
if (deviceId != IRKit_Light_ID) {
// 無効なデバイス
response = generateInvDevResponse(request);
log("DEBUG: ", "Invalid deviceId:", JSON.stringify(response));
callback(null, response);
}
response = generatePowerResponse(request, powerState);
sendIR(ir)
.then(writeDynamoDBItem(powerState, brightness)) // DBに書き込む
.then(() => {
//log("DEBUG: ", "handlePowerControl", JSON.stringify(response));
callback(null, response);
})
.catch((error) => {
callback(error); // 送信、DB書き込みが失敗
});
}
// 照明の輝度操作
function handleBrightnessControl(request, context, callback) {
var requestMethod = request.directive.header.name;
var endpoint = request.directive.endpoint;
var payload = request.directive.payload;
var deviceId = endpoint.endpointId; // 輝度操作対象のデバイス識別子
var ir = LightIR.dim;
var response;
readDynamoDBItem()
.then((data) => {
var powerState = "ON";
var brightnessResult = data.Item.brightness;
if (requestMethod === "SetBrightness") {
log("DEBUG: ", "handleBrightnessControl: ", "Set Brightness");
let val = parseInt(payload.brightness, 10);
if (val > 100 || val < 0) {
callback(null,generateInvValueResponse(request));
}
brightnessResult = val;
}
else if (requestMethod === "AdjustBrightness") {
log("DEBUG: ", "handleBrightnessControl: ", "Adjust Brightness");
let val = parseInt(payload.brightnessDelta, 10);
if (val > 100 || val < -100) {
callback(null,generateInvValueResponse(request));
}
brightnessResult += val;
}
if (brightnessResult >= 100) {
brightnessResult = 100;
ir = LightIR.on;
}
else if (brightnessResult <= 0) {
powerState = "OFF";
brightnessResult = 0;
ir = LightIR.off;
}
if (deviceId == IRKit_Light_ID) {
response = generateBrightnessResponse(request, brightnessResult);
}
else {
// 無効なデバイス
response = generateInvDevResponse(request);
log("DEBUG: ", "Invalid deviceId", JSON.stringify(response));
callback(null, response);
}
sendIR(ir)
.then(writeDynamoDBItem(powerState, brightnessResult)) // DBに書き込む
.then(() => {
//log("DEBUG: ", "handleBrightnessControl", JSON.stringify(response));
callback(null, response);
})
.catch((error) => {
callback(error); // 送信、DB書き込みが失敗
});
})
.catch((error) => {
// DynamoDBからの読み出しが失敗
callback(error);
});
}
// 照明の状態(DynamoDBに保存されている値で代替)レスポンスを返却する
function handleState(request, callback) {
readDynamoDBItem()
.then((data) => {
let res = generateStateResponse(request,
data.Item.powerState,
data.Item.brightness);
//log("DEBUG: ", "handleState", JSON.stringify(res));
callback(null, res);
})
.catch((error) => {
// DynamoDBからの読み出しが失敗
callback(error);
});
}
};
// 存在していないエンドポイント(デバイス)へのディレクティブ
function generateInvDevResponse(request) {
return generateErrorResponse(request, "NO_SUCH_ENDPOINT", InvDeviceMsg);
}
// 無効な値を設定しようとするディレクティブ
function generateInvValueResponse(request) {
return generateErrorResponse(request, "INVALID_VALUE", InvValueMsg);
}
// サポートしていないディレクティブ
function generateNotSupportResponse(request) {
return generateErrorResponse(request, "INVALID_DIRECTIVE", UnSupportMsg);
}
// エラー応答(JSON)を生成する
function generateErrorResponse(request, error_type, message) {
var responseHeader = request.directive.header;
var endpoint = request.directive.endpoint;
var deviceId = endpoint.endpointId; // デバイス識別子
var requestToken = endpoint.scope.token;
responseHeader.namespace = "Alexa";
responseHeader.name = "ErrorResponse";
responseHeader.messageId = responseHeader.messageId + "-R";
return {
event: {
header: responseHeader,
endpoint: {
"scope":{
"type": "BearerToken",
"token": requestToken
},
"endpointId": deviceId,
},
payload: {
"type": error_type,
"message": message
}
}
};
}
// 電源操作の応答(JSON)を生成する
function generatePowerResponse(request, powerResult) {
var now = new Date();
var contextResult = {
"properties": [{
"namespace": "Alexa.PowerController",
"name": "powerState",
"value": powerResult,
"timeOfSample": now,
"uncertaintyInMilliseconds": 0
}]
};
return generateResonse(request, "Response", contextResult);
}
// 輝度調整の応答(JSON)を生成する
function generateBrightnessResponse(request, brightnessResult) {
var now = new Date();
var contextResult = {
"properties": [{
"namespace": "Alexa.BrightnessController",
"name": "brightness",
"value": brightnessResult,
"timeOfSample": now,
"uncertaintyInMilliseconds": 0
}]
};
return generateResonse(request, "Response", contextResult);
}
// 照明の状態の応答(JSON)を生成する
function generateStateResponse(request, powerState, brightness) {
var now = new Date();
var contextResult = {
"properties": [
{
"namespace": "Alexa.PowerController",
"name": "powerState",
"value": powerState,
"timeOfSample": now,
"uncertaintyInMilliseconds": 0
},
{
"namespace": "Alexa.BrightnessController",
"name": "brightness",
"value": brightness,
"timeOfSample": now,
"uncertaintyInMilliseconds": 0
},
{
"namespace": "Alexa.EndpointHealth",
"name": "connectivity",
"value": {"value": "OK"},
"timeOfSample": now,
"uncertaintyInMilliseconds": 0
}
]
};
return generateResonse(request, "StateReport", contextResult);
}
function generateResonse(request, responseName, contextResult) {
var responseHeader = request.directive.header;
var endpoint = request.directive.endpoint;
var deviceId = endpoint.endpointId; // デバイス識別子
responseHeader.namespace = "Alexa";
responseHeader.name = responseName;
responseHeader.messageId = responseHeader.messageId + "-R";
return {
context: contextResult,
event: {
header: responseHeader,
endpoint: {
"endpointId": deviceId,
},
payload: {}
}
};
}
// DynamoDBのSmartIRKitテーブルの、id:'LivingLight'項目を読み出す
function readDynamoDBItem() {
var docClient = new AWS.DynamoDB.DocumentClient();
const params = {
TableName: 'SmartIRKit', // DynamoDBテーブル名
Key:{ 'id': 'LivingLight' } // Primary Key
};
var deferred = Promise.defer(); // Deferredオブジェクトを作る
docClient.get(params, (err, data) => {
if (err) {
log("DEBUG: ", "Unable to read item. Error JSON:", JSON.stringify(err, null, 2));
deferred.reject(null);
}
log("DEBUG: ", "GetItem succeeded:", JSON.stringify(data));
deferred.resolve(data);
});
return deferred.promise; // Promiseを返す
}
// DynamoDBのSmartIRKitテーブルの、id:'LivingLight'項目を書き込む
function writeDynamoDBItem(powerState, brightness) {
var docClient = new AWS.DynamoDB.DocumentClient();
var writeData = {
TableName: 'SmartIRKit', // DynamoDBテーブル名
Item:{
'id': 'LivingLight',
'powerState': powerState,
'brightness': brightness
}
};
var deferred = Promise.defer(); // Deferredオブジェクトを作る
docClient.put(writeData, (err) => {
if (err) {
log("DEBUG: ", "Unable to write item. Error JSON:", JSON.stringify(err, null, 2));
deferred.reject(err);
}
log("DEBUG: ", "PutItem succeeded:", JSON.stringify(writeData));
deferred.resolve(null);
});
return deferred.promise; // Promiseを返す
}
function log(message, message1, message2) {
console.log(message + message1 + message2);
}
|