処理内容
・kintone で 命令を選択し、制御命令レコードを追加すると、MQTTS にコマンドを送信(Publish)します
・MQTTS からコマンドを受信 (Subscribe) した Raspberry Pi が、制御コマンドを受信し、命令の処理(計測・撮影)を行います。
・Raspberry Pi から API 経由で kintone のレコードにデータを PUT して更新します。
・kintone の詳細画面で計測・撮影結果が表示されます。(結果が戻るか、指定回数に到達するまでは ReLoad を繰り返す。)
Raspberry Pi 側の処理結果は https://qiita.com/yukataoka/items/b3a7e5e9dc7471b5b66d を参照
完成画面
命令を選択し「保存」します。(保管と同時に、制御指示をMQTTSに送信。)
遠隔地の Raspberry Pi が計測温湿度(3秒後)・撮影画像(8秒後)が kintone に保管され、表示されます。
設定画面
kintone の設定画面を開きます。
以下のように JavaScript のライブラリィを参照し、カスタマイズしたプログラムをアップロードします。
参照するライブラリィは以下
https://sdk.amazonaws.com/js/aws-sdk-2.2.37.min.js
https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.min.js
https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/core-min.js
https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/hmac-min.js
https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/sha256-min.js
https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js
ブラウザ内の AWS SDK for JavaScript
https://aws.amazon.com/jp/sdk-for-browser/
[JavaScript] moment.jsで日付時刻操作を行うTips
https://qiita.com/Sa2Knight/items/092a764679f960de81c9
crypto-js
https://github.com/brix/crypto-js
HTML5からMQTTに接続する方法
https://qiita.com/MahoTakara/items/8024f78d057d84cbc012
AWS IoTのMQTT over WebSocketにHTMLから接続してみた
https://dev.classmethod.jp/cloud/aws/aws-iot-mqtt-over-websocket/
kintone JavaScript コード
kintone の制御指示画面でレコードが追加された後、制御指示用の情報と、kintone の API 接続用の情報を MQTTS に送信しています。
kintone のレコード追加後に遷移した詳細画面で、Raspberry Pi から API 経由で更新されたセンサの情報や、カメラの画像を表示します。
(function() {
"use strict";
// レコード追加時イベント
kintone.events.on('mobile.app.record.create.show', function(event) {
alert("この機能をモバイルで使う時は、ブラウザのPC表示でご利用ください!");
window.history.back(-1);
});
// レコード追加後のイベント
var eventsCreateSuccess = ['app.record.create.submit.success'];
kintone.events.on(eventsCreateSuccess, function(event) {
var record = event.record;
// 計測・撮影した Raspberry から kintone へ Respons を送信するための情報を送信
var sendData = '{';
sendData += '"command":"'+record['command']['value']+'",';
sendData += '"token":"KINTONE_TOKEN",';
sendData += '"appid":"KINTONE_APP_ID",';
sendData += '"domain":"KINTONE_SUBDOMEIN",';
sendData += '"key":"'+record['レコード番号']['value']+'"';
sendData += '}'
sendMqtts(sendData);
return event;
});
// レコード詳細表示時イベント
var eventsDetailShow = ['app.record.detail.show'];
kintone.events.on(eventsDetailShow, function(event) {
var record = event.record;
if(record['command']['value'] == "温湿度取得" && record['temp']['value'] == ""){
setInterval("location.reload()",3000);
}else if(record['command']['value'] != "温湿度取得" && record['picture']['value'] == ""){
setInterval("location.reload()",5000);
}
});
var data = {
messages: []
};
function SigV4Utils(){}
SigV4Utils.sign = function(key, msg) {
var hash = CryptoJS.HmacSHA256(msg, key);
return hash.toString(CryptoJS.enc.Hex);
};
SigV4Utils.sha256 = function(msg) {
var hash = CryptoJS.SHA256(msg);
return hash.toString(CryptoJS.enc.Hex);
};
SigV4Utils.getSignatureKey = function(key, dateStamp, regionName, serviceName) {
var kDate = CryptoJS.HmacSHA256(dateStamp, 'AWS4' + key);
var kRegion = CryptoJS.HmacSHA256(regionName, kDate);
var kService = CryptoJS.HmacSHA256(serviceName, kRegion);
var kSigning = CryptoJS.HmacSHA256('aws4_request', kService);
return kSigning;
};
function getCredentials(done) {
AWS.config.region = 'ap-northeast-1';
var cognitoidentity = new AWS.CognitoIdentity();
var params = {
IdentityPoolId: 'ap-northeast-1:COGNITO_IDENTITY_POOL_ID'
};
cognitoidentity.getId(params, function(err, objectHavingIdentityId) {
if (err) return done(err);
cognitoidentity.getCredentialsForIdentity(objectHavingIdentityId, function(err, data) {
if (err) return done(err);
var credentials = {
accessKey: data.Credentials.AccessKeyId,
secretKey: data.Credentials.SecretKey,
sessionToken: data.Credentials.SessionToken
};
console.log('CognitoIdentity has provided temporary credentials successfully.');
done(null, credentials);
});
});
}
function getEndpoint(regionName, awsIotEndpoint, done) {
getCredentials(function (err, creds) {
if (err) return done(err);
var time = moment.utc();
var dateStamp = time.format('YYYYMMDD');
var amzdate = dateStamp + 'T' + time.format('HHmmss') + 'Z';
var service = 'iotdevicegateway';
var region = regionName;
var secretKey = creds.secretKey;
var accessKey = creds.accessKey;
var algorithm = 'AWS4-HMAC-SHA256';
var method = 'GET';
var canonicalUri = '/mqtt';
var host = awsIotEndpoint;
var sessionToken = creds.sessionToken;
var credentialScope = dateStamp + '/' + region + '/' + service + '/' + 'aws4_request';
var canonicalQuerystring = 'X-Amz-Algorithm=AWS4-HMAC-SHA256';
canonicalQuerystring += '&X-Amz-Credential=' + encodeURIComponent(accessKey + '/' + credentialScope);
canonicalQuerystring += '&X-Amz-Date=' + amzdate;
canonicalQuerystring += '&X-Amz-SignedHeaders=host';
var canonicalHeaders = 'host:' + host + '\n';
var payloadHash = SigV4Utils.sha256('');
var canonicalRequest = method + '\n' + canonicalUri + '\n' + canonicalQuerystring + '\n' + canonicalHeaders + '\nhost\n' + payloadHash;
var stringToSign = algorithm + '\n' + amzdate + '\n' + credentialScope + '\n' + SigV4Utils.sha256(canonicalRequest);
var signingKey = SigV4Utils.getSignatureKey(secretKey, dateStamp, region, service);
var signature = SigV4Utils.sign(signingKey, stringToSign);
canonicalQuerystring += '&X-Amz-Signature=' + signature;
var wssEndpoint = 'wss://' + host + canonicalUri + '?' + canonicalQuerystring;
wssEndpoint += '&X-Amz-Security-Token=' + encodeURIComponent(sessionToken);
done(null, wssEndpoint);
});
}
var client;
getEndpoint(
'ap-northeast-1',
'HOST_NAME.iot.ap-northeast-1.amazonaws.com',
function(err, endpoint) {
if (err) {
console.log('Failed', err);
return;
}
var clientId = Math.random().toString(36).substring(7);
client = new Paho.MQTT.Client(endpoint, clientId);
var connectOptions = {
useSSL: true,
timeout: 3,
mqttVersion: 4,
onSuccess: subscribe
};
client.connect(connectOptions);
client.onMessageArrived = onMessage;
client.onConnectionLost = function(e) { console.log(e) };
}
);
function subscribe() {
client.subscribe("command/picture");
}
function sendMqtts(content) {
var message = new Paho.MQTT.Message(content);
message.destinationName = "human/picture";
client.send(message);
}
function onMessage(message) {
data.messages.push(message.payloadString);
}
})();
参照情報
MQTT の基本知識 ( https://www.ibm.com/developerworks/jp/iot/library/iot-mqtt-why-good-for-iot/index.html )
MQTT Over the WebSocket Protocol ( https://docs.aws.amazon.com/iot/latest/developerguide/protocols.html#mqtt-ws )
AWS IoTのMQTT over WebSocketにHTMLから接続してみた ( https://dev.classmethod.jp/cloud/aws/aws-iot-mqtt-over-websocket/ )
AWS IoTのMQTT over WebSocketをCognito(Unauth)で認証して使ってみた ( https://dev.classmethod.jp/cloud/aws/aws-iot-mqtt-over-websocket-cognito-identity-unatuh/ )