Last active
February 20, 2019 18:22
-
-
Save ma2shita/4a43d39a8d61f6be8f1163050498e498 to your computer and use it in GitHub Desktop.
See: https://qiita.com/ma2shita/items/8f1b4b12faa99dd17063 | Monitoring ThingShadow in AWS IoT Core using Websocket
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="ja"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>Call monitor</title> | |
<script src="//unpkg.com/vue/dist/vue.js"></script> | |
<script src="//unpkg.com/vue-i18n/dist/vue-i18n.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/aws-sdk/2.404.0/aws-sdk.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.2/mqttws31.min.js"></script> | |
<!-- UIkit CSS --> | |
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/uikit/3.0.3/css/uikit.min.css" /> | |
<script>/* from https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/protocols.html */ | |
/** | |
* utilities to do sigv4 | |
* @class SigV4Utils | |
*/ | |
function SigV4Utils() { } | |
SigV4Utils.getSignatureKey = function (key, date, region, service) { | |
var kDate = AWS.util.crypto.hmac('AWS4' + key, date, 'buffer'); | |
var kRegion = AWS.util.crypto.hmac(kDate, region, 'buffer'); | |
var kService = AWS.util.crypto.hmac(kRegion, service, 'buffer'); | |
var kCredentials = AWS.util.crypto.hmac(kService, 'aws4_request', 'buffer'); | |
return kCredentials; | |
}; | |
SigV4Utils.getSignedUrl = function (host, region, credentials) { | |
var datetime = AWS.util.date.iso8601(new Date()).replace(/[:\-]|\.\d{3}/g, ''); | |
var date = datetime.substr(0, 8); | |
var method = 'GET'; | |
var protocol = 'wss'; | |
var uri = '/mqtt'; | |
var service = 'iotdevicegateway'; | |
var algorithm = 'AWS4-HMAC-SHA256'; | |
var credentialScope = date + '/' + region + '/' + service + '/' + 'aws4_request'; | |
var canonicalQuerystring = 'X-Amz-Algorithm=' + algorithm; | |
canonicalQuerystring += '&X-Amz-Credential=' + encodeURIComponent(credentials.accessKeyId + '/' + credentialScope); | |
canonicalQuerystring += '&X-Amz-Date=' + datetime; | |
canonicalQuerystring += '&X-Amz-SignedHeaders=host'; | |
var canonicalHeaders = 'host:' + host + '\n'; | |
var payloadHash = AWS.util.crypto.sha256('', 'hex') | |
var canonicalRequest = method + '\n' + uri + '\n' + canonicalQuerystring + '\n' + canonicalHeaders + '\nhost\n' + payloadHash; | |
var stringToSign = algorithm + '\n' + datetime + '\n' + credentialScope + '\n' + AWS.util.crypto.sha256(canonicalRequest, 'hex'); | |
var signingKey = SigV4Utils.getSignatureKey(credentials.secretAccessKey, date, region, service); | |
var signature = AWS.util.crypto.hmac(signingKey, stringToSign, 'hex'); | |
canonicalQuerystring += '&X-Amz-Signature=' + signature; | |
if (credentials.sessionToken) { | |
canonicalQuerystring += '&X-Amz-Security-Token=' + encodeURIComponent(credentials.sessionToken); | |
} | |
var requestUrl = protocol + '://' + host + uri + '?' + canonicalQuerystring; | |
return requestUrl; | |
}; | |
</script> | |
<style> | |
.status { | |
font-size: 2.2rem; | |
padding-top: 5rem; | |
padding-left: 1rem; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="app1"> | |
<div class="uk-section uk-padding-remove-vertical uk-section-muted"> | |
<div clss="uk-container uk-container-expand"> | |
<div uk-grid class="uk-grid-collapse"> | |
<div class="uk-width-1-3"> | |
<img src="https://docs.google.com/drawings/d/e/2PACX-1vS0Xcd447K0qwF0cfpv-TgWCsMccm4VUw2zSUN9PH5vadizscbd9FyWegUxP6YbFwSJ6hLUK7fDaGeE/pub?w=812&h=554"> | |
</div> | |
<div class="uk-width-1-3"> | |
<img src="https://docs.google.com/drawings/d/e/2PACX-1vRvOPH3QJ1lnBQQpBT6T5_fE6Dg9P9XzXWHjqkSbuqbp5U40x5eEfXC7F70An7Me0UwOtHZiZmBljtt/pub?w=812&h=554"> | |
</div> | |
<div class="uk-width-1-3 status"> | |
{{ $t(members["soracom_max"]) }} | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="uk-section uk-padding-remove-vertical uk-section-secondary"> | |
<div clss="uk-container uk-container-expand"> | |
<div uk-grid class="uk-grid-collapse"> | |
<div class="uk-width-1-3"> | |
<img src="https://docs.google.com/drawings/d/e/2PACX-1vQw3stGVwjr64T8gxmXNSCMr4gk3fmPrhJHrGJQWCxiJP7svgigJ0VazWPmMKBOCyvxtLRMNJ-5cxfL/pub?w=812&h=554"> | |
</div> | |
<div class="uk-width-1-3"> | |
<img src="https://docs.google.com/drawings/d/e/2PACX-1vSjsVZRBvz8AuNPcOOh7F8kyNkAHaK1FVcDXz6q9Mq3oQJSgjcEh9KPkh8PT7mHKGbrhQ0vFTQSXUxW/pub?w=812&h=554"> | |
</div> | |
<div class="uk-width-1-3 status"> | |
{{ $t(members["soracom_moto"]) }} | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="uk-section uk-padding-remove-vertical uk-section-primary"> | |
<div clss="uk-container uk-container-expand"> | |
<div uk-grid class="uk-grid-collapse"> | |
<div class="uk-width-1-3"> | |
<img src="https://docs.google.com/drawings/d/e/2PACX-1vRQ2ikHXaJuGzfvFOYowgJZBxROyq-xn1dK1iE1lwL3NaIwGqrVjxT7zmHHAQDoWUYmKsnZeVXyUWND/pub?w=812&h=554"> | |
</div> | |
<div class="uk-width-1-3"> | |
<img src="https://docs.google.com/drawings/d/e/2PACX-1vTiNqpkjiENsKyGJCSggJUsgoO-Fql-O0myJKu0vWZHC0nw_evQuzgHtKSO9JJLfnQb5WhFXfmKvedn/pub?w=812&h=554"> | |
</div> | |
<div class="uk-width-1-3 status"> | |
{{ $t(members["soracom_yaman"]) }} | |
</div> | |
</div> | |
</div> | |
</div> | |
</div><!-- #app1 --> | |
</body> | |
<script> | |
const i18n = new VueI18n({ | |
locale: 'ja', | |
messages: { | |
ja: { | |
idle: '呼び出しOK!', | |
calling: '呼び出し中...', | |
running: '現場急行中!', | |
suspend: '―' | |
} | |
} | |
}); | |
const app1 = new Vue({ | |
el: '#app1', | |
i18n: i18n, | |
data: { | |
members: {}, // set defered | |
_target_things: null, // for iterator (not need reactive) | |
_lookup_key: null, // for lookup key in Shadow Document | |
_mqtt: null, | |
connectSettings: { | |
cognitoIdentityPoolId: '', | |
awsiotcoreEndpoint: '', | |
clientId: '' | |
} | |
}, | |
methods: { | |
connect: function () { | |
AWS.config.region = this.connectSettings.cognitoIdentityPoolId.split(':')[0]; | |
AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId: this.connectSettings.cognitoIdentityPoolId }); | |
var self = this; /* ref: https://qiita.com/takeharu/items/9935ce476a17d6258e27 */ | |
AWS.config.credentials.get(function () { | |
var endpoint = SigV4Utils.getSignedUrl(self.connectSettings.awsiotcoreEndpoint, AWS.config.region, AWS.config.credentials); | |
self._mqtt = new Paho.MQTT.Client(endpoint, self.connectSettings.clientId); | |
self._mqtt.connect({ | |
useSSL: true, | |
mqttVersion: 4, | |
onSuccess: function () { | |
self._mqtt.subscribe('$aws/things/+/shadow/update/documents'); | |
self._mqtt.subscribe('$aws/things/+/shadow/get/+', { | |
onSuccess: function () { | |
self._target_things.forEach(function (i) { // get current staet for bootup | |
self._mqtt.send(`$aws/things/${i}/shadow/get`, '{}'); | |
}); | |
} | |
}); | |
}, | |
onFailure: function (e) { | |
console.log(e); | |
} | |
}); | |
self._mqtt.onConnectionLost = function (e) { | |
console.log(e); | |
}; | |
self._mqtt.onMessageArrived = function (message) { | |
console.log(message.destinationName); | |
let topic = message.destinationName.split("/"); | |
switch (topic.slice(-2).toString()) { | |
case ['update', 'documents'].toString(): | |
var content = JSON.parse(message.payloadString).current; | |
break; | |
case ['get', 'accepted'].toString(): | |
var content = JSON.parse(message.payloadString); | |
break; | |
case ['get', 'rejected'].toString(): | |
console.warn(`Shadow not found: create > {"reported":{"status":"idle","welcome":null},"desired":{"${self._lookup_key}":null,"welcome":null}}`); | |
break; | |
default: | |
console.warn('Received other topic'); | |
console.warn(message.payloadString); | |
return; | |
} | |
let thing_name = topic[2]; | |
let status = content.state.reported[self._lookup_key]; // Need improved: In case of raising TypeError and in case of undefined | |
console.log(thing_name, status, content.version); | |
self.members[thing_name] = status; | |
}; | |
}); | |
} | |
} | |
}); | |
window.onload = function () { | |
app1._target_things = ['soracom_max', 'soracom_moto', 'soracom_yaman']; | |
app1._lookup_key = "status"; | |
app1.connectSettings.cognitoIdentityPoolId = `YOUR_AMAZON_COGNITO_POOL_ID`; | |
app1.connectSettings.awsiotcoreEndpoint = `YOUR_AWS_IOTCORE_CUSTOM_ENDPOINT`; | |
app1.connectSettings.clientId = `webclient0`; | |
app1._target_things.forEach(function(i) { // Dynamic setup using `_target_things` | |
app1.$set(app1.members, i, 'suspend'); | |
}); | |
app1.connect(); | |
}; | |
</script> | |
<!-- UIkit JS --> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/uikit/3.0.3/js/uikit.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/uikit/3.0.3/js/uikit-icons.min.js"></script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment