RaspberryPiでリソースの可視化(NetData版)

提供: ディーズガレージ wiki
移動先: 案内検索

NetData使ってみます。
応答速度3ミリ秒、5000メトリクス以上/秒、300チャート以上/秒処理できるそうです。
こんなもの作成してみます。

開発環境

ボード Raspberry Pi 2 Model B
OS Raspbian Jessie with PIXEL 2017-04-10
microSDHC SanDisk Ultra microSDHC Class10 8GB
LAN 標準搭載の有線LAN

ファームウェア更新

$ sudo rpi-update

再起動

$ sudo reboot

最新状態にアップデート

$ sudo apt-get update
$ sudo apt-get upgrade

再起動

$ sudo reboot

ホスト名

ホスト名変更

$ sudo leafpad /etc/hostname

変更

iot

ホスト名変更

$ sudo leafpad /etc/hosts

変更

127.0.1.1 iot

再起動

$ sudo reboot

IP固定

以降の遠隔操作でIPアドレスで接続したい場合必要な設定です。

$ sudo leafpad /etc/dhcpcd.conf

末尾に追記(設定内容は各ネットワーク環境により異なります。「Raspberry Pi Jessie IP固定」などで検索してみてください。)

interface eth0
static ip_address=192.168.1.187/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1

再起動

$ sudo reboot

遠隔操作

運用にはモニター、マウス、キーボードは必要ないので、この段階で遠隔操作にしてしまいます。
Raspbian Jessie with PIXEL にはRealVNCが標準で入ってます。これを使用してみます。

デフォルトで有効になってないので

Menu→設定→RaspberryPiの設定→インターフェイスタブ→VNC有効→OK→再起動

何も考えずディスプレイを取り外しVNCに接続すると画面サイズが656x416となってしまいます。おまじないがあるようなので設定しておきます。

$ sudo leafpad /boot/config.txt
#hdmi_mode=1の下に以下を追加
hdmi_ignore_edid=0xa5000080
hdmi_group=2
hdmi_mode=47

参考: config.txt - Raspberry Pi Documentation

RaspberryPiをシャットダウン

モニター、マウス、キーボードを取り外して電源入れ直し

VNCクライアントから接続してみる
ホスト名で接続する場合、クライアントパソコンにはavahiかBonjourがインストールされている必要があります。
Windowsパソコンだけ問題になりやすく、簡単な方法はiTunesをインストールすることです。
ネット検索で「Raspberry avahi Bonjour」辺りで調べると詳しい事情が出てきます。

RealVNC Viewerの場合
VNC Server: iot.local または 192.168.1.187
Username: pi
Password: raspberry
Confirm: raspberry

成功していれば、以降はVNCクライアントの窓の中で作業できます。

NetDataのインストール

参考: Installation · firehol/netdata Wiki · GitHub

$ sudo apt-get update
$ sudo apt-get install zlib1g-dev uuid-dev libmnl-dev gcc make git autoconf autoconf-archive autogen automake pkg-config curl
$ git clone https://github.com/firehol/netdata.git --depth=1
$ cd netdata
$ sudo ./netdata-installer.sh
$ sudo cp system/netdata.service /etc/systemd/system/
$ sudo systemctl daemon-reload
$ sudo systemctl enable netdata
$ sudo service netdata start

設定
参考: netdata for IoT · firehol/netdata Wiki · GitHub

$ sudo leafpad /etc/netdata/netdata.conf

RPi1向け (RPi2とRP3のnetdataのチューニングは必要ないとなってます)

[global]
    history = 600
    update every = 1
    memory mode = ram
    debug log = none
    error log = none
    access log = none
[web]
    enable gzip compression = no
    gzip compression level = 1
[plugins]
    tc = no
    idlejitter = no
    cgroups = no
    #enable running new plugins = no

sensorsプラグインを有効に(python版は動きませんでしたbash版で対応)

$ sudo leafpad /etc/netdata/charts.d.conf

アンコメント

sensors=force

再起動

$ sudo reboot

動作確認

http://127.0.0.1:19999
またはローカルネット内の他のPCから http://192.168.1.187:19999

設定項目の確認

http://127.0.0.1:19999/netdata.conf
またはローカルネット内の他のPCから http://192.168.1.187:19999/netdata.conf

nginxのインストール

参考: Running behind nginx · firehol/netdata Wiki · GitHub
参考: netdata/python.d at master · firehol/netdata · GitHub

$ sudo apt-get install nginx
$ sudo /etc/init.d/nginx start

デフォルトのシンボリックリンク削除

$ sudo unlink /etc/nginx/sites-enabled/default

設定作成

$ sudo leafpad /etc/nginx/sites-available/netdata
log_format netdata '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $body_bytes_sent '
                '$request_length $request_time '
                '"$http_referer" "$http_user_agent"';

access_log /var/log/nginx/access.log netdata;

upstream backend {
    # the netdata server
    server 127.0.0.1:19999;
    keepalive 64;
}

server {
    # nginx listens to this
    listen 80;
    
    # the virtual host name of this
    server_name netdata.example.com;

    location / {
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_pass_request_headers on;
        proxy_set_header Connection "keep-alive";
        proxy_store off;
    }
    location /stub_status {
        stub_status on;
        access_log off;
        allow 192.168.1.0/24;
        allow 127.0.0.1;
        deny all;
    }
}

シンボリックリンク

$ sudo ln -s /etc/nginx/sites-available/netdata /etc/nginx/sites-enabled/

再起動

$ sudo reboot

動作確認

http://127.0.0.1
またはローカルネット内の他のPCから http://192.168.1.187

アップデートの自動化

インストール

$ sudo apt-get install unattended-upgrades
$ sudo dpkg-reconfigure -plow unattended-upgrades
GUIがでるので「はい」を選択

設定

$ sudo leafpad /etc/apt/apt.conf.d/50unattended-upgrades

変更箇所(セキュリティーアップデートのみ自動アップデート設定)

//      "o=Raspbian,a=stable";"o=Raspbian,a=stable";
に

//Unattended-Upgrade::Automatic-Reboot "false";
を
Unattended-Upgrade::Automatic-Reboot "true";
に

//Unattended-Upgrade::Automatic-Reboot-Time "02:00";
を
Unattended-Upgrade::Automatic-Reboot-Time "03:00";

再起動

$ sudo reboot

カスタマイズ

貼付けテスト

参考: Custom Dashboards · firehol/netdata Wiki · GitHub

NetDataはdashboard.jsで(相対パス)ライブラリを読み込みしているのでdashboard.jsまで到達できればhtmlはどこでも設置可能です。
ただ、メジャーなライブラリを多用してるのでコンフリクトで動かなくなる状況が想像できます。
iframeの埋め込みができればいちばん簡単と思います。
stub_statusに関係するnginxのaccess.logなどはアクセス制限してるので値が取れません。
セキュリティー対策は自己責任で

ソースコード

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<style>
canvas {  
	width: 100px;
	height: 100px;
} 
.easyPieChartTitle {
	font-size: 11px !important;
	top: 28px !important;
}
.easyPieChartLabel {
	font-size: 16px !important;
	top: 38px !important;
}
.easyPieChartUnits {
	font-size: 11px !important;
	top: 62px !important;
}
</style>
</head>
<body>
<div data-netdata="system.cpu"
	data-chart-library="easypiechart"
	data-title="CPU"
	data-units="%"
	data-easypiechart-max-value="100"
	data-width="100"
	data-points="600"
	data-colors="#e3e000">
</div>
<div data-netdata="sensors.temp_thermal_zone0_thermal_thermal_zone0"
	data-chart-library="easypiechart"
	data-title="TEMP"
	data-units="°C"
	data-easypiechart-max-value="85"
	data-width="100"
	data-points="600"
	data-colors="#ff8400">
</div>
<div data-netdata="system.ram"
	data-dimensions="free"
	data-chart-library="easypiechart"
	data-title="Free RAM"
	data-units="MB"
	data-easypiechart-max-value="512"
	data-width="100"
	data-points="600"
	data-colors="#ff00de">
</div>
<div data-netdata="system.ipv4"
	data-dimensions="received"
	data-chart-library="easypiechart"
	data-title="IPv4 IN"
	data-units="Kbps"
	data-width="100"
	data-points="600">
</div>
<div data-netdata="system.ipv4"
	data-dimensions="sent"
	data-chart-library="easypiechart"
	data-title="IPv4 OUT"
	data-units="Kbps"
	data-width="100"
	data-points="600">
</div>
<div data-netdata="disk_space._"
	data-dimensions="used"
	data-chart-library="easypiechart"
	data-title="Used DISK"
	data-units="GB"
	data-easypiechart-max-value="8"
	data-width="100"
	data-points="600"
	data-colors="#93c085">
</div>
<div data-netdata="system.io"
	data-dimensions="in"
	data-chart-library="easypiechart"
	data-title="Disk READ"
	data-units="Kbps"
	data-width="100"
	data-points="600">
</div>
<div data-netdata="system.io"
	data-dimensions="out"
	data-chart-library="easypiechart"
	data-title="Disk WRITE"
	data-units="Kbps"
	data-width="100"
	data-points="600">
</div>
<div data-netdata="system.swap"
	data-dimensions="used"
	data-append-options="percentage"
	data-chart-library="easypiechart"
	data-title="SWAP"
	data-units="%"
	data-easypiechart-max-value="100"
	data-width="100"
	data-points="600"
	data-colors="#93c085">
