21
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

岩手県立大学ソフトウェア情報学部Advent Calendar 2014

Day 5

Macのバッテリー情報を取得・記録してGoogleChartAPIでグラフ化

Last updated at Posted at 2014-12-05

はじめに

Qiita初投稿です。(普段ブログなので・・・)
何書くか迷ったのですが、自分の周りにMac使いが多いのでMac関連で最近自分が作ったものについて書こうかと思います。

Mac book(というかノートPC全般)を使っていて一番気になるのはやっぱりバッテリーの状態です。
今回、そのバッテリーの状態をスクリプトで定期的に取得し、GoogleChartAPIを使ってグラフ化するようにしたのでその方法を書きたいと思います。

同じようなことをしてくれるアプリはあるのですが、個人的にインストールするアプリは最小限にしたい派なので、バッテリーの状態を確認するためだけにアプリを入れるのは正直少し気が進まず。。
それなら作っちゃえ!と思って作りました。(もちろん既存のアプリ自体は素晴らしいものだと思いますので、入れるのも全然ありだと思います!)

結構スクリプト組んで同じようなことをしている方がいたので、参考にさせてもらいつつやったら割と簡単に出来ました。

必須環境

今回の記事の内容を行うにあたって必要な環境は以下の通りです。

  • rubyのインストール: 今回作成するスクリプトはRubyで書くので。
  • ローカルでのWebサーバ(Apache): API叩くときにHttpRequestを使うので。MAMPあるならそれでやったほうが早いかもです。

Macのバッテリー情報を取得

まずはMacのバッテリー情報を取得してみます。
Macのシステムレポートを見ると色々と書いてあるんですが、ターミナルで以下のコマンドを実行すると数値データで見ることが出来ます。

$ ioreg -l | grep Capacity

実行結果は以下のようになります。

実行結果
$ ioreg -l | grep Capacity
    | |    "MaxCapacity" = 5757
    | |    "CurrentCapacity" = 2995
    | |    "LegacyBatteryInfo" = {"Amperage"=3548,"Flags"=7,"Capacity"=5757,"Current"=2995,"Voltage"=12160,"Cycle Count"=188}
    | |    "DesignCapacity" = 6330

この中から、今回使うデータは以下の4つです。

  • MaxCapacity: 完全充電時の容量(mAh)
  • CurrentCapacity: 現在のバッテリー容量(mAh)
  • Cycle Count: 充放電回数
  • DesignCapacity: 設計時のバッテリー容量(mAh)

これらの数値データをスクリプトで定期的に取得し、グラフ化していきます。
なお、グラフ化するのは数値に変動がないDesignCapacityの値を除いた3つの値です。
DesignCapacityの値はあとで使うので覚えておきましょう。

スクリプトの作成

さて、スクリプトを作っていきます。
作成する際にはこちらのサイトが非常に参考になりました。
rubyで書いてますが、複雑なことはしていません。上記のioregコマンドの実行結果を元に文字列処理をして数値を取得し、putsするだけです。

putsする際にはそれぞれ日付(時刻)と数値をカンマ区切りでputsしていますが、これはグラフ化するときに扱いやすいようCSV形式にするためです。
CurrentCapacityは、後に1時間に1回取得するようにするために時刻までputsしています。他は1日1回取得するようにするので日付のみです。

MaxCapacityの取得

battery_max_log.rb
#!/usr/bin/ruby

require 'date'

# 現在の日付を取得し、フォーマット変換して文字列出力
now = Date.today.strftime('%Y/%m/%d')
capacity = `/usr/sbin/ioreg -l | grep MaxCapacity`.chomp.split(" = ")[1]
 
puts now+","+capacity

CurrentCapacityの取得

battery_current_log.rb
#!/usr/bin/ruby

# 現在時刻取得
now = Time.now.strftime('%Y/%m/%d %H:%M')
capacity = `/usr/sbin/ioreg -l | grep CurrentCapacity`.chomp.split(" = ")[1]

puts now+','+capacity

Cycle Countの取得

battery_cycle_log.rb
#!/usr/bin/ruby

require 'date'

# 現在の日付を取得し、フォーマット変換して文字列出力
now = Date.today.strftime('%Y/%m/%d')
cyclecount = `/usr/sbin/ioreg -l | grep CycleCount | grep -v Design`.chomp.split(" = ")[1]

puts now+","+cyclecount

launchdで定期的にスクリプトを実行させる

さて、次に作ったスクリプトを定期的に実行させ、ログを残すようにします。
今回は、Mac(OS X)においてデーモン等の管理を行うlaunchdでデーモンを設定し、定期的に実行させたいと思います。
launchdについては長くなるので今回は触れませんが、簡単な説明は以前私がブログで記事にしてますので、こちらからどうぞ。

今回は /Library/LaunchDaemons に以下の3つのファイルを作成します。
MaxCapacityとCycle Countは毎日0時に、CurrentCapacityは毎時スクリプトを実行し、ログに記録するようにしています。

MaxCapacityの設定ファイル

