概要
少し前に
『Gobotの招きにあひて、徒然なるままにArduinoとRaspberry PiでIoTっぽいことをやってみるなり』
という記事で温度計情報をAPIとして公開できるIoTの仕組みを作りました。
やっぱり、情報とれるようになったら綺麗に描画したい!
今回はそんな感じの話をします。よろしくお願い致します!٩( 'ω' )و
なお、今回もGitHubに連動したリポジトリを作っています。あわせてご覧ください。
どうやって描画する?
Vue.js使ってクライアントサイドでレンダリングします!
小さくはじめられる簡便性、プロジェクトが大きくなっても対応可能なスケーラビリティ性、仮想DOMによって実現される軽快動作のリアクティブ性などなどが気に入って1週間ほど前にVue.jsに入門しました。
vue-material
Vue.js用のマテリアルデザイン実装であるvue-materialも使って簡単に綺麗なUIを手に入れます。
Canvas Gauges
温度計や各種ゲージを模したUIパーツCanvas Gaugesも使います。
axios
APIサービスにアクセスするhttpクライアントにはaxiosを使います。
ネット上ではvue-resourceを使った記事が多いのですが、公式の以下の記事を呼んでaxiosに変更しました。
やってみた
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Temperature Monitor</title>
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">
<link rel="stylesheet" href="//fonts.googleapis.com/icon?family=Material+Icons">
</head>
<body>
<div id="tempmon">
<md-toolbar>
<h1 class="md-title">Temperature Monitor</h1>
</md-toolbar>
<md-button class="md-raised md-primary" v-on:click="getData">Update</md-button>
<md-layout md-gutter>
<md-layout md-flex-xsmall="100" md-flex-small="50" md-flex-medium="33" class="main-content">
<md-card>
<md-card-header>
<div class="md-title">{{ name }}</div>
</md-card-header>
<md-card-content>
<canvas id="gauge-id"></canvas>
<br>
<p>{{ message }}</p>
</md-card-content>
</md-card>
</md-layout>
</md-layout>
</div>
</body>
<script src="js/vue.js"></script>
<link rel="stylesheet" href="css/vue-material.css">
<script src="js/vue-material.js"></script>
<script src="js/axios.js"></script>
<script src="js/gauge.min.js"></script>
<script src="js/app.js"></script>
</html>
Vue.use(VueMaterial);
// TODO: サービス実行時に外部の設定ファイルから値を受け取りたい
const baseURI = 'http://localhost:5000/api/v1';
let tempmon = new Vue({
el: '#tempmon',
data: {
name: 'Sensor',
gauge: null,
message: '',
},
methods: {
init: function() {
this.gauge = new LinearGauge({
renderTo: 'gauge-id',
colorNumbers: 'red',
minValue: -10,
maxValue: 50,
majorTicks: [-10, 0, 10, 20, 30, 40, 50],
highlights: [{
"from": 30,
"to": 50,
"color": "rgba(200, 50, 50, .75)"
}],
width: 200,
height: 400,
});
},
getData: function() {
axios.get(baseURI + '/sensors')
.then((response) => {
let sensors = response.data.sensor_list;
this.name = 'Sensor: ' + sensors[0].name;
this.gauge.value = sensors[0].temp_c;
this.message = '';
})
.catch((error) => {
this.message = error;
});
}
}
});
window.onload = function() {
tempmon.init();
}
LinearGauge
のインスタンスを生成してVueのdataにバインドする処理をinit
関数に実装し、window.onload
のタイミングで呼ぶようにしました。これならhtml側に記述した<canvas id="gauge-id"></canvas>
が存在するのでLinearGauge
の生成がうまくいきます。
APIサービスのベースURIは、Webサービス実行時の引数から反映したかったのですがうまく実装できずbaseURI
定数としてapp.js
に直書きしてしまっております(´・ω・`) 賢者様、私にアドバイスください。。。
動かしてみた
こんな感じになりました。
見た目が本当に温度計そのもので良い感じですし、ゲージのぐりゅんと動くアニメーションも好きです(´ω`)
Update
ボタンを押すと、以下のようにv-on:click
によって紐付けられたVueインスタンスのgetData
メソッドが実行されます。
<md-button class="md-raised md-primary" v-on:click="getData">Update</md-button>
getData
関数ではhttpクライアントであるaxios
を使って、以下のようにHTTPのGETメソッドでJSONを取得しています。
axios.get(baseURI + '/sensors')
.then((response) => {
let sensors = response.data.sensor_list;
this.name = 'Sensor: ' + sensors[0].name;
this.gauge.value = sensors[0].temp_c;
this.message = '';
})
.catch((error) => {
this.message = error;
});
取得できるJSONは以下のような内容です。
{
"sensor_list": [
{
"number": 1,
"name": "Kitchen",
"temp_c": 25.86
}
]
}
最初はルートのオブジェクト名をsensor-list
というチェーンケースにしていたために、response.data.sensor-list
ではなくresponse.data['sensor-list']
という記法で取り出さなくてはならず面倒でした。そこでsensor-list
ではなくsensor_list
に変更しました。JSONのオブジェクト名は、チェーンケースではなくスネークケースを使った方が何かと捗りそうです(´・ω・`)b
まとめ
- Vue.jsの門扉を叩いて入門し、IoTのAPIをレンダリングする簡単なWebサービスが作れました
- Vue material本当に綺麗で好き(´ω`)
- Canvas Gaugeで温度計っぽい表現も簡単に実現できました
- アニメーション、個人的には好きですが好き嫌いがでそうですね・・・
- JSONのオブジェクト名はスネークケース(アンダーバー区切り)のほうが良さそう
こんな感じでやってみましたが、相変わらずフロントエンド技術はひよっこですのでコメント欄でアドバイスやご指導いただけますと幸いですm(_ _)m