</div>
<script type="text/javascript" src="http://dz.plala.jp:19999/dashboard.js"></script>
<script>
NETDATA.options.current.stop_updates_when_focus_is_lost = false;
</script>
</body>
</html>

参考雛型

http://dz.plala.jp:19999/index.html
http://dz.plala.jp:19999/tv.html
http://dz.plala.jp:19999/dashboard.html
http://dz.plala.jp:19999/infographic.html
http://dz.plala.jp:19999/demo.html
http://dz.plala.jp:19999/demo2.html
http://dz.plala.jp:19999/demosites.html
http://dz.plala.jp:19999/demosites2.html
http://dz.plala.jp:19999/goto-host-from-alarm.html
http://dz.plala.jp:19999/registry.html

オリジナル作成下調べ

NetDataのビジュアル部分は結局URL叩き返ってくる配列を処理するだけなのでdashboard.jsに頼らずとも作成できます。
APIドキュメントが見つからないので大雑把なまとめ

http://dz.plala.jp:19999/api/v1/badge.svg?chart=CHARTS_ID
http://dz.plala.jp:19999/api/v1/data?chart=CHARTS_ID
http://dz.plala.jp:19999/api/v1/chart?chart=CHARTS_ID
http://dz.plala.jp:19999/api/v1/charts
http://dz.plala.jp:19999/api/v1/registry?action=hello%7Caccess%7Cdelete%7Csearch
http://dz.plala.jp:19999/api/v1/alarms
http://dz.plala.jp:19999/api/v1/allmetrics?format=shell%7Cprometheus%7Cprometheus_all_hosts%7Cjson

場合により使えるクエリパラメータ
&format=array|json|csv|csvjsonarray|tsv|html|datatable|datasource
&options=absolute|ms|flip|jsonwrap|nonzero|percentage|objectrows
&options=seconds|milliseconds
&before=数値 &after=-数値 &points=数値 &group=average &dimensions=dimensions名 &_=UNIX時間

http://dz.plala.jp:19999/api/v1/charts
http://dz.plala.jp:19999/api/v1/chart?chart=system.cpu
http://dz.plala.jp:19999/api/v1/data?chart=system.cpu&after=-1&format=array

EasyPieChart

参考: EASY PIE CHART by rendro
参考: GitHub - rendro/easy-pie-chart

EasyPieChartは100%が基準の作りのため100やパーセント以外の数値や単位では動かせません。
ライブラリを改変して任意の値に対応させます。

使用ライブラリ

https://github.com/rendro/easy-pie-chart/blob/master/dist/jquery.easypiechart.js

改変箇所
190行目 書き換え

// draw bar
//drawCircle(color, options.lineWidth, percent / 100);
drawCircle(color, options.lineWidth, percent / options.maxValue);

219行目 追記

var defaultOptions = {
    maxValue: 100,
    barColor: '#ef1e25',

ソースコード

<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<style>
* {
	margin: 0;
	padding: 0;
}
html, body {
	font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
	font-style: normal;
	font-weight: normal;
}
.gauge-container {
	position: relative;
	height: 100px;
	width: 100px;
	float: left;
}
canvas {
	position: absolute;
	top: 0;
	left: 0;
}
.gauge {
	width: 100px;
	height: 100px;
	display: table-cell;
	vertical-align: middle;
	text-align: center;
}
.title, .label, .units {
	width: 100%;
	color: #999;
}
.title, .units {
	font-size: 11px;
}
.label {
	display: inline-block;
	text-align: center;
	font-size: 16px;
	font-weight: bold;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js"></script>
<script src="jquery.easypiechart.js"></script>
<script>
$(function() {
	var ip = 'http://dz.plala.jp:19999/';
	
	var gaugeList = [
		{
			title: 'CPU',
			url: 'api/v1/data?chart=system.cpu&after=-1&format=array',
			units: '%',
			max: 100,
			color: '#e3e000'
		},{
			title: 'TEMP',
			url: 'api/v1/data?chart=sensors.temp_thermal_zone0_thermal_thermal_zone0&after=-1&format=array',
			units: '°C',
			max: 85,
			color: '#ff8400'
		},{
			title: 'Free RAM',
			url: 'api/v1/data?chart=system.ram&after=-1&format=array&dimensions=free',
			units: 'MB',
			max: 512,
			color: '#ff00de'
		},{
			title: 'IPv4 IN',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=received',
			units: 'Kbps',
			max: 1000,
			color: '#3366cc'
		},{
			title: 'IPv4 OUT',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=sent',
			units: 'Kbps',
			max: 1000,
			color: '#dc3912'
		},{
			title: 'Used DISK',
			url: 'api/v1/data?chart=disk_space._&after=-1&format=array&dimensions=used',
			units: 'GB',
			max: 8,
			color: '#93c085'
		},{
			title: 'Disk READ',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=in&options=absolute',
			units: 'Kbps',
			max: 100,
			color: '#3366cc'
		},{
			title: 'Disk WRITE',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=out&options=absolute',
			units: 'Kbps',
			max: 100,
			color: '#dc3912'
		},{
			title: 'SWAP',
			url: 'api/v1/data?chart=system.swap&after=-1&format=array&dimensions=used',
			units: '%',
			max: 100,
			color: '#93c085'
		}
	];
	
	var xhrList = [];
	
	for (var i = 0; i < gaugeList.length; i++) {
		$('body').append('<div class="gauge-container"><div id="gauge' + i + '" class="gauge"><span class="title">' + gaugeList[i].title + '</span><span class="label"></span><span class="units">' + gaugeList[i].units + '</span></div></div>');
		$('#gauge' + i).easyPieChart({
			size: 100,
			maxValue: gaugeList[i].max,
			barColor: gaugeList[i].color,
			trackColor: '#eee',
			scaleColor: 'rgba(0,0,0,0)',
			lineWidth: 4,
			//easing: 'easeOutQuart',
			onStep: function(from, to, currentValue) {
				$(this.el).find('.label').text(currentValue.toFixed(2));
			}
		});
	}
	
	setInterval(function() {
		for (var i = 0; i < gaugeList.length; i++) {
			xhrList.push($.ajax({
				url: ip + gaugeList[i].url,
				type: 'GET',
				cache: false,
				dataType: 'json'
			}));
		}
		$.when.apply($, xhrList ).done(function () {
			for ( var i = 0; i < arguments.length; i++ ) {
				if (arguments[i][0][0]) {
					$('#gauge' + i).data('easyPieChart').update(Math.abs(arguments[i][0][0]));
				} else {
					$('#gauge' + i).data('easyPieChart').update(0);
				}
			}
			xhrList.length = 0;
		});
	}, 1000);
});
</script>
</head>
<body>
</body>
</html>

justGage

参考: justGage.com
参考: GitHub - toorshia/justgage

ソースコード (IEとEdgeのローカルファイルパス起動で不具合あり)

<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<style>
* {
	margin: 0;
	padding: 0;
}
html, body {
	font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
	font-style: normal;
	font-weight: normal;
}
.gauge {
	position: relative;
	width: 120px;
	height: 100px;
	float: left;
}
svg {
	position: absolute !important;
	margin-top: 10px;
}
.title, .units {
	position: absolute;
	width: 100%;
	text-align: center;
	font-size: 11px;
	color: #999;
}
.title {
	margin-top: 9px;
}
.units {
	margin-top: 82px;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.2.7/raphael.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/justgage/1.2.9/justgage.min.js"></script>
<script>
$(function() {
	var ip = 'http://dz.plala.jp:19999/';
	
	var gaugeList = [
		{
			title: 'CPU',
			url: 'api/v1/data?chart=system.cpu&after=-1&format=array',
			units: '%',
			min: 0,
			max: 100,
			color: '"#eaed73", "#f0f700", "#abb100"'
		},{
			title: 'TEMP',
			url: 'api/v1/data?chart=sensors.temp_thermal_zone0_thermal_thermal_zone0&after=-1&format=array',
			units: '°C',
			min: 0,
			max: 85,
			color: '"#ffc17e", "#ff890c", "#bf6300"'
		},{
			title: 'Free RAM',
			url: 'api/v1/data?chart=system.ram&after=-1&format=array&dimensions=free',
			units: 'MB',
			min: 0,
			max: 512,
			color: '"#ff7ef8", "#ff0cf1", "#bf00b2"'
		},{
			title: 'IPv4 IN',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=received',
			units: 'Kbps',
			min: 0,
			max: 1000,
			color: '"#a19dff", "#2b22ff", "#0800cf"'
		},{
			title: 'IPv4 OUT',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=sent',
			units: 'Kbps',
			min: 0,
			max: 1000,
			color: '"#ff7e7d", "#ff0c0c", "#bf0000"'
		},{
			title: 'Used DISK',
			url: 'api/v1/data?chart=disk_space._&after=-1&format=array&dimensions=used',
			units: 'GB',
			min: 0,
			max: 8,
			color: '"#7dff7e", "#0cff0c", "#00bf00"'
		},{
			title: 'Disk READ',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=in&options=absolute',
			units: 'Kbps',
			min: 0,
			max: 100,
			color: '"#a19dff", "#2b22ff", "#0800cf"'
		},{
			title: 'Disk WRITE',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=out&options=absolute',
			units: 'Kbps',
			min: 0,
			max: 100,
			color: '"#ff7e7d", "#ff0c0c", "#bf0000"'
		},{
			title: 'SWAP',
			url: 'api/v1/data?chart=system.swap&after=-1&format=array&dimensions=used',
			units: '%',
			min: 0,
			max: 100,
			color: '"#7dff7e", "#0cff0c", "#00bf00"'
		}
	];
	
	if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
		var dflt = {
			value: 0,
			decimals: 2,
			counter: true,
			gaugeWidthScale: 1.0,
			hideMinMax: true,
			valueMinFontSize: 14,
			valueFontFamily: "Arial",
			valueFontColor: "#999",
			refreshAnimationTime: 1000
		};
	} else {
		var dflt = {
			value: 0,
			decimals: 2,
			counter: true,
			gaugeWidthScale: 1.0,
			hideMinMax: true,
			valueMinFontSize: 14,
			valueFontFamily: "Arial",
			valueFontColor: "#999",
			refreshAnimationTime: 1000,
			startAnimationType: "linear",
			refreshAnimationType: "linear",
			showInnerShadow: true,
			shadowOpacity: 0.3,
			shadowSize: 1,
			shadowVerticalOffset: 1
		};
	};
	
	for (var i = 0; i < gaugeList.length; i++) {
		$('body').append('<div id="gauge' + i + '" class="gauge"><span class="title">' + gaugeList[i].title + '</span><span class="units">' + gaugeList[i].units + '</span></div>');
		eval('var gauge' + i + ' = new JustGage({id: "gauge' + i + '", min: ' + gaugeList[i].min + ', max: ' + gaugeList[i].max + ', levelColors: [' + gaugeList[i].color + '], defaults: dflt})');
	};
	
	setInterval(function() {
		$.ajax({url: ip + gaugeList[0].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge0.refresh(Math.abs(json[0]));}});
		$.ajax({url: ip + gaugeList[1].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge1.refresh(Math.abs(json[0]));}});
		$.ajax({url: ip + gaugeList[2].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge2.refresh(Math.abs(json[0]));}});
		$.ajax({url: ip + gaugeList[3].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge3.refresh(Math.abs(json[0]));}});
		$.ajax({url: ip + gaugeList[4].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge4.refresh(Math.abs(json[0]));}});
		$.ajax({url: ip + gaugeList[5].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge5.refresh(Math.abs(json[0]));}});
		$.ajax({url: ip + gaugeList[6].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge6.refresh(Math.abs(json[0]));}});
		$.ajax({url: ip + gaugeList[7].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge7.refresh(Math.abs(json[0]));}});
		$.ajax({url: ip + gaugeList[8].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge8.refresh(Math.abs(json[0]));}});
	}, 1000);
	
	/*
	var instance = [gauge0, gauge1, gauge2, gauge3, gauge4, gauge5, gauge6, gauge7, gauge8];
	var xhrList = [];
	
	setInterval(function() {
		for (var i = 0; i < gaugeList.length; i++) {
			xhrList.push($.ajax({
				url: ip + gaugeList[i].url,
				type: 'GET',
				cache: false,
				dataType: 'json'
			}));
		}
		$.when.apply($, xhrList ).done(function () {
			for ( var i = 0; i < arguments.length; i++ ) {
				if (arguments[i][0][0]) {
					instance[i].refresh(Math.abs(arguments[i][0][0]));
					console.log(instance[0]);
				} else {
					instance[i].refresh(0);
				}
			}
			xhrList.length = 0;
		});
	}, 1000);
	*/
});
</script>
</head>
<body>
</body>
</html>

カラーチャート
Color chart.jpg

gauge.js Donut

参考: gauge.js
参考: GitHub - bernii/gauge.js

ソースコード

<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<style>
* {
	margin: 0;
	padding: 0;
}
html, body {
	font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
	font-style: normal;
	font-weight: normal;
}
.gauge {
	position: relative;
	width: 120px;
	height: 120px;
	float: left;
}
canvas {
	position: absolute;
	width: 180px;
	height: 90px;
	margin-left: -30px;
	margin-top: 14px;
}
.value {
	position: absolute;
	width: 100%;
	text-align: center;
	font-size: 14px;
	color: #999;
}
.title, .value, .units {
	position: absolute;
}
.title {
	width: 100%;
	text-align: center;
	margin-top: 36px;
	font-size: 11px;
	color: #999;
}
.value {
	width: 100%;
	text-align: center;
	margin-top: 52px;
	font-size: 16px;
	font-weight: bold;
	color: #999;
}
.units {
	width: 100%;
	text-align: center;
	margin-top: 74px;
	font-size: 11px;
	color: #999;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="gauge.min.js"></script>
<script>
$(function() {
	if (typeof Object.assign != 'function') {
		Object.assign = function(target) {
			'use strict';
			if (target == null) {
				throw new TypeError('Cannot convert undefined or null to object');
			}
			
			target = Object(target);
			for (var index = 1; index < arguments.length; index++) {
				var source = arguments[index];
				if (source != null) {
					for (var key in source) {
						if (Object.prototype.hasOwnProperty.call(source, key)) {
							target[key] = source[key];
						}
					}
				}
			}
			return target;
		};
	}
	
	var ip = 'http://dz.plala.jp:19999/';
	
	var gaugeList = [
		{
			title: 'CPU',
			url: 'api/v1/data?chart=system.cpu&after=-1&format=array',
			units: '%',
			min: 0,
			max: 100,
			color: '#eaed73'
		},{
			title: 'TEMP',
			url: 'api/v1/data?chart=sensors.temp_thermal_zone0_thermal_thermal_zone0&after=-1&format=array',
			units: '°C',
			min: 0,
			max: 85,
			color: '#ffc17e'
		},{
			title: 'Free RAM',
			url: 'api/v1/data?chart=system.ram&after=-1&format=array&dimensions=free',
			units: 'MB',
			min: 0,
			max: 512,
			color: '#ff7ef8'
		},{
			title: 'IPv4 IN',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=received',
			units: 'Kbps',
			min: 0,
			max: 1000,
			color: '#a19dff'
		},{
			title: 'IPv4 OUT',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=sent',
			units: 'Kbps',
			min: 0,
			max: 1000,
			color: '#ff7e7d'
		},{
			title: 'Used DISK',
			url: 'api/v1/data?chart=disk_space._&after=-1&format=array&dimensions=used',
			units: 'GB',
			min: 0,
			max: 8,
			color: '#7dff7e'
		},{
			title: 'Disk READ',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=in&options=absolute',
			units: 'Kbps',
			min: 0,
			max: 100,
			color: '#a19dff'
		},{
			title: 'Disk WRITE',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=out&options=absolute',
			units: 'Kbps',
			min: 0,
			max: 100,
			color: '#ff7e7d'
		},{
			title: 'SWAP',
			url: 'api/v1/data?chart=system.swap&after=-1&format=array&dimensions=used',
			units: '%',
			min: 0,
			max: 100,
			color: '#7dff7e'
		}
	];
	
	var dflt = {
		angle: 0.35,
		lineWidth: 0.08,
		radiusScale: 1.0,
		pointer: {
			length: 1,
			strokeWidth: 0.05,
			color: '#e6e9ec'
		},
		limitMax: false,
		limitMin: false,
		strokeColor: '#edebeb',
		colorStart: '#fff',
		generateGradient: true,
		highDpiSupport: true
	};
	
	var animSpped = 200;
	
	for (var i = 0; i < gaugeList.length; i++) {
		$('body').append('<div class="gauge"><canvas id="gauge' + i + '"></canvas><span class="value" id="value' + i + '"></span><span class="title">' + gaugeList[i].title + '</span><span class="units">' + gaugeList[i].units + '</span>');
		eval('var gauge' + i + 'conf = Object.assign({}, {colorStop: "' + gaugeList[i].color + '"}, dflt);');
		eval('var gauge' + i + ' = new Donut(document.getElementById("gauge' + i + '")).setOptions(gauge' + i + 'conf);');
		eval('var textRenderer = new TextRenderer(document.getElementById("value' + i + '"));');
		eval('textRenderer.render = function(gauge' + i + ') {this.el.innerHTML = gauge' + i + '.displayedValue.toFixed(2);};');
		eval('gauge' + i + '.maxValue = ' + gaugeList[i].max + '; gauge' + i + '.setMinValue(' + gaugeList[i].min + '); gauge' + i + '.animationSpeed = ' + animSpped + '; gauge' + i + '.set(' + gaugeList[i].min + '); gauge' + i + '.setTextField(textRenderer);');
	};
	
	setInterval(function() {
		$.ajax({url: ip + gaugeList[0].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge0.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[1].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge1.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[2].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge2.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[3].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge3.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[4].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge4.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[5].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge5.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[6].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge6.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[7].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge7.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[8].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge8.set(Math.abs(json[0]).toFixed(2));}});
	}, 1000);
	
	/*
	var xhrList = [];
	
	setInterval(function() {
		for (var i = 0; i < gaugeList.length; i++) {
			xhrList.push($.ajax({
				url: ip + gaugeList[i].url,
				type: 'GET',
				cache: false,
				dataType: 'json'
			}));
		}
		$.when.apply($, xhrList ).done(function () {
			for ( var i = 0; i < arguments.length; i++ ) {
				if (arguments[i][0][0]) {
					//instance[i].refresh(Math.abs(arguments[i][0][0]));
					eval('gauge' + i + '.set(Math.abs(arguments[i][0][0]).toFixed(2));');
				} else {
					//instance[i].refresh(0);
					eval('gauge' + i + '.set(0);');
				}
			}
			xhrList.length = 0;
		});
	}, 1000);
	*/
});
</script>
</head>
<body>
</body>
</html>

gauge.js Gauge

参考: gauge.js
参考: GitHub - bernii/gauge.js

ソースコード

<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<style>
* {
	margin: 0;
	padding: 0;
}
html, body {
	font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
	font-style: normal;
	font-weight: normal;
}
.gauge {
	position: relative;
	width: 125px;
	height: 90px;
	float: left;
}
canvas {
	position: absolute;
	width: 125px;
	height: 90px;
}
.value {
	position: absolute;
	width: 100%;
	text-align: center;
	font-size: 14px;
	color: #999;
}
.title, .value, .units {
	position: absolute;
}
.title {
	width: 100%;
	text-align: center;
	margin-top: 3px;
	font-size: 11px;
	color: #999;
}
.value {
	width: 100%;
	text-align: center;
	margin-top: 30px;
	font-size: 15px;
	font-weight: bold;
	color: #fff;
	text-shadow:0px 1px 2px #666;
}
.units {
	left: 7px;
	bottom: 6px;
	font-size: 11px;
	color: #999;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="gauge.min.js"></script>
<script>
$(function() {
	if (typeof Object.assign != 'function') {
		Object.assign = function(target) {
			'use strict';
			if (target == null) {
				throw new TypeError('Cannot convert undefined or null to object');
			}
			
			target = Object(target);
			for (var index = 1; index < arguments.length; index++) {
				var source = arguments[index];
				if (source != null) {
					for (var key in source) {
						if (Object.prototype.hasOwnProperty.call(source, key)) {
							target[key] = source[key];
						}
					}
				}
			}
			return target;
		};
	}
	
	var ip = 'http://dz.plala.jp:19999/';
	
	var gaugeList = [
		{
			title: 'CPU',
			url: 'api/v1/data?chart=system.cpu&after=-1&format=array',
			units: '%',
			min: 0,
			max: 100,
			color: '[0.0, "#eaed73"],[0.50, "#f0f700"],[1.0, "#abb100"]'
		},{
			title: 'TEMP',
			url: 'api/v1/data?chart=sensors.temp_thermal_zone0_thermal_thermal_zone0&after=-1&format=array',
			units: '°C',
			min: 0,
			max: 85,
			color: '[0.0, "#ffc17e"],[0.50, "#ff890c"],[1.0, "#bf6300"]'
		},{
			title: 'Free RAM',
			url: 'api/v1/data?chart=system.ram&after=-1&format=array&dimensions=free',
			units: 'MB',
			min: 0,
			max: 512,
			color: '[0.0, "#ff7ef8"],[0.50, "#ff0cf1"],[1.0, "#bf00b2"]'
		},{
			title: 'IPv4 IN',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=received',
			units: 'Kbps',
			min: 0,
			max: 1000,
			color: '[0.0, "#a19dff"],[0.50, "#2b22ff"],[1.0, "#0800cf"]'
		},{
			title: 'IPv4 OUT',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=sent',
			units: 'Kbps',
			min: 0,
			max: 1000,
			color: '[0.0, "#ff7e7d"],[0.50, "#ff0c0c"],[1.0, "#bf0000"]'
		},{
			title: 'Used DISK',
			url: 'api/v1/data?chart=disk_space._&after=-1&format=array&dimensions=used',
			units: 'GB',
			min: 0,
			max: 8,
			color: '[0.0, "#7dff7e"],[0.50, "#0cff0c"],[1.0, "#00bf00"]'
		},{
			title: 'Disk READ',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=in&options=absolute',
			units: 'Kbps',
			min: 0,
			max: 100,
			color: '[0.0, "#a19dff"],[0.50, "#2b22ff"],[1.0, "#0800cf"]'
		},{
			title: 'Disk WRITE',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=out&options=absolute',
			units: 'Kbps',
			min: 0,
			max: 100,
			color: '[0.0, "#ff7e7d"],[0.50, "#ff0c0c"],[1.0, "#bf0000"]'
		},{
			title: 'SWAP',
			url: 'api/v1/data?chart=system.swap&after=-1&format=array&dimensions=used',
			units: '%',
			min: 0,
			max: 100,
			color: '[0.0, "#7dff7e"],[0.50, "#0cff0c"],[1.0, "#00bf00"]'
		}
	];
	
	var dflt = {
		angle: 0.12,
		lineWidth: 0.45,
		radiusScale: 0.8,
		pointer: {
			length: 0.7,
			strokeWidth: 0.04,
			color: '#c0c0c0'
		},
		limitMax: false,
		limitMin: false,
		strokeColor: '#edebeb',
		generateGradient: true,
		highDpiSupport: true
	};
	
	var animSpped = 200;
	
	for (var i = 0; i < gaugeList.length; i++) {
		$('body').append('<div class="gauge"><canvas id="gauge' + i + '"></canvas><span class="value" id="value' + i + '"></span><span class="title">' + gaugeList[i].title + '</span><span class="units">' + gaugeList[i].units + '</span>');
		eval('var gauge' + i + 'conf = Object.assign({}, {percentColors: [' + gaugeList[i].color + ']}, dflt);');
		eval('var gauge' + i + ' = new Gauge(document.getElementById("gauge' + i + '")).setOptions(gauge' + i + 'conf);');
		eval('var textRenderer = new TextRenderer(document.getElementById("value' + i + '"));');
		eval('textRenderer.render = function(gauge' + i + ') {this.el.innerHTML = gauge' + i + '.displayedValue.toFixed(2);};');
		eval('gauge' + i + '.maxValue = ' + gaugeList[i].max + '; gauge' + i + '.setMinValue(' + gaugeList[i].min + '); gauge' + i + '.animationSpeed = ' + animSpped + '; gauge' + i + '.set(' + gaugeList[i].min + '); gauge' + i + '.setTextField(textRenderer);');
	};
	
	setInterval(function() {
		$.ajax({url: ip + gaugeList[0].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge0.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[1].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge1.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[2].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge2.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[3].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge3.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[4].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge4.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[5].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge5.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[6].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge6.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[7].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge7.set(Math.abs(json[0]).toFixed(2));}});
		$.ajax({url: ip + gaugeList[8].url, type: 'GET', cache: false, dataType: 'json', success: function(json) {gauge8.set(Math.abs(json[0]).toFixed(2));}});
	}, 1000);
	
	/*
	var xhrList = [];
	
	setInterval(function() {
		for (var i = 0; i < gaugeList.length; i++) {
			xhrList.push($.ajax({
				url: ip + gaugeList[i].url,
				type: 'GET',
				cache: false,
				dataType: 'json'
			}));
		}
		$.when.apply($, xhrList ).done(function () {
			for ( var i = 0; i < arguments.length; i++ ) {
				if (arguments[i][0][0]) {
					//instance[i].refresh(Math.abs(arguments[i][0][0]));
					eval('gauge' + i + '.set(Math.abs(arguments[i][0][0]).toFixed(2));');
				} else {
					//instance[i].refresh(0);
					eval('gauge' + i + '.set(0);');
				}
			}
			xhrList.length = 0;
		});
	}, 1000);
	*/
});
</script>
</head>
<body>
</body>
</html>

Canvas Gauges

参考: Canvas Gauges
参考: GitHub - Mikhus/canvas-gauges

ソースコード