maxBattery.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>maxBattery</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/ruby</string>
        <string>/Users/username/battery_max_log.rb</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>0</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
    <key>StandardOutPath</key>
    <string>/Users/username/max_battery.txt</string>
</dict>
</plist>

CurretCapacityの設定ファイル

currentBattery.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>currentBattery</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/ruby</string>
        <string>/Users/username/battery_current_log.rb</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
    <key>StandardOutPath</key>
    <string>/Users/username/current_battery.txt</string>
</dict>
</plist>

Cycle Countの設定ファイル

cycleBattery.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>cycleBattery</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/ruby</string>
        <string>/Users/username/battery_cycle_log.rb</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>0</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
    <key>StandardOutPath</key>
    <string>/Users/username/cycle_battery.txt</string>
</dict>
</plist>

作成したらそれぞれのplistファイルを launchctl load コマンドでlaunchdに登録します。
これで定期的に実行され、ログを残すようになりました。

ちなみにログデータはこんな感じになると思います。

current_battery.txt
2014/12/04 21:00,2854
2014/12/04 22:00,1414
2014/12/04 23:00,3035
2014/12/05 00:00,5459
2014/12/05 01:00,4820
2014/12/05 02:00,3554

あとはこのログデータを使ってグラフ化するだけです。

グラフ化しよう

GoogleChartAPIを使ってグラフ化します。
GoogleChartAPIとはGoogleが提供しているグラフ作成サービスで、かなり簡単に自分のページ上にグラフを作成することが出来ます。グラフの種類も多めなので、色々と使いどころはあるかと思います。

このAPIはJavaScriptで叩くので、JavaScriptで実装していきます。

まずはhtmlの作成

とりあえずグラフを表示するページを作ります。
自分の場合は3つのボタンを作成し、対応するボタンを押したらそれぞれのグラフが出るようにしました。

index.html
<!DOCTYPE html>
<html>
    <head>
        <script src="./js/jquery-1.7.1.min.js"></script>
        <script type="text/javascript" src="https://www.google.com/jsapi"></script>
        <script type="text/javascript">
            // Visualization API と折れ線グラフ用のパッケージのロード
            google.load("visualization", "1", {packages:["corechart"]});
        </script>
        <script type="text/javascript" src="./js/index.js"></script>
    </head>
    <body>
        <div id="btn_area">
            <input type="button" value="バッテリー残量" id="current_battery_btn" />
            <input type="button" value="最大バッテリー容量" id="max_battery_btn" />
            <input type="button" value="充放電回数" id="cycle_battery_btn" />
        </div>
        <!-- グラフを描く div 要素 -->
        <div id="chart_div" style="width: 80%; height: 400px;"></div>
    </body>
</html>

いらないところは省きました。
また、JavaScriptの記述はindex.jsに書くようにしているので読み込ませています。

重要な点

  • jqueryを読み込む(必須ではないですがjqueryで書いたほうが便利です)
  • https://www.google.com/jsapiの読み込み
  • Visualization API と折れ線グラフ用のパッケージのロード

JavaScriptでグラフ作成

さて、いよいよグラフ作成のメインとなる部分です。

処理の流れ

このソースは以下のような処理の流れになります。

  1. ボタンが押されたら対応するログデータを読み込みcsv3Arrayメソッドでグラフ用の配列を作成(縦軸を数値に、横軸を日付にする)
  2. CurrentCapacityとMaxCapacityのときは縦軸を%表記に変換
  3. drawChartメソッドでグラフの種類やオプションを決定し、データを元としたグラフを作成する
  4. 描画されたグラフを設定しCallBack関数を呼び出す

ソースコード

ソースコードは以下の通りです。
少し長いですが、基本的にはソース内のコメントに書いてあるとおりです。

index.js
$(function() {
    // バッテリー残量ボタンが押された場合
    $("#current_battery_btn").click(function(){
        // データ入力
        var data = csv3Array("./current_battery.txt", 0);
        var mdata = csv3Array("./max_battery.txt", 1);
        // 数値データを%値に
        data = currentDataPercent(data, mdata);
        // Google Visualization API ロード時のコールバック関数の設定と呼び出し
        google.setOnLoadCallback(drawChart(data, 0));
    });
    // 最大バッテリー容量ボタンが押された場合
    $("#max_battery_btn").click(function(){
        // MaxCapacity data 作成
        var maxData = csv3Array("./max_battery.txt", 1);
        // 一回だけ実行
        if (maxData[1][1] > 100){
            // 最大バッテリー容量の場合は出荷時最大値との%値(小数点第2位まで)に変換
			// 6330はDesignCapacityの値です。環境によって違うと思うので適宜変更してください。
            for(var i = 1; i < maxData.length; i++){
                maxData[i][1] = maxData[i][1] / 6330 * 100 * 100;
                maxData[i][1] = Math.round(maxData[i][1]) / 100;
            }
        }
        google.setOnLoadCallback(drawChart(maxData, 1));
    });
    // 充放電回数ボタンが押された場合
    $("#cycle_battery_btn").click(function(){
        var cycleData = csv3Array("./cycle_battery.txt", 2);
        // Google Visualization API ロード時のコールバック関数の設定と呼び出し
        google.setOnLoadCallback(drawChart(cycleData, 2));
    });
});

