Go
vue.js
IoT
vue-material
canvas-gauges

IoTのAPIサービスから取得した温度計情報をVue.jsとCanvas Gaugesでレンダリングしてみた

More than 1 year has passed since last update.

概要

少し前に
 『Gobotの招きにあひて、徒然なるままにArduinoとRaspberry PiでIoTっぽいことをやってみるなり』
という記事で温度計情報をAPIとして公開できるIoTの仕組みを作りました。

やっぱり、情報とれるようになったら綺麗に描画したい!
今回はそんな感じの話をします。よろしくお願い致します!٩( 'ω' )و

なお、今回もGitHubに連動したリポジトリを作っています。あわせてご覧ください。

KemoKemo/IoT-Web-Sample

どうやって描画する?

Vue.js使ってクライアントサイドでレンダリングします!
小さくはじめられる簡便性、プロジェクトが大きくなっても対応可能なスケーラビリティ性、仮想DOMによって実現される軽快動作のリアクティブ性などなどが気に入って1週間ほど前にVue.jsに入門しました。

vue-material

Vue.js用のマテリアルデザイン実装であるvue-materialも使って簡単に綺麗なUIを手に入れます。

vue-material
29198697-523e-4fad-a242-4c89beaa6957.png

Canvas Gauges

温度計や各種ゲージを模したUIパーツCanvas Gaugesも使います。

Canvas Gauges
f84ad7cb-8210-4e99-9842-6a7e5a694795.png

axios

APIサービスにアクセスするhttpクライアントにはaxiosを使います。
ネット上ではvue-resourceを使った記事が多いのですが、公式の以下の記事を呼んでaxiosに変更しました。

vue-resource の引退について

やってみた

<!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に直書きしてしまっております(´・ω・`) 賢者様、私にアドバイスください。。。

動かしてみた

web-sample.gif

こんな感じになりました。
見た目が本当に温度計そのもので良い感じですし、ゲージのぐりゅんと動くアニメーションも好きです(´ω`)

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