kintone から MQTTS に送信（Subscribe) し Raspberry Pi の遠隔制御結果を表示（kintone側の処理）

## 処理内容
・kintone で 命令を選択し、制御命令レコードを追加すると、MQTTS にコマンドを送信（Publish）します
・MQTTS からコマンドを受信 (Subscribe) した Raspberry Pi が、制御コマンドを受信し、命令の処理（計測・撮影）を行います。
・Raspberry Pi から API 経由で kintone のレコードにデータを PUT して更新します。
・kintone の詳細画面で計測・撮影結果が表示されます。(結果が戻るか、指定回数に到達するまでは ReLoad を繰り返す。)
Raspberry Pi 側の処理結果は https://qiita.com/yukataoka/items/b3a7e5e9dc7471b5b66d を参照
## 完成画面
命令を選択し「保存」します。（保管と同時に、制御指示をMQTTSに送信。）
![kintopneMqtt01.png](https://qiita-image-store.s3.amazonaws.com/0/75892/1e7c6940-59ee-7db0-e3df-b95b4c853b14.png)
遠隔地の Raspberry Pi が計測温湿度（3秒後）・撮影画像（8秒後）が kintone に保管され、表示されます。 
![kintopneMqtt02.png](https://qiita-image-store.s3.amazonaws.com/0/75892/cfb788fd-86ec-c0e3-db90-a38ec7542798.png)
## 設定画面
kintone の設定画面を開きます。
![kintopneMqtt03.png](https://qiita-image-store.s3.amazonaws.com/0/75892/0297cc5e-477b-8692-d037-9cc5ee4a6ce0.png)
以下のように JavaScript のライブラリィを参照し、カスタマイズしたプログラムをアップロードします。
![kintopneMqtt04.png](https://qiita-image-store.s3.amazonaws.com/0/75892/d99db18b-db24-2738-2149-0b664a6401af.png)

参照するライブラリィは以下
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 経由で更新されたセンサの情報や、カメラの画像を表示します。

```javascript:kintoneAwsIot.js
(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/ )