// CSV出力関数
// flg 0 バッテリー残量
// flg 1 バッテリー最大値
// flg 2 充放電回数
function csv3Array(filePath, flg) {
    var csvData = [];
    var data = new XMLHttpRequest();

    if (flg === 0){
        csvData = [["時刻", "バッテリー残量"]];
    }else if (flg === 1){
        csvData = [["日付", "バッテリー最大値"]];
    }else{
        csvData = [["日付", "充放電回数"]];
    }

    data.open("GET", filePath, false); //true:非同期,false:同期
    data.send(null);

    var LF = String.fromCharCode(10); //改行コード
    var lines = data.responseText.split(LF);

    for (var i = 0; i < lines.length;++i) {
       var cells = lines[i].split(",");
       if( cells.length != 1 ) {
          csvData.push(cells);
        }
    }
    // 日付をDate型にし、実データを数値に変換
    for (var j = 1; j < csvData.length; j++){
        csvData[j][0] = new Date(csvData[j][0]);
        csvData[j][1] = Number(csvData[j][1]);
    }
    return csvData;
}

// CurrentCapacityのデータを%にする
// その日のMaxCapacityの値を取得して、その値を元に%表記にする(日毎にMaxの値が変わることがあるため)
function currentDataPercent(data, mdata){
    for (var i = 1; i < data.length; i++){
        // dataの月日を取得
		// getMonthでは0-11月になってしまうので、+1する
        var currentMonth = data[i][0].getMonth() + 1;
        var currentDate = data[i][0].getDate();

        for (var j = 1; j < mdata.length; j++){
            // maxDataの月日を取得
            var maxMonth = mdata[j][0].getMonth() + 1;
            var maxDate = mdata[j][0].getDate();
            // 二つの月日が一致しているか
            if (currentMonth === maxMonth && currentDate === maxDate){
                data[i][1] = Math.round(data[i][1] / mdata[j][1] * 100);
                break;
            }
        }
    }
    return data;
}

// グラフ作成関数
// flg 0 バッテリー残量
// flg 1 バッテリー最大値
// flg 2 充放電回数
function drawChart(data, flg){
    var title;
    var chartType;
    var vAxisTitle;
    var max;
    var min = new Date();
    var format;
   
    if (flg === 0){
        title = "バッテリー残量";
        chartType = 'AreaChart';
        vAxisTitle = "%";
        max = 100;
		// 最小値を現在から2週間前に(見やすさ考慮)
        min.setDate(min.getDate() - 14);
        format = "yyyy/MM/dd HH:mm"
    }else if (flg === 1){
        title = "最大バッテリー容量";
        chartType = 'LineChart';
        vAxisTitle = "%";
        max = 100;
        min = null;
        format = "yyyy/MM/dd"
    }else{
        title = "充放電回数";
        chartType = 'LineChart';
        vAxisTitle = "";
        max = null;
        min = null;
        format = "yyyy/MM/dd"
    }

    var wrapper = new google.visualization.ChartWrapper({
        chartType: chartType,
        dataTable: data,
        options: {
            title: title,
            legend: {
                position: 'none'
            },
            hAxis: {
                format: format,
                viewWindow: {
                    min:min, 
                }
            },
            vAxis: {
                title: vAxisTitle,
                viewWindow:{
                    max: max,
                }
            }
        },
        containerId: 'chart_div'
    });
    wrapper.draw();
}

drawChartメソッドでグラフの種類やオプションを定義しています。
今回はCurrentCapacityのグラフをAreaChartに、その他のグラフをLineChartにしましたが、他にどんな種類やオプションがあるのかは、こちらのサイトを参考にしてください。

実際に作成されるグラフ

実際に作成されるグラフはこんな感じになります。(ちなみにボタンはCSSで見やすくしています)
うーん、最大バッテリー容量が着実に減ってきてますね・・・

current.png

max.png

cycle.png

さいごに

以上でMacのバッテリー状態をグラフ化することが出来ました。
GoogleChartAPIのおかげで非常に簡単にグラフ化出来たので良かったです。すごい。さすがGoogle。

私は結構このグラフを毎回見てしまいます。結構気にする性格なので・・・(ちなみに私が使っているAndroidにもバッテリー状態をグラフ化するアプリを入れています笑)
特に満充電をしすぎてないかと最大バッテリー容量を確認しています。

やっぱりノートPCもスマホもバッテリーを長持ちさせるというのは結構大事なことだと思うので、みなさんも気を使っていきましょう!
ちなみにこちらのサイトにMacのバッテリーを長持ちさせるコツがまとめられてます。非常に参考になります!

何か間違い等ありましたらコメント下さい。

参考にさせて頂いたサイト

Macのバッテリー状態を取得する(2) - mac日記

バッテリー情報を記録するrubyスクリプトをcronで実行 « pplog.org

Google Chart Tools の使い方 | プログラマーズ雑記帳

21
21
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?