Class: SmarterMeter::Service

Inherits:
Object
  • Object
show all
Defined in:
lib/smartermeter/service.rb

Overview

Provides access to the PG&E SmartMeter data through a ruby interface. This class depends on the PG&E website to function the same that it does today, so if something stops working its likely that something changed on PG&E's site and this class will need to be adapted.

Constant Summary

LOGIN_FORM_URL =
"http://www.pge.com/"
LOGIN_URL =
"https://www.pge.com/eum/login"
MY_USAGE_URL =
"https://www.pge.com/myenergyweb/appmanager/pge/customer?_nfpb=true&_pageLabel=MyUsage"
SAML_URL =
"https://sso.opower.com/sp/ACS.saml2"
MY_ENERGY_USE_URL =
"https://pge.opower.com/ei/app/myEnergyUse"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeService

Returns a new instance of Service



27
28
29
30
31
# File 'lib/smartermeter/service.rb', line 27

def initialize
  @agent = Mechanize.new { |agent|
    agent.user_agent_alias = 'Mac Safari'
  }
end

Instance Attribute Details

#last_exceptionObject (readonly)

Provides access to the last exception thrown while accessing PG&E's site. Useful for debugging/error reporting.



25
26
27
# File 'lib/smartermeter/service.rb', line 25

def last_exception
  @last_exception
end

#last_pageObject (readonly)

Provides access to the last page retrieved by mechanize. Useful for debugging and reporting errors.



21
22
23
# File 'lib/smartermeter/service.rb', line 21

def last_page
  @last_page
end

Instance Method Details

#fetch_espi(date) ⇒ String

Downloads an ESPI file containing data in 15 minute windows on the given date.

Parameters:

  • date (Time)
    • The day of the data you wish to fetch.

Returns:

  • (String)

    the XML data.

Raises:

  • (RuntimeException)


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/smartermeter/service.rb', line 61

def fetch_espi(date)
  raise RuntimeException, "login must be called before fetch_espi" unless @authenticated

  begin
    form = @data_page.forms.first
    form.radiobutton_with(:name => 'exportFormat', :value => 'ESPI_AMI').check
    form['from'] = date.strftime("%m/%d/%Y")
    form['to'] = date.strftime("%m/%d/%Y")
    espi_xml_zip = form.submit

    # This has to be one of the stupidest implementations I've seen
    # of a ruby library. Why on earth can you not pass in an IO
    # object to the rubyzip library?
    file = Tempfile.new('espi-zip')
    begin
      file.binmode
      file << espi_xml_zip.body
      file.flush
      file.close

      Zip::ZipInputStream::open(file.path) do |contents|
        while (entry = contents.get_next_entry)
          if (entry.name =~ /pge_electric_interval_data/) then
            return contents.read
          end
        end
      end
    ensure
      file.close
      file.unlink
    end
  rescue Exception => e
    @last_exception = e
    return ""
  end
end

#login(username, password) ⇒ Boolean

Authenticates to the PG&E's website. Only needs to be performed once per instance of this class.

Returns:

  • (Boolean)

    true upon succesful login and false otherwise



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/smartermeter/service.rb', line 37

def (username, password)
  begin
    @agent.get() do |page|
      saml_page = page.form_with(:action => ) do ||
        .USER = username
        .PASSWORD = password
        .TARGET = MY_USAGE_URL
      end.submit
      token = saml_page.form_with(:action => SAML_URL).submit
      usage_page = token.form_with(:action => MY_ENERGY_USE_URL).submit
      @data_page = usage_page.link_with(:text => "Green Button - Download my data").click
    end
    @authenticated = true
  rescue Exception => e
    @last_exception = e
    return false
  end
end