<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<style>
* {
	margin: 0;
	padding: 0;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="gauge.min_214.js"></script>
<script>
$(function() {
	if (!String.prototype.repeat) {
		String.prototype.repeat = function(count) {
			'use strict';
			if (this == null) {
				throw new TypeError('can\'t convert ' + this + ' to object');
			}
			var str = '' + this;
			count = +count;
			if (count != count) {
				count = 0;
			}
			if (count < 0) {
				throw new RangeError('repeat count must be non-negative');
			}
			if (count == Infinity) {
				throw new RangeError('repeat count must be less than infinity');
			}
			count = Math.floor(count);
			if (str.length == 0 || count == 0) {
				return '';
			}
			// Ensuring count is a 31-bit integer allows us to heavily optimize the
			// main part. But anyway, most current (August 2014) browsers can't handle
			// strings 1 << 28 chars or longer, so:
			if (str.length * count >= 1 << 28) {
				throw new RangeError('repeat count must not overflow maximum string size');
			}
			var rpt = '';
			for (;;) {
				if ((count & 1) == 1) {
					rpt += str;
				}
				count >>>= 1;
				if (count == 0) {
					break;
				}
				str += str;
			}
			// Could we try:
			// return Array(count + 1).join(this);
			return rpt;
		}
	}
	
	if (typeof Object.assign != 'function') {
		Object.assign = function(target) {
			'use strict';
			if (target == null) {
				throw new TypeError('Cannot convert undefined or null to object');
			}
			
			target = Object(target);
			for (var index = 1; index < arguments.length; index++) {
				var source = arguments[index];
				if (source != null) {
					for (var key in source) {
						if (Object.prototype.hasOwnProperty.call(source, key)) {
							target[key] = source[key];
						}
					}
				}
			}
			return target;
		};
	}
	
	var ip = 'http://dz.plala.jp:19999/';
	
	var gaugeList = [
		{
			title: 'CPU',
			url: 'api/v1/data?chart=system.cpu&after=-1&format=array',
			units: '%',
			minValue: 0,
			maxValue: 100,
			majorTicks: '"0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100"',
			minorTicks: 2,
			highlightsLow: 'from: 60, to: 80, color: "rgba(243, 239, 118, 1)"',
			highlightsHigh: 'from: 80, to: 100, color: "rgba(242, 166, 138, 1)"'
		},{
			title: 'TEMP',
			url: 'api/v1/data?chart=sensors.temp_thermal_zone0_thermal_thermal_zone0&after=-1&format=array',
			units: '°C',
			minValue: 0,
			maxValue: 80,
			majorTicks: '"0", "10", "20", "30", "40", "50", "60", "70", "80"',
			minorTicks: 2,
			highlightsLow: 'from: 60, to: 70, color: "rgba(243, 239, 118, 1)"',
			highlightsHigh: 'from: 70, to: 80, color: "rgba(242, 166, 138, 1)"'
		},{
			title: 'Free RAM',
			url: 'api/v1/data?chart=system.ram&after=-1&format=array&dimensions=free',
			units: 'MB',
			minValue: 0,
			maxValue: 512,
			majorTicks: '"0", "256", "512"',
			minorTicks: 2,
			highlightsLow: 'from: 128, to: 256, color: "rgba(243, 239, 118, 1)"',
			highlightsHigh: 'from: 0, to: 128, color: "rgba(242, 166, 138, 1)"'
		},{
			title: 'IPv4 IN',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=received',
			units: 'Kbps',
			minValue: 0,
			maxValue: 1000,
			majorTicks: '"0", "200", "400", "600", "800", "1000"',
			minorTicks: 2,
			highlightsLow: 'from: 600, to: 800, color: "rgba(243, 239, 118, 1)"',
			highlightsHigh: 'from: 800, to: 1000, color: "rgba(242, 166, 138, 1)"'
		},{
			title: 'IPv4 OUT',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=sent',
			units: 'Kbps',
			minValue: 0,
			maxValue: 1000,
			majorTicks: '"0", "200", "400", "600", "800", "1000"',
			minorTicks: 2,
			highlightsLow: 'from: 600, to: 800, color: "rgba(243, 239, 118, 1)"',
			highlightsHigh: 'from: 800, to: 1000, color: "rgba(242, 166, 138, 1)"'
		},{
			title: 'Used DISK',
			url: 'api/v1/data?chart=disk_space._&after=-1&format=array&dimensions=used',
			units: 'GB',
			minValue: 0,
			maxValue: 8,
			majorTicks: '"0", "1", "2", "3", "4", "5", "6", "7", "8"',
			minorTicks: 2,
			highlightsLow: 'from: 4, to: 6, color: "rgba(243, 239, 118, 1)"',
			highlightsHigh: 'from: 6, to: 8, color: "rgba(242, 166, 138, 1)"'
		},{
			title: 'Disk READ',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=in&options=absolute',
			units: 'Kbps',
			minValue: 0,
			maxValue: 100,
			majorTicks: '"0", "20", "40", "60", "80", "100"',
			minorTicks: 2,
			highlightsLow: 'from: 60, to: 80, color: "rgba(243, 239, 118, 1)"',
			highlightsHigh: 'from: 80, to: 100, color: "rgba(242, 166, 138, 1)"'
		},{
			title: 'Disk WRITE',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=out&options=absolute',
			units: 'Kbps',
			minValue: 0,
			maxValue: 100,
			majorTicks: '"0", "20", "40", "60", "80", "100"',
			minorTicks: 2,
			highlightsLow: 'from: 60, to: 80, color: "rgba(243, 239, 118, 1)"',
			highlightsHigh: 'from: 80, to: 100, color: "rgba(242, 166, 138, 1)"'
		},{
			title: 'SWAP',
			url: 'api/v1/data?chart=system.swap&after=-1&format=array&dimensions=used',
			units: '%',
			minValue: 0,
			maxValue: 100,
			majorTicks: '"0", "25", "50", "75", "100"',
			minorTicks: 2,
			highlightsLow: 'from: 50, to: 75, color: "rgba(243, 239, 118, 1)"',
			highlightsHigh: 'from: 75, to: 100, color: "rgba(242, 166, 138, 1)"'
		}
	];
	
	var defaultSetting = {
		width: 120,
		height: 120,
		value: 0,
		valueInt: 0,
		valueDec: 2,
		colorNeedle: 'rgba(227, 33, 33, 1)',
		colorNeedleEnd: 'rgba(255, 119, 119, 1)',
		colorNeedleCircleOuter: '#fff',
		colorNeedleCircleOuterEnd: '#ccc',
		colorNeedleCircleInner: '#ccc',
		colorNeedleCircleInnerEnd: '#fff',
		colorNeedleShadowUp: 'rgba(0, 0, 0, 0)',
		colorNeedleShadowDown: 'rgba(0, 0, 0, 0.3)',
		colorValueBoxRect: '#fff',
		colorValueBoxRectEnd: '#eee',
		colorValueBoxBackground: '#ddd',
		colorValueBoxShadow: 'rgba(0, 0, 0, 0.5)',
		colorValueText: '#444',
		colorValueTextShadow: 'rgba(0, 0, 0, 0.3)',
		colorBorderShadow: 'rgba(0, 0, 0, 0.5)',
		colorBorderOuter: '#ccc',
		colorBorderOuterEnd: '#ccc',
		colorBorderMiddle: '#ccc',
		colorBorderMiddleEnd: '#ccc',
		borderOuterWidth: 0,
		borderMiddleWidth: 0,
		borderInnerWidth: 0,
		colorPlate: '#fafafa',
		colorMajorTicks: '#444',
		colorMinorTicks: '#444',
		colorTitle: '#444',
		colorUnits: '#444',
		colorNumbers: '#444',
		fontNumbersSize: '28',
		fontTitleWeight: 'bold',
		fontTitleSize: '30',
		fontUnitsSize: '30',
		fontValueSize: '30',
		fontValueWeight: 'lighter',
		highlightsWidth: 12,
		numbersMargin: -6
	};
	var customAnimation = {
		animationRule: 'elastic',
		animationDuration: 1000,
		animatedValue: true
	};
	
	for (var i = 0; i < gaugeList.length; i++) {
		eval('var gauge' + i + 'Conf = Object.assign({}, {renderTo: "gauge' + i + '", title: "' + gaugeList[i].title + '",	units: "'+ gaugeList[i].units + '", minValue: ' + gaugeList[i].minValue + ', maxValue: ' + gaugeList[i].maxValue + ', majorTicks: [' + gaugeList[i].majorTicks + '], minorTicks: ' + gaugeList[i].minorTicks + ', highlights: [{' + gaugeList[i].highlightsLow + '},{' + gaugeList[i].highlightsHigh + '}]}, customAnimation, defaultSetting);');
		$('body').append('<canvas id="gauge' + i + '"></canvas>');
		eval('var gauge' + i + ' = new RadialGauge(gauge' + i + 'Conf)');
		eval('gauge' + i + '.draw()');
	};
	
	var xhrList = [];
	
	setInterval(function() {
		for (var i = 0; i < gaugeList.length; i++) {
			xhrList.push($.ajax({
				url: ip + gaugeList[i].url,
				type: 'GET',
				cache: false,
				dataType: 'json'
			}));
		}
		$.when.apply($, xhrList ).done(function () {
			for ( var i = 0; i < arguments.length; i++ ) {
				if (arguments[i][0][0]) {
					eval('gauge' + i + '.value = (Math.abs(arguments[i][0][0]))');
				} else {
					eval('gauge' + i + '.value = (0)');
				}
			}
			xhrList.length = 0;
		});
	}, 1000);
});
</script> 
</head>
<body>
</body>
</html>

Google Charts

参考: Google Charts - Visualization: Gauge

ソースコード

<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<style>
.gauge {
	float: left;
}
.gauge svg g text {
	font-size: 8px;
}
.gauge svg g g text {
	font-size: 11px;
}
</style>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
	var ip = 'http://dz.plala.jp:19999/';
	
	var gaugeList = [
		{
			title: 'CPU',
			url: 'api/v1/data?chart=system.cpu&after=-1&format=array',
			units: '%',
			max: 100,
			majorTicks: '"0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100"',
			minorTicks: 2,
			yellowFrom: 60,
			yellowTo: 80,
			redFrom: 80,
			redTo: 100
		},{
			title: 'TEMP',
			url: 'api/v1/data?chart=sensors.temp_thermal_zone0_thermal_thermal_zone0&after=-1&format=array',
			units: '°C',
			max: 80,
			majorTicks: '"0", "10", "20", "30", "40", "50", "60", "70", "80"',
			minorTicks: 2,
			yellowFrom: 60,
			yellowTo: 70,
			redFrom: 70,
			redTo: 80
		},{
			title: 'Free RAM',
			url: 'api/v1/data?chart=system.ram&after=-1&format=array&dimensions=free',
			units: 'MB',
			max: 512,
			majorTicks: '"0", "256", "512"',
			minorTicks: 2,
			yellowFrom: 128,
			yellowTo: 256,
			redFrom: 0,
			redTo: 128
		},{
			title: 'IPv4 IN',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=received',
			units: 'Kbps',
			max: 1000,
			majorTicks: '"0", "200", "400", "600", "800", "1000"',
			minorTicks: 2,
			yellowFrom: 600,
			yellowTo: 800,
			redFrom: 800,
			redTo: 1000
		},{
			title: 'IPv4 OUT',
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=sent',
			units: 'Kbps',
			max: 1000,
			majorTicks: '"0", "200", "400", "600", "800", "1000"',
			minorTicks: 2,
			yellowFrom: 600,
			yellowTo: 800,
			redFrom: 800,
			redTo: 1000
		},{
			title: 'Used DISK',
			url: 'api/v1/data?chart=disk_space._&after=-1&format=array&dimensions=used',
			units: 'GB',
			max: 8,
			majorTicks: '"0", "1", "2", "3", "4", "5", "6", "7", "8"',
			minorTicks: 2,
			yellowFrom: 4,
			yellowTo: 6,
			redFrom: 6,
			redTo: 8
		},{
			title: 'Disk READ',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=in&options=absolute',
			units: 'Kbps',
			max: 100,
			majorTicks: '"0", "20", "40", "60", "80", "100"',
			minorTicks: 2,
			yellowFrom: 60,
			yellowTo: 80,
			redFrom: 80,
			redTo: 100
		},{
			title: 'Disk WRITE',
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=out&options=absolute',
			units: 'Kbps',
			max: 100,
			majorTicks: '"0", "20", "40", "60", "80", "100"',
			minorTicks: 2,
			yellowFrom: 60,
			yellowTo: 80,
			redFrom: 80,
			redTo: 100
		},{
			title: 'SWAP',
			url: 'api/v1/data?chart=system.swap&after=-1&format=array&dimensions=used',
			units: '%',
			max: 100,
			majorTicks: '"0", "25", "50", "75", "100"',
			minorTicks: 2,
			yellowFrom: 50,
			yellowTo: 75,
			redFrom: 75,
			redTo: 100
		}
	];
	
	var gaugeWidth = 120;
	var gaugeHeight = 120;
	
	google.charts.load('current', {'packages': ['gauge']});
	
	for (var i = 0; i < gaugeList.length; i++) {
		$('body').append('<div id="chart' + i + '" class="gauge" style="width: ' + gaugeWidth + '; height: ' + gaugeHeight + ';"></div>');
		eval('function drawChart' + i + '() {var data = google.visualization.arrayToDataTable([["Label", "Value"],["' + gaugeList[i].title + '", 0]]); var options = {animation: {duration: 1000, easing: "linear",}, width: ' + gaugeWidth + ',	height: ' + gaugeHeight + ', max: ' + gaugeList[i].max + ', majorTicks : [' + gaugeList[i].majorTicks + '] ,minorTicks: ' + gaugeList[i].minorTicks + ', yellowFrom: ' + gaugeList[i].yellowFrom + ', yellowTo: ' + gaugeList[i].yellowTo + ', redFrom: ' + gaugeList[i].redFrom + ', redTo: ' + gaugeList[i].redTo + '}; var chart = new google.visualization.Gauge(document.getElementById("chart' + i + '")); chart.draw(data, options); setInterval(function() {$.ajax({url: "' + ip + gaugeList[i].url + '", type: "GET", cache: false, dataType: "json", success: function(json) {data.setValue(0, 1, Math.abs(json[0]).toFixed(2)); var formatter = new google.visualization.NumberFormat({suffix: "' + gaugeList[i].units + '",pattern:"#"}); formatter.format(data,1); chart.draw(data, options);}});}, 1000);}');
		eval('google.charts.setOnLoadCallback(drawChart' + i + ');');
	};
});
</script>
</head>
<body>
</body>
</html>

