mac bookのバッテリー状態をはてなグラフに記録してみよう

[2014.08.10追記]
記事が古く、Mavericks(ruby2.0)で動かない(gemのHatena::API::Graphが動かない)
はてなグラフAPIも2014年3月にOAuth認証以外は受け付けなくなって、グラフデータが追加できない、などぼろぼろなので、上記に対処したコードを投稿しました。
mac bookのバッテリー状態をはてなグラフに記録してみよう(OS X 10.9 Mavericks対応版) - mac日記
[追記ここまで]



だいぶ前から、このネタやってたんですけどね。。。
やっとコードを晒せる状態になったというか。
最初に、やりたいことができることを確認しちゃったら、そのあと、ほったらかしにしてました。
まともに動くようにコードを整理したので、公開してみたいと思います。
なにか新しい発明、という訳ではないし、だれでもささっとかけちゃうコードですが、、。


rubyでがっつりコード書いたことないんで、たぶん文化に沿ってないところとかはあると思うけど、
変なところあったら指摘してもらえるとうれしいです。)


gem の Hatena::API::Graph パッケージを使っているので、
はてなグラフapiとは - はてなキーワードを参考に
インストールしておく必要があります。
あとは、cron にでも登録しておけば、毎日自動ではてなグラフにバッテリーの寿命?を記録していってくれます。

MacBook Pro battery capacity (mAh)



MacBook Pro battery cycle (times)




#!/usr/bin/ruby
require 'rubygems'
require 'hatena/api/graph'

class BatteryLog
	HATENA_USER = '[はてなのidを書く]'
	HATENA_PASS = '[はてなのpasswordを書く]'
	GRAPH_NAME_CAPA = 'macbook pro battery capacity (mAh)'
	GRAPH_NAME_CYCLE = 'macbook pro battery cycle time'
	BATTERY_LOG = 'battery.log'
	DEBUG_LOG = 'battery.dbg.log'
	LOG_PATH = File.dirname(__FILE__)
	
	def run
		logging
		post
	end
	
	def logging
		if logged?
			_debug("no write log")
			return
		end
		
		capa, cycle = get_status
		_logging(Time.new, capa, cycle)
		_debug("write log")
	end
	
	def logged?
		Date.today == latest_log_date
	end
	
	def _logging(time, capa, cycle)
		l = sprintf("%s,%d,%d,*", time.strftime("%Y/%m/%d %H:%M:%S"), capa, cycle)
		
		io = open(battery_log(), "a")
		io.puts(l)
		io.close
	end
	
	def decode(l)
		if /(\d{4}\/\d{2}\/\d{2}).+?,(\d+),(\d+)/ =~ l
			[Date.parse($1), $2, $3]
		else
			nil
		end
	end
	
	def post
		temp_file_name = battery_log() + ".tmp";
		
		r = open(battery_log(), "r")
		w = open(temp_file_name, "w")
		while l = r.gets
			l.chomp!
			if !posted?(l)
				if _post(l)
					l = posted!(l)
				end
			end
			
			w.puts(l)
		end
		
		r.close
		w.close
		
		File.rename(temp_file_name, battery_log())
	end
	
	def _post(l)
		date, capa, cycle = decode(l)
		
		if date
			post_hatena(date, capa, cycle)
		else
			_debug("failure decode [" + l + "]")
		end
	end
	
	def posted?(l)
		/-$/ =~ l
	end
	
	def posted!(l)
		l.sub(/,\*$/, ",-")
	end
	
	def get_status
		s = eval(`/usr/sbin/ioreg -w0 -l | grep LegacyBatteryInfo`.gsub(/=/, '=>').gsub(/^.*\{/, '{'))
		capa = s["Capacity"]
		cycle = s["Cycle Count"]
		
		if (capa && cycle)
			[capa, cycle]
		else
			[-1, -1]
		end
	end
	
	def latest_log_date
		if (!File.file?(battery_log()))
			return nil;
		end
		
		log = battery_log()
		tail = `tail -n1 #{log}`
		
		if /(\d{4}\/\d{2}\/\d{2})/ =~ tail
			Date.parse($1)
		else
			nil
		end
	end
	
	def post_hatena(_date, capa, cycle)
		_debug(sprintf("post to hatena graph ... (%s, %d, %d)", _date, capa, cycle))
		
		ret = true
		
		graph = Hatena::API::Graph.new(HATENA_USER, HATENA_PASS)
		begin
			graph.post_data(GRAPH_NAME_CAPA, :date => _date, :value => capa)
			graph.post_data(GRAPH_NAME_CYCLE, :date => _date, :value => cycle)
		rescue
			ret = false
		end
		
		_debug(ret ? "success" : "failure")
		
		ret
	end
	
	def _debug(l)
		if debug_log().size > 0
			io = open(debug_log(), "a")
			io.puts(Time.new.strftime("%Y/%m/%d %H:%M:%S") + " " + l)
			io.close
		end
	end
	
	def battery_log()
		LOG_PATH + "/" + BATTERY_LOG
	end
	
	def debug_log()
		LOG_PATH + "/" + DEBUG_LOG
	end
end

BatteryLog.new.run