ミルエネのデータを収集する

ミルエネとは

「ミルエネ」というのは、NTTが提供しているサービスで、自宅に電力を測定する機器と通信機器を取り付けると、 1分おきの電力使用量を測定し、ミルエネのサーバにデータをあげて、それをPCやスマートホンでグラフで確認 できるサービスです。

ミルエネのページ

ミルエネでデータを収集

ミルエネは電力使用量をわかりやすいグラフで表示してくれるのですが、できれば電力使用量を数値データとして 取得したいこともあると思います。

データ取得用のWeb-APIとかあればいいのですが、今のところそれはなさそう。 だったら自分で作ろうか。

やり方

Webページ内からデータを取れないかと思い、ページのHTMLソースを眺めてみると、グラフ部分はGoogleのChart API というのを使っているようです。

  <img src="https://chart.googleapis.com/chart?cht=bvg&amp;
    chs=715x190&amp;
    chbh=7,0,4&amp;
    chd=t1:34.6,32.8,32.8,32.8,34.0,34.0,34.0,40.5,...途中省略...&amp;
    chco=ff9733&amp;
    chm=D,ff2a5f,1,4,3&amp;
    chg=0,20,1,2&amp;
    chxt=x,r&amp;
    chxl=0:|0|||||5|||||10||||...途中省略...1:|0.0000|0.0100|0.0200|0.0300|0.0400|0.0500" 
  alt="消費電力状況グラフ"/>
  

この部分のデータを解析すれば、消費電力を数値として得られそうです。

  • chd= の部分に、グラフの数値データが入っている。ただし絶対値ではなく、縦軸の最大値を100としたパーセント値になっている
  • chxl= の部分がX軸/Y軸の情報。ここからY軸の最大値を求めて、chdのパーセント値を掛け合わせれば絶対値を求めることができる

日付指定でグラフ表示できるのは、「消費電力詳細」のページ。 ここのページに移動し、左枠で日付を入力し、[変更]ボタンを押せば、その日と前日の電力使用量が表示される。

ここのページから電力使用量を絶対値で得られればいいだろう。

実装

IE_libを使えば、希望のページへ移動し、特定のタグのデータを取ってくるのは簡単にできる。

ただ、このテのアプリは全部決めうちで作らざるを得ないので、ページの構成がちょっとでも変わると動かなくなる。

以下のサンプルを実行すると、指定した日付の1時間ごとの消費電力の数値が得られる。タップ指定もちょっといじればできるでしょう。

  #!ruby -Ks
  require 'ie_lib'
  
  class MiruenePage
    def initialize(userid, passwd)
      @ie = IE.new
      @ie.navigate("https://flets-eco.jp/eipc/")
      login(userid, passwd)
  
    end
  
    def get_elepower_data(year, month, day)
      move_page(:PAGE_DETAIL)
      select_day(year, month, day)
      data_array = get_graph_data()
      return data_array
    end
  
    private
    def login(userid, passwd)
      loginbtn_tag = nil
      @ie.document.body.tags("input").each {|tag|
        #p [tag.name, tag.src]
        case tag.name
        when "userId"
          tag.text = userid
        when "password"
          tag.text = passwd
        end
        if tag.src == "https://flets-eco.jp/eipc/img/login_default.png"
          loginbtn_tag = tag
        end
      }
      if loginbtn_tag
        loginbtn_tag.click
      else
        raise "ログインボタンが見つからない"
      end
  
    end
    INDEX_MENU_TABLE = 0
    INDEX_MENU_TR = 2
    def move_page(page_name)
      # onclickに設定されているkeyword
      case page_name
      when :PAGE_MAIN
        keyword = "main_init.do"
      when :PAGE_TODAY
        keyword = "pct_init.do"
      when :PAGE_DETAIL
        keyword = "pcd_init.do"
      else
        raise "規定外のページ.#{page_name}"
      end
      # ページメニューはINDEX_MENU_TABLE番目のTABLEにある
      menu_table = @ie.document.body.tags("table")[INDEX_MENU_TABLE]  
      menu_td = menu_table.tags("tr")[INDEX_MENU_TR]
      menu_td.tags("td").each {|tag|
        inner_html = tag.innerHTML
        if inner_html =~ /onclick=\"(.+?)\"/
          onclick_cmd = $1
          if onclick_cmd =~ /#{keyword}/
            input_tag = tag.tags("INPUT")[0]
            input_tag.click
            return
          end
        end
      }
      # ここまでくるのは、すでにそのページにいるということ
    end
  
    INDEX_CHANGE_TABLE = 1
    INDEX_CHANGE_TD = 0
    def select_day(year, month, day)
      change_table = @ie.document.body.tags("table")[INDEX_CHANGE_TABLE] # 真ん中
      change_td = change_table.tags("td")[INDEX_CHANGE_TD] # 左の枠
      change_td.tags("select").each {|tag|
        case tag.name
        when "tabId"
        when "year"
          tag.text = "#{year}年"
        when "month"
          tag.text = "#{month}月"
        when "day"
          tag.text = "#{day}日"
        end
      }
      change_td.tags("tr").each {|tag|
        #p tag.innerHTML
        #puts "--------------------"
        input_tags = tag.tags("input")
        next unless input_tags
        if (input_tags.size == 1) and (tag.innerHTML =~ /pcd_time\.do/)
          #puts "Find 変更ボタン。inner = #{tag.innerHTML}"
          input_tag = tag.tags("input")[0]
          input_tag.click
        end
      }
    end
  
  
    def get_graph_data()
      graph_tag = nil
      @ie.document.body.tags("IMG").each {|tag|
        if tag.src =~ /chart\.googleapis\.com/
          graph_tag = tag
          break
        end
      }
      return nil unless graph_tag
      graph_src = graph_tag.src
      graph_api_param = graph_src.split(/\?/)[1]
      today_data_text = nil
      value_data_text = nil
      graph_api_param.split(/\&/).each {|oneline|
        if oneline =~ /^chd=(.+)/
          data_part = $1.split(/\:/)[1]
          data_part_arr = data_part.split(/\|/)
          pre_day_data, today_data_text, sekisan_data = data_part_arr
          #puts "DATA = #{[pre_day_data, today_data_text, sekisan_data].inspect}"
        end
        if oneline =~ /^chxl=(.+)/
          jiku_param = $1
          if jiku_param =~ /0:(.+)1:(.+)2:(.+)/
            hour_jiku = $1
            byhour_jiku = $2
            bytotal_jiku = $3
            value_data_text = byhour_jiku
          end
        end
      }
      today_data_list = convert_data(today_data_text, value_data_text)
      return today_data_list
    end
  
    def convert_data(today_data_text, value_data_text)
      val_arr = value_data_text.split(/\|/)
      max_value = val_arr[-1].to_f
      value_arr = today_data_text.split(/\,/).map {|percent_val|
        max_value * percent_val.to_f / 100.0
      }
    end
  
  end
  
  
  
  miruene_page = MiruenePage.new("xxxxxx", "xxxxxxx")  # ログオンユーザー名とパスワード
  data_arr = miruene_page.get_elepower_data(2012, 3, 11) # 日付指定
  
  puts "data_arr = #{data_arr.inspect}"