複数台を集約

参考: Replication Overview · firehol/netdata Wiki · GitHub
参考: Monitoring ephemeral nodes · firehol/netdata Wiki · GitHub
参考: netdata のレプリケーション設定(stream) - biaxident’s blog

マスターの設定

マスターにするRaspberryPiにホスト名IP固定NetDataのインストールnginxのインストール

$ sudo leafpad /etc/netdata/stream.conf

末尾に追記

[11111111-2222-3333-4444-555555555555]
enabled = yes
default history = 600
default memory = ram
health enabled by default = auto

再起動

$ sudo reboot

スレーブの設定

スレーブが複数台なら複数台に同じ設定でOK
スレーブにするRaspberryPiにホスト名NetDataのインストールnginxのインストール

$ sudo leafpad /etc/netdata/stream.conf

末尾に追記(destinationはマスターの固定IPアドレス)

[stream]
enabled = yes
destination = 192.168.1.187:19999
api key = 11111111-2222-3333-4444-555555555555
$ sudo leafpad /etc/netdata/netdata.conf

書き換え

[global]
    history = 300
    update every = 1
    memory mode = none
    debug log = none
    error log = none
    access log = none
[web]
    enable gzip compression = no
    gzip compression level = 1
    mode = none
[health]
    enabled = no

再起動

$ sudo reboot

確認
マスターのIPアドレス

http://192.168.1.187

左上から機器の切り替え

IoT Monitor

Iot-monitor.jpg

主電源供給維持活動限界監視システム

ソースコード

<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Expires" content="0">
<link rel="apple-touch-icon" sizes="180x180" href="favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon/favicon-16x16.png">
<link rel="manifest" href="favicon/manifest.json">
<link rel="mask-icon" href="favicon/safari-pinned-tab.svg" color="#5bbad5">
<link rel="shortcut icon" href="favicon/favicon.ico">
<meta name="msapplication-config" content="favicon/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<title>主電源供給維持活動限界監視システム</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style>
@font-face {
 font-family: 'AyatoNumA_eot';
 src: url('AyatoNumA.eot');
}
@font-face {
 font-family: 'AyatoNumA_ttf';
 src: url('AyatoNumA.ttf') format("truetype");
}
* {
	margin: 0;
	padding: 0;
}
html, body {
	font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
	height: 100%;
	background-color: #222;
	color: #fff;
	text-align: center;
}
a, a:focus, a:hover {
	outline: none;
}
.close {
	font-size: 26px;
}
.clear {
	clear: both;
}
.dummy {
	height: 60px;
}
.navbar-nav > li > a:hover, .navbar-nav > li > a:focus {
	background-color: rgba(0,0,0,0);
	color: #fff;
}
.navbar-nav>li>a, .navbar-brand, .navbar-inverse .navbar-brand:focus, .navbar-inverse .navbar-brand:hover {
	color: #fff !important;
    padding-top: 18px !important; 
    padding-bottom: 0 !important;
    height: 50px;
}
.navbar {
	min-height:28px !important;
	margin-right: auto;
	margin-left: auto;
	background-color: #222;
	border-bottom: 1px solid #ebd311;
}
.nav {
	float: right;
	font-style: normal;
	font-size: 12px;
}
.navbar-brand {
	float: left;
	font-style: normal;
	font-size: 12px;
}
.wrapper {
	display: table;
	width: 100%;
	height: 100%;
	min-height: 100%;
	opacity: 0;
}
.wrapper-inner {
	display: table-cell;
	vertical-align: middle;
}
.wrapper-container, .container {
	width: 945px;
}

.wrapper-container {
	margin-right: auto;
	margin-left: auto;
}
.gauge-container {
	width: 105px;
	height: 105px;
	float: left;
}
.gauge {
	position: relative;
	width: 105px;
	height: 105px;
}
canvas {
	position: absolute;
	top: 0;
	left: 0;
}
.title, .label, .units {
	position: absolute;
	left: 0;
	right: 0;
}
.title {
	top: 26px;
	font-size: 11px;
	color: #444;
}
.label {
	top: 40px;
	font-family: 'AyatoNumA_eot', 'AyatoNumA_ttf';
	font-style: normal;
	font-weight: normal;
	font-size: 20px;
	color: #ebd311;
}
.units {
	top: 66px;
	font-size: 11px;
	color: #444;
}
.alert {
	position: absolute;
	left: 9px;
	top: 9px;
	width: 88px;
	height: 88px;
	opacity: 0;
	-webkit-border-radius: 50%;
	-moz-border-radius: 50%;
	border-radius: 50%;
	background-color: red;
}
.hard {
	float: left;
	margin-left: 5px;
	font-family: "游明朝", YuMincho, "ヒラギノ明朝 ProN W3", "Hiragino Mincho ProN", "HG明朝E", "MS P明朝", "MS 明朝", serif;
	font-style: normal;
	font-weight: bold;
	font-size: 22px;
	color: #ebd311;
}
.hardSub {
	float: left;
	margin-left: 5px;
	padding-top: 20px;
	font-style: normal;
	font-size: 14px;
	color: #444;
}
.modal-content {
	font-size: 12px;
}
.modal-header {
	border-bottom: 0px solid #e5e5e5;
	padding-bottom: 0px;
}
.modal-body {
	padding: 0 20px 20px 20px;
	text-align: left;
	color: #000;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-color/2.1.2/jquery.color.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bowser/1.7.0/bowser.min.js"></script>
<script src="jquery.easypiechart.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script>
$(function() {
	var ipList = [
		'http://dz.plala.jp:19999/',
		'http://dz.plala.jp:19999/host/seti/',
		'http://dz.plala.jp:19999/host/fr24/',
		'http://dz.plala.jp:19999/host/livebot3/'
	];
	
	var gaugeList = [
		{
			title: 'CPU',
			ip: ipList[0],
			url: 'api/v1/data?chart=system.cpu&after=-1&format=array',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'TEMP',
			ip: ipList[0],
			url: 'api/v1/data?chart=sensors.temp_thermal_zone0_thermal_thermal_zone0&after=-1&format=array',
			max: 85,
			redFrom: 70,
			redTo: 80
		},{
			title: 'Free RAM',
			ip: ipList[0],
			url: 'api/v1/data?chart=system.ram&after=-1&format=array&dimensions=free',
			max: 1024,
			redFrom: 0,
			redTo: 256
		},{
			title: 'IPv4 IN',
			ip: ipList[0],
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=received',
			max: 100000,
			redFrom: 80000,
			redTo: 100000
		},{
			title: 'IPv4 OUT',
			ip: ipList[0],
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=sent',
			max: 100000,
			redFrom: 80000,
			redTo: 100000
		},{
			title: 'Used DISK',
			ip: ipList[0],
			url: 'api/v1/data?chart=disk_space._&after=-1&format=array&dimensions=used',
			max: 8,
			redFrom: 6,
			redTo: 8
		},{
			title: 'Disk READ',
			ip: ipList[0],
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=in&options=absolute',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'Disk WRITE',
			ip: ipList[0],
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=out&options=absolute',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'SWAP',
			ip: ipList[0],
			url: 'api/v1/data?chart=system.swap&after=-1&format=array&dimensions=used',
			max: 100,
			redFrom: 10,
			redTo: 100
		},{
			title: 'CPU',
			ip: ipList[1],
			url: 'api/v1/data?chart=system.cpu&after=-1&format=array',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'TEMP',
			ip: ipList[1],
			url: 'api/v1/data?chart=sensors.temp_thermal_zone0_thermal_thermal_zone0&after=-1&format=array',
			max: 85,
			redFrom: 70,
			redTo: 80
		},{
			title: 'Free RAM',
			ip: ipList[1],
			url: 'api/v1/data?chart=system.ram&after=-1&format=array&dimensions=free',
			max: 1024,
			redFrom: 0,
			redTo: 256
		},{
			title: 'IPv4 IN',
			ip: ipList[1],
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=received',
			max: 100000,
			redFrom: 80000,
			redTo: 100000
		},{
			title: 'IPv4 OUT',
			ip: ipList[1],
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=sent',
			max: 100000,
			redFrom: 80000,
			redTo: 100000
		},{
			title: 'Used DISK',
			ip: ipList[1],
			url: 'api/v1/data?chart=disk_space._&after=-1&format=array&dimensions=used',
			max: 8,
			redFrom: 6,
			redTo: 8
		},{
			title: 'Disk READ',
			ip: ipList[1],
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=in&options=absolute',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'Disk WRITE',
			ip: ipList[1],
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=out&options=absolute',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'SWAP',
			ip: ipList[1],
			url: 'api/v1/data?chart=system.swap&after=-1&format=array&dimensions=used',
			max: 100,
			redFrom: 10,
			redTo: 100
		},{
			title: 'CPU',
			ip: ipList[2],
			url: 'api/v1/data?chart=system.cpu&after=-1&format=array',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'TEMP',
			ip: ipList[2],
			url: 'api/v1/data?chart=sensors.temp_thermal_zone0_thermal_thermal_zone0&after=-1&format=array',
			max: 85,
			redFrom: 70,
			redTo: 80
		},{
			title: 'Free RAM',
			ip: ipList[2],
			url: 'api/v1/data?chart=system.ram&after=-1&format=array&dimensions=free',
			max: 512,
			redFrom: 0,
			redTo: 128
		},{
			title: 'IPv4 IN',
			ip: ipList[2],
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=received',
			max: 100000,
			redFrom: 80000,
			redTo: 100000
		},{
			title: 'IPv4 OUT',
			ip: ipList[2],
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=sent',
			max: 100000,
			redFrom: 80000,
			redTo: 100000
		},{
			title: 'Used DISK',
			ip: ipList[2],
			url: 'api/v1/data?chart=disk_space._&after=-1&format=array&dimensions=used',
			max: 8,
			redFrom: 6,
			redTo: 8
		},{
			title: 'Disk READ',
			ip: ipList[2],
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=in&options=absolute',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'Disk WRITE',
			ip: ipList[2],
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=out&options=absolute',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'SWAP',
			ip: ipList[2],
			url: 'api/v1/data?chart=system.swap&after=-1&format=array&dimensions=used',
			max: 100,
			redFrom: 10,
			redTo: 100
		},{
			title: 'CPU',
			ip: ipList[3],
			url: 'api/v1/data?chart=system.cpu&after=-1&format=array',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'TEMP',
			ip: ipList[3],
			url: 'api/v1/data?chart=sensors.temp_thermal_zone0_thermal_thermal_zone0&after=-1&format=array',
			max: 85,
			redFrom: 70,
			redTo: 80
		},{
			title: 'Free RAM',
			ip: ipList[3],
			url: 'api/v1/data?chart=system.ram&after=-1&format=array&dimensions=free',
			max: 1024,
			redFrom: 0,
			redTo: 256
		},{
			title: 'IPv4 IN',
			ip: ipList[3],
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=received',
			max: 100000,
			redFrom: 80000,
			redTo: 100000
		},{
			title: 'IPv4 OUT',
			ip: ipList[3],
			url: 'api/v1/data?chart=system.ipv4&after=-1&format=array&dimensions=sent',
			max: 100000,
			redFrom: 80000,
			redTo: 100000
		},{
			title: 'Used DISK',
			ip: ipList[3],
			url: 'api/v1/data?chart=disk_space._&after=-1&format=array&dimensions=used',
			max: 8,
			redFrom: 6,
			redTo: 8
		},{
			title: 'Disk READ',
			ip: ipList[3],
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=in&options=absolute',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'Disk WRITE',
			ip: ipList[3],
			url: 'api/v1/data?chart=system.io&after=-1&format=array&dimensions=out&options=absolute',
			max: 100,
			redFrom: 80,
			redTo: 100
		},{
			title: 'SWAP',
			ip: ipList[3],
			url: 'api/v1/data?chart=system.swap&after=-1&format=array&dimensions=used',
			max: 100,
			redFrom: 10,
			redTo: 100
		}
	];
	
	var xhrList = [];
	
	for (var i = 0; i < gaugeList.length; i++) {
		$('#gauge' + i).easyPieChart({
			size: 105,
			maxValue: gaugeList[i].max,
			barColor: '#ebd311',
			trackColor: '#444',
			scaleColor: 'rgba(0,0,0,0)',
			lineWidth: 2,
			onStep: function(from, to, currentValue) {
				$(this.el).find('.label').text(currentValue.toFixed(2));
			}
		});
	}
	
	setInterval(function() {
		for (var i = 0; i < gaugeList.length; i++) {
			xhrList.push($.ajax({
				url: gaugeList[i].ip + gaugeList[i].url,
				type: 'GET',
				cache: false,
				dataType: 'json',
				timeout: 4000
			}));
		}
		$.when.apply($, xhrList ).done(function () {
			for ( var i = 0; i < arguments.length; i++ ) {
				if (arguments[i][0][0]) {
					arg = Math.abs(arguments[i][0][0]);
					$('#gauge' + i).data('easyPieChart').update(arg);
					if ((i == 7 || i == 16 || i == 25 || i == 34) && arg > 100) {
						//only Disk WRITE
						$('#gauge' + i + ' .label, #gauge' + i + ' .title, #gauge' + i + ' .units').animate({'color': 'red'}, 500 );
						$('#gauge' + i + ' .alert').animate({'opacity': '0.2'}, 500 );
						$('#gauge' + i).data('easyPieChart').options.barColor = 'red';
					} else if (arg > gaugeList[i].redFrom && arg <= gaugeList[i].redTo ) {
						$('#gauge' + i + ' .label, #gauge' + i + ' .title, #gauge' + i + ' .units').animate({'color': 'red'}, 500 );
						$('#gauge' + i + ' .alert').animate({'opacity': '0.2'}, 500 );
						$('#gauge' + i).data('easyPieChart').options.barColor = 'red';
					} else {
						$('#gauge' + i + ' .title, #gauge' + i + ' .units').animate({'color': '#444'}, 500 );
						$('#gauge' + i + ' .label').animate({'color': '#ebd311'}, 500 );
						$('#gauge' + i + ' .alert').animate({'opacity': '0'}, 500 );
						$('#gauge' + i).data('easyPieChart').options.barColor = '#ebd311';
					}
				} else {
					$('#gauge' + i).data('easyPieChart').update(0);
					$('#gauge' + i + ' .title, #gauge' + i + ' .units').animate({'color': '#444'}, 500 );
					$('#gauge' + i + ' .label').animate({'color': '#ebd311'}, 500 );
					$('#gauge' + i + ' .alert').animate({'opacity': '0'}, 500 );
					$('#gauge' + i).data('easyPieChart').options.barColor = '#ebd311';
				}
			}
			xhrList.length = 0;
		});
	}, 1000);
	
	function resize() {
		windowWidth = $(window).innerWidth(), windowHeight = $(window).innerHeight();
		if (bowser.mobile) {
			if (windowWidth < windowHeight) {
				split = 3;
				regulation = 0;
			} else {
				split = 9;
				regulation = 20;
			}
		} else if (bowser.tablet) {
			if (windowWidth < windowHeight) {
				split = 3;
				regulation = 0;
			} else {
				split = 9;
				regulation = 20;
			}
		} else {
				split = 9;
			if (bowser.msie) {
				regulation = 15;
			} else {
				regulation = 20;
			}
		}
		$(".wrapper-container, .container").css("width", $(window).innerWidth() * 0.9);
		conatinerWidth = $(".wrapper-container").width();
		$(".gauge-container, .gauge, canvas").css({"width": Math.floor(conatinerWidth / split), "height": Math.floor(conatinerWidth / split)});
		$(".hard").css("font-size", Math.floor(30 * conatinerWidth / split / 105));
		$(".hardSub").css({"font-size": Math.floor(14 * conatinerWidth / split / 105), "padding-top": Math.floor(regulation * conatinerWidth / split / 105)});
		$(".title").css({"font-size": Math.floor(11 * conatinerWidth / split / 105), "top": Math.floor(26 * conatinerWidth / split / 105)});
		$(".label").css({"font-size": Math.floor(20 * conatinerWidth / split / 105), "top": Math.floor(40 * conatinerWidth / split / 105)});
		$(".units").css({"font-size": Math.floor(11 * conatinerWidth / split / 105), "top": Math.floor(66 * conatinerWidth / split / 105)});
		$(".alert").css({"left": Math.floor(9 * conatinerWidth / split / 105), "top": Math.floor(9 * conatinerWidth / split / 105), "width": Math.floor(88 * conatinerWidth / split / 105), "height": Math.floor(88 * conatinerWidth / split / 105)});
	};
	
	resize();
	$(window).on('resize', function(){
		resize();
	});
	
	$(".wrapper").css("opacity", "1.0");
});
</script>
</head>
<body>
<div class="wrapper">
	<div class="wrapper-inner">
		<div class="wrapper-container">
			<nav class="navbar navbar-fixed-top">
				<div class="container">
					<div class="navbar-header">
						<div class="navbar-brand">内部主電源供給維持活動限界IoT監視システム</div>
					</div>
					<div id="navbar" class="collapse navbar-collapse">
						<ul class="nav navbar-nav">
							<li><a href="#" data-toggle="modal" data-target="#myModal">FEATURES</a></li>
							<li><a href="http://dz.plala.jp/wiki/index.php/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8" target="_blank">WIKI</a></li>
						</ul>
					</div>
				</div>
			</nav>
			<div class="dummy"></div>
			<div>
				<div class="hard">指令本部中央制御装置</div>
				<div class="hardSub">Raspberry Pi 2 Model B v1.1 a01041 (Sony, UK)</div>
			</div>
			<div class="clear"></div>
			<div class="gauge-container">
				<div id="gauge0" class="gauge"> <span class="title">CPU</span> <span class="label"></span> <span class="units">%</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge1" class="gauge"> <span class="title">TEMP</span> <span class="label"></span> <span class="units">°C</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge2" class="gauge"> <span class="title">Free RAM</span> <span class="label"></span> <span class="units">MB</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge3" class="gauge"> <span class="title">IPv4 IN</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge4" class="gauge"> <span class="title">IPv4 OUT</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge5" class="gauge"> <span class="title">Used DISK</span> <span class="label"></span> <span class="units">GB</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge6" class="gauge"> <span class="title">Disk READ</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge7" class="gauge"> <span class="title">Disk WRITE</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge8" class="gauge"> <span class="title">SWAP</span> <span class="label"></span> <span class="units">%</span> <span class="alert"></span></div>
			</div>
			<div class="clear"></div>
			<div>
				<div class="hard">地球外知的生命体探査装置</div>
				<div class="hardSub">Raspberry Pi 2 Model B v1.1 a01041 (Sony, UK)</div>
			</div>
			<div class="clear"></div>
			<div class="gauge-container">
				<div id="gauge9" class="gauge"> <span class="title">CPU</span> <span class="label"></span> <span class="units">%</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge10" class="gauge"> <span class="title">TEMP</span> <span class="label"></span> <span class="units">°C</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge11" class="gauge"> <span class="title">Free RAM</span> <span class="label"></span> <span class="units">MB</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge12" class="gauge"> <span class="title">IPv4 IN</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge13" class="gauge"> <span class="title">IPv4 OUT</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge14" class="gauge"> <span class="title">Used DISK</span> <span class="label"></span> <span class="units">GB</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge15" class="gauge"> <span class="title">Disk READ</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge16" class="gauge"> <span class="title">Disk WRITE</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge17" class="gauge"> <span class="title">SWAP</span> <span class="label"></span> <span class="units">%</span> <span class="alert"></span></div>
			</div>
			<div class="clear"></div>
			<div>
				<div class="hard">航空機追跡異常事態検出装置</div>
				<div class="hardSub">Raspberry Pi 1 Model B+ 0010</div>
			</div>
			<div class="clear"></div>
			<div class="gauge-container">
				<div id="gauge18" class="gauge"> <span class="title">CPU</span> <span class="label"></span> <span class="units">%</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge19" class="gauge"> <span class="title">TEMP</span> <span class="label"></span> <span class="units">°C</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge20" class="gauge"> <span class="title">Free RAM</span> <span class="label"></span> <span class="units">MB</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge21" class="gauge"> <span class="title">IPv4 IN</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge22" class="gauge"> <span class="title">IPv4 OUT</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge23" class="gauge"> <span class="title">Used DISK</span> <span class="label"></span> <span class="units">GB</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge24" class="gauge"> <span class="title">Disk READ</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge25" class="gauge"> <span class="title">Disk WRITE</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge26" class="gauge"> <span class="title">SWAP</span> <span class="label"></span> <span class="units">%</span> <span class="alert"></span></div>
			</div>
			<div class="clear"></div>
			<div>
				<div class="hard">映像送出装置</div>
				<div class="hardSub">Raspberry Pi 3 Model B a02082 (Sony, UK)</div>
			</div>
			<div class="clear"></div>
			<div class="gauge-container">
				<div id="gauge27" class="gauge"> <span class="title">CPU</span> <span class="label"></span> <span class="units">%</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge28" class="gauge"> <span class="title">TEMP</span> <span class="label"></span> <span class="units">°C</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge29" class="gauge"> <span class="title">Free RAM</span> <span class="label"></span> <span class="units">MB</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge30" class="gauge"> <span class="title">IPv4 IN</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge31" class="gauge"> <span class="title">IPv4 OUT</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge32" class="gauge"> <span class="title">Used DISK</span> <span class="label"></span> <span class="units">GB</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge33" class="gauge"> <span class="title">Disk READ</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge34" class="gauge"> <span class="title">Disk WRITE</span> <span class="label"></span> <span class="units">Kbps</span> <span class="alert"></span></div>
			</div>
			<div class="gauge-container">
				<div id="gauge35" class="gauge"> <span class="title">SWAP</span> <span class="label"></span> <span class="units">%</span> <span class="alert"></span></div>
			</div>
		</div>
	</div>
</div>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
	<div class="modal-dialog" role="document">
		<div class="modal-content">
			<div class="modal-header">
				<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
			</div>
			<div class="modal-body">
				<div style="line-height:14px;margin-top:0;"> <span class="glyphicon glyphicon-th-large" aria-hidden="true" style="margin-right:2px;margin-bottom:6px;"></span>内部主電源供給維持活動限界IoT監視システム<br />
					IoT機器をNetDataで監視、nginxで集約ビジュアル化、Grafanaで記録管理解析するシステムです。<br />
					<br />
					Code licensed MIT, docs CC BY 4.0.<br />
					Copyright © 2017 shogooda (shogo.gfx@gmail.com) All rights reserved.<br />
					<br />
					TrueType フォント<a href="http://www.ayatoweb.com/download/down01.html" target="_blank">「AyatoNumber A」</a><br />
					個人利用の場合はフリーフォントとしてご利用できます。<br />
					商用利用の場合は(放送媒体・紙媒体・WEB問わず)必ずメールにてお問い合わせください。<br />
					著作権者に無断で販売・転載・再配布することは禁止します。<br />
				</div>
			</div>
		</div>
	</div>
</div>
</body>
</html>

ファビコン

http://dz.plala.jp/wiki_data/favicons.zip

消費電力の設定

参考: Raspberry Pi 3 Model B の消費電力削減 - Rabbit Note

USB電源をOFF(VNCで操作すると接続が切れてしまいます。ご注意。)

$ sudo sh -c "echo 0 > /sys/devices/platform/soc/20980000.usb/buspower"
または
$ sudo sh -c "echo 0 > /sys/devices/platform/soc/3f980000.usb/buspower"
ONにする場合は0を1に

HDMI電源をOFF

$ sudo /opt/vc/bin/tvservice -o
ONにする場合
$ sudo /opt/vc/bin/tvservice -p

再起動

$ sudo reboot

メモリの節約

参考: Raspberry メモリ 節約 - Google

デーモンの停止

$ sudo apt-get install chkconfig
$ #sudo chkconfig dbus off
$ sudo chkconfig triggerhappy off
$ sudo chkconfig alsa-utils off
$ sudo chkconfig lightdm off
$ sudo chkconfig motd off
$ sudo chkconfig plymouth off

topコマンド見るとXorg(デスクトップ環境)がいちばんメモリを消費しています。停止してみます。
※この操作するとVNCが機能しないように見えます。復帰にはモニター、マウス、キーボード必要です。

Menu→設定→RaspberryPiの設定→システムタブ→ブート→CLIにチェック→再起動

Xorg(デスクトップ環境)復帰方法

電源OFF
モニター、マウス、キーボードを取り付け
電源ON
コマンドプロンプトの状態
$ sudo startx
デスクトップ環境起動
Menu→設定→RaspberryPiの設定→システムタブ→ブート→デスクトップにチェック→シャットダウン
モニター、マウス、キーボード取り外し
電源ON
VNCなどで接続

参考