smartermeter 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 (January 28, 2012)
4
+ * Removed Google PowerMeter as it no longer exists
5
+ * Rewrote PG&E scraper to work with OPower, their new web data provider.
6
+ * Handle passwords greater than 8 characters.
7
+ * Remove spec files from the gem.
8
+
3
9
  ## 0.3.3 (April 20, 2011)
4
10
  * Fixed a bug which prevent the proper Google PowerMeter authentication
5
11
  information to be sent.
data/README.md CHANGED
@@ -6,8 +6,8 @@ really want to jump through 37 hoops to see the data on PG&E's website.
6
6
  So I made this.
7
7
 
8
8
  While making this library I discovered that PG&E doesn't even manage the
9
- software for the energy reporting. It's all done by energyguide.com. Not
10
- terribly useful but an interesting piece of trivia.
9
+ software for the energy reporting. It's all done by OPower, not terribly
10
+ useful but an interesting piece of trivia.
11
11
 
12
12
  Getting Started
13
13
  ---------------
@@ -34,25 +34,25 @@ to manipulate it using ruby like so:
34
34
 
35
35
  For futher information see the [API docs][rdoc]
36
36
 
37
- Google PowerMeter
37
+ Pachube
38
38
  -----------------
39
39
 
40
40
  Once you've configured SmarterMeter once, you might want to use it with
41
- Google PowerMeter.
42
-
43
- 1. Visit: https://www.google.com/powermeter/device/activate?mfg=Ruby&model=SmarterMeter&did=PGE&dvars=1
44
- 1. Then sign in with your desired Google Account.
45
- 1. Follow the directions on screen.
46
- 1. On the final screen copy the entire "authInfo" into your favorite editor.
47
- Pull out the "token" and the "path" from the string.
48
- 1. Take the "path" you collected previously and append ".d1" to the end of it.
41
+ Pachube, so you can visualize the results.
42
+
43
+ 1. Visit http://pachube.com and sign up for an account.
44
+ 1. Create a feed and a datastream.
45
+ 1. Copy the feed id (the last item in a feed url like 123 in
46
+ https://pachube.com/feeds/123) and the datastream id (which is the
47
+ name that you enter)
49
48
  1. Then append the following to your ~/.smartermeter file to
50
- automatically upload data as it's retrieved from PG&E.
49
+ automatically upload data as it's retrieved from PG&E.
51
50
 
52
- :transport: :google_powermeter
53
- :google_powermeter:
54
- :token: "your-token"
55
- :variable: "your-path-with.d1-appended"
51
+ :transport: :pachube
52
+ :pachube:
53
+ :api_key: "your-api-key"
54
+ :feed_id: "your-feed-id"
55
+ :datastream_id: "your-datastream-id"
56
56
 
57
57
  To Build the Windows Installer
58
58
  --------
data/Rakefile CHANGED
@@ -43,24 +43,18 @@ end
43
43
  #
44
44
  #############################################################################
45
45
 
46
- task :default => :test
46
+ task :default => :spec
47
47
 
48
- require 'rake/testtask'
49
- Rake::TestTask.new(:test) do |test|
50
- test.libs << 'lib' << 'test'
51
- test.pattern = 'spec/**/*_spec.rb'
52
- test.verbose = true
53
- end
48
+ require 'rspec/core/rake_task'
49
+ RSpec::Core::RakeTask.new(:spec)
54
50
 
55
- desc "Generate RCov test coverage and open in your browser"
56
- task :coverage do
57
- require 'rcov'
58
- sh "rm -fr coverage"
59
- sh "rcov test/test_*.rb"
60
- sh "open coverage/index.html"
51
+ desc "Generate code coverage"
52
+ RSpec::Core::RakeTask.new(:coverage) do |t|
53
+ t.rcov = true
54
+ t.rcov_opts = ["--exclude", "spec"]
61
55
  end
62
56
 
63
- require 'rake/rdoctask'
57
+ require 'rdoc/task'
64
58
  Rake::RDocTask.new do |rdoc|
65
59
  rdoc.rdoc_dir = 'rdoc'
66
60
  rdoc.title = "#{name} #{version}"
@@ -221,6 +215,7 @@ task :gemspec => :validate do
221
215
  reject { |file| file =~ /^\./ }.
222
216
  reject { |file| file =~ /^(rdoc|pkg)/ }.
223
217
  reject { |file| file =~ /\.jar$/ }.
218
+ reject { |file| file =~ /^spec\// }.
224
219
  map { |file| " #{file}" }.
225
220
  join("\n")
226
221
 
@@ -2,6 +2,7 @@ require 'fileutils'
2
2
  require 'crypt/blowfish'
3
3
  require 'yaml'
4
4
  require 'date'
5
+ require 'base64'
5
6
 
6
7
  module SmarterMeter
7
8
  # @private
@@ -64,16 +65,14 @@ module SmarterMeter
64
65
 
65
66
  # Takes the unencrypted password and encrypts it.
66
67
  def password=(unencrypted)
67
- padding_length = 8 - (unencrypted.bytesize % 8)
68
- unencrypted = unencrypted + ("\0" * padding_length)
69
- @config[:password] = cipher.encrypt_block(unencrypted)
68
+ @config[:password] = Base64.encode64(cipher.encrypt_string(unencrypted))
70
69
  end
71
70
 
72
71
  # Returns the clear-text password or nil if it isn't set.
73
72
  def password
74
- password = @config.fetch(:password, nil)
73
+ password = Base64.decode64(@config.fetch(:password, nil))
75
74
  if password
76
- cipher.decrypt_block(password).gsub("\0", "")
75
+ cipher.decrypt_string(password).gsub("\0", "")
77
76
  else
78
77
  password
79
78
  end
@@ -179,11 +178,11 @@ module SmarterMeter
179
178
  dates.each do |date|
180
179
  @ui.log.info("Fetching #{date}")
181
180
 
182
- data = service.fetch_csv(date)
181
+ data = service.fetch_espi(date)
183
182
  next if data.empty?
184
183
 
185
184
  @ui.log.info("Verifying #{date}")
186
- samples = Samples.parse_csv(data).values.first
185
+ samples = Samples.parse_espi(data).values.first
187
186
  first_sample = samples.first
188
187
 
189
188
  if first_sample.kwh
@@ -208,9 +207,9 @@ module SmarterMeter
208
207
 
209
208
  def upload(date, samples)
210
209
  case @config[:transport]
211
- when :google_powermeter
212
- @ui.log.info("Uploading #{date} to Google PowerMeter")
213
- transport = SmarterMeter::Services::GooglePowerMeter.new(@config[:google_powermeter])
210
+ when :pachube
211
+ @ui.log.info("Uploading #{date} to Pachube")
212
+ transport = SmarterMeter::Services::Pachube.new(@config[:pachube])
214
213
  if transport.upload(samples)
215
214
  @ui.log.info("Upload for #{date} complete")
216
215
  else
@@ -187,11 +187,11 @@ module SmarterMeter
187
187
  config = {
188
188
  :username => @pages[0].username,
189
189
  :password => @pages[0].password,
190
- :transport => :google_powermeter,
191
- :google_powermeter => {
192
- :token => @pages[1].token,
193
- :variable => @pages[1].variable,
194
- :auth => @pages[1].auth
190
+ :transport => :pachube,
191
+ :pachube => {
192
+ :api_key => @pages[1].api_key,
193
+ :feed_id => @pages[1].feed_id,
194
+ :datastream_id => @pages[1].datastream_id
195
195
  }
196
196
  }
197
197
  else
@@ -206,7 +206,7 @@ module SmarterMeter
206
206
  c.buttons = @buttons.build
207
207
 
208
208
  @panel = JPanel.new(CardLayout.new)
209
- @pages = [PGEPage, GooglePowerMeterPage, CompletePage].map do |klass|
209
+ @pages = [PGEPage, PachubePage, CompletePage].map do |klass|
210
210
  page = klass.new(@buttons)
211
211
  @panel.add(page.build, klass.to_s)
212
212
  page
@@ -397,7 +397,7 @@ module SmarterMeter
397
397
  end
398
398
  end
399
399
 
400
- class GooglePowerMeterPage
400
+ class PachubePage
401
401
  include_package "javax.swing"
402
402
  include_package "java.awt"
403
403
  include WizardPage
@@ -405,48 +405,51 @@ module SmarterMeter
405
405
  def initialize(buttons)
406
406
  @buttons = buttons
407
407
 
408
- title = "<html><b>Connect to Google PowerMeter</b></html>"
409
- message = "In order to view your power data on Google PowerMeter you'll need to create an account."
408
+ title = "<html><b>Connect to Pachube</b></html>"
409
+ message = "In order to view your power data on Pachube you'll need to create an account."
410
410
 
411
411
  header(title, message) do |c|
412
412
  layout = "
413
413
  [ create ]
414
- [ auth_label ]
415
- [ auth_field ]
414
+ [ api_key_label | <api_key ]
415
+ [ feed_id_label | <feed_id ]
416
+ [ datastream_id_label | <datastream_id ]
416
417
  "
417
418
  @controls = Profligacy::Swing::LEL.new(JPanel, layout) do |cc,ii|
418
- cc.create = JButton.new "Create a PowerMeter Account"
419
- ii.create = { :action => method(:open_google_powermeter_registration) }
420
- cc.auth_label = JLabel.new "Then, copy your Authorization Information below:"
421
- cc.auth_field = JTextArea.new
422
- cc.auth_field.line_wrap = true
423
- cc.auth_field.minimum_size = Dimension.new(400, 80)
424
- cc.auth_field.maximum_size = Dimension.new(400, 80)
425
- ii.auth_field = { :key => method(:validate) }
419
+ cc.create = JButton.new "Create a Pachube Account"
420
+ ii.create = { :action => method(:open_pachube_registration) }
421
+ cc.api_key_label = JLabel.new "Api Key:"
422
+ cc.api_key_field = JTextField.new
423
+ ii.api_key_field = { :key => method(:validate) }
424
+ cc.feed_id_label = JLabel.new "Feed id:"
425
+ cc.feed_id_field = JTextField.new
426
+ ii.feed_id_field = { :key => method(:validate) }
427
+ cc.datastream_id_label = JLabel.new "Datastream Name:"
428
+ cc.datastream_id_field = JTextField.new
429
+ ii.datastream_id_field = { :key => method(:validate) }
426
430
  end
427
431
  c.controls = @controls.build
428
432
  end
429
433
  end
430
434
 
431
- def token
432
- CGI.parse(@controls.auth_field.text.strip)["token"][0]
435
+ def api_key
436
+ @controls.api_key.text.strip
433
437
  end
434
438
 
435
- def variable
436
- CGI.parse(@controls.auth_field.text.strip)["path"][0]+".d1"
439
+ def feed_id
440
+ @controls.feed_id.text.strip
437
441
  end
438
442
 
439
- def auth
440
- @controls.auth_field.text.strip
443
+ def datastream_id
444
+ @controls.datastream_id.text.strip
441
445
  end
442
446
 
443
- # Opens the Google Powermeter registration flow so that users can upload
444
- # their data.
447
+ # Opens the Pachube plans page so that users can register
445
448
  #
446
449
  # Returns nothing.
447
- def open_google_powermeter_registration(*ignored_args)
450
+ def open_pachube_registration(*ignored_args)
448
451
  desktop = Desktop.getDesktop()
449
- uri = Java::JavaNet::URI.new("https://www.google.com/powermeter/device/activate?mfg=SmarterMeter&model=SmarterMeter&did=PGE&dvars=1")
452
+ uri = Java::JavaNet::URI.new("https://pachube.com/plans")
450
453
  desktop.browse(uri)
451
454
  end
452
455
 
@@ -455,7 +458,9 @@ module SmarterMeter
455
458
  #
456
459
  # Returns nothing.
457
460
  def validate(*ignored_args)
458
- @buttons.next.enabled = @controls.auth_field.text.size > 0
461
+ if api_key.any? && feed_id.any? && datastream_id.any?
462
+ @buttons.next.enabled = true
463
+ end
459
464
  end
460
465
  end
461
466
 
@@ -1,44 +1,40 @@
1
1
  require 'date'
2
2
  require 'time'
3
+ require 'nokogiri'
3
4
 
4
5
  module SmarterMeter
5
6
  # Represents a collection of samples. In some cases it's useful to operate on
6
7
  # groups of samples and this class provides that functionality.
7
8
  class Samples < Hash
8
- # Parses the CSV returned by PG&E and creates a Samples collection.
9
+ # Parses the XML returned by PG&E and creates a Samples collection.
9
10
  #
10
- # @param [String] data the string containing the CSV returned by PG&E
11
+ # @param [String] data the string containing the XML returned by PG&E
11
12
  # @return [Samples] creates a Samples collection from the given data.
12
- def self.parse_csv(data)
13
+ def self.parse_espi(data)
13
14
  samples = Samples.new
14
- date_re = /([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})/
15
15
 
16
- # Apparently they felt the need to put a = outside of the correct place
17
- data = data.gsub('=','')
16
+ doc = Nokogiri::HTML(data)
18
17
 
19
- hour_increment = 60*60
20
- CSV.parse(data) do |row|
21
- next unless row.length > 0 and date_re.match row[0]
18
+ doc.xpath("//intervalreading").each do |reading|
19
+ # NOTE: This is a hack, the ESPI data seems to be assuming that
20
+ # all users live in the Eastern Time Zone. The timestamps
21
+ # returned in the ESPI should really be in UTC and not in local
22
+ # time. I'm going to assume all PG&E customers are in the
23
+ # pacific timezone and since the eastern timezone has the same
24
+ # daylight savings time rules then we can use a constant
25
+ # difference to correct the problem.
26
+ pacific_timezone_correction = 60*60*3
22
27
 
23
- month, day, year = date_re.match(row[0]).captures
24
- month = month.to_i
25
- day = day.to_i
26
- year = year.to_i
28
+ timestamp = Time.at(reading.xpath("./timeperiod/start").first.content.to_i + pacific_timezone_correction)
29
+ value = reading.xpath("./value").first.content.to_i / 900.0
27
30
 
28
- timestamp = Time.local(year, month, day, 0) - hour_increment
29
- next if row[1].include? "$"
30
- hourly_samples = row[1..24].map do |v|
31
- if v == "-"
32
- kwh = nil
33
- else
34
- kwh = v.to_f
35
- end
31
+ year = timestamp.year
32
+ month = timestamp.month
33
+ day = timestamp.day
36
34
 
37
- timestamp = timestamp + hour_increment
38
- Sample.new(timestamp, kwh)
39
- end
40
- samples[Date.new(year, month, day)] = hourly_samples
35
+ (samples[Date.new(year, month, day)] ||= []) << Sample.new(timestamp, value)
41
36
  end
37
+
42
38
  samples
43
39
  end
44
40
 
@@ -1,5 +1,8 @@
1
1
  require 'mechanize'
2
2
  require 'csv'
3
+ require 'tempfile'
4
+ require 'zip/zip'
5
+ require 'uri'
3
6
 
4
7
  module SmarterMeter
5
8
  # Provides access to the PG&E SmartMeter data through a ruby interface. This
@@ -7,9 +10,11 @@ module SmarterMeter
7
10
  # so if something stops working its likely that something changed on PG&E's
8
11
  # site and this class will need to be adapted.
9
12
  class Service
10
- LOGIN_URL = "http://www.pge.com/myhome/"
11
- OVERVIEW_URL = "https://www.pge.com/csol/actions/login.do?aw"
12
- ENERGYGUIDE_AUTH_URL = "https://www.energyguide.com/LoadAnalysis/LoadAnalysis.aspx?Referrerid=154"
13
+ LOGIN_FORM_URL = "http://www.pge.com/"
14
+ LOGIN_URL = "https://www.pge.com/eum/login"
15
+ MY_USAGE_URL = "https://www.pge.com/myenergyweb/appmanager/pge/customer?_nfpb=true&_pageLabel=MyUsage"
16
+ SAML_URL = "https://sso.opower.com/sp/ACS.saml2"
17
+ MY_ENERGY_USE_URL = "https://pge.opower.com/ei/app/myEnergyUse"
13
18
 
14
19
  # Provides access to the last page retrieved by mechanize. Useful for
15
20
  # debugging and reporting errors.
@@ -31,40 +36,15 @@ module SmarterMeter
31
36
  # @return [Boolean] true upon succesful login and false otherwise
32
37
  def login(username, password)
33
38
  begin
34
- @agent.get(LOGIN_URL) do |page|
35
- logged_in_page = page.form_with(:action => 'https://www.pge.com/eum/login') do |login|
39
+ @agent.get(LOGIN_FORM_URL) do |page|
40
+ saml_page = page.form_with(:action => LOGIN_URL) do |login|
36
41
  login.USER = username
37
42
  login.PASSWORD = password
43
+ login.TARGET = MY_USAGE_URL
38
44
  end.submit
39
- end
40
-
41
- # There is a crazy meta-reload thing here that mechanize doesn't handle
42
- # correctly by itself so let's help it along...
43
- @agent.get(OVERVIEW_URL) do |page|
44
-
45
- return false if page.title =~ /PG&E Login/
46
-
47
- # Load the PG&E Terms of Use page
48
- tou_link = page.link_with(:href => '/csol/actions/billingDisclaimer.do?actionType=hourly')
49
- unless tou_link
50
- @last_page = page
51
- return false
52
- end
53
- tou_page = @agent.click(tou_link)
54
- form = tou_page.forms().first
55
- agree_button = form.button_with(:value => 'I Understand - Proceed')
56
-
57
- # Agree to the terms of use
58
- form['agreement'] = 'yes'
59
-
60
- # Load up the PG&E frame page for historical data
61
- hourly_usage_container = form.submit(agree_button)
62
-
63
- # Now load up the frame with the content
64
- hourly_usage = @agent.click(hourly_usage_container.frames.select{|f| f.href == "/csol/nexus/content.jsp"}.first)
65
-
66
- # Now post the authentication information from PG&E to energyguide.com
67
- @data_page = hourly_usage.form_with(:action => ENERGYGUIDE_AUTH_URL).submit
45
+ token = saml_page.form_with(:action => SAML_URL).submit
46
+ usage_page = token.form_with(:action => MY_ENERGY_USE_URL).submit
47
+ @data_page = usage_page.link_with(:text => "Green Button").click
68
48
  end
69
49
  @authenticated = true
70
50
  rescue Exception => e
@@ -73,41 +53,44 @@ module SmarterMeter
73
53
  end
74
54
  end
75
55
 
76
- # Downloads a CSV containing hourly date on that date. Up to a week worth
77
- # of other data will be included depending on which day of the week that
78
- # you request.
56
+ # Downloads an ESPI file containing data in 15 minute windows on the
57
+ # given date.
79
58
  #
80
- # PG&E compiles this data on a weekly schedule so if you ask for Monday
81
- # you'll get the previous Sunday and the following days upto the next
82
- # Sunday.
83
- #
84
- # @return [String] the CSV data.
85
- def fetch_csv(date)
86
- raise RuntimeException, "login must be called before fetch_csv" unless @authenticated
59
+ # @param [Time] date - The day of the data you wish to fetch.
60
+ # @return [String] the XML data.
61
+ def fetch_espi(date)
62
+ raise RuntimeException, "login must be called before fetch_espi" unless @authenticated
87
63
 
88
- # Now we almost actually have data. However we need to setup the desired
89
- # parameters first before we can get the exportable data. This really shouldn't
90
- # be necessary.
91
64
  begin
92
- hourly_data = @data_page.form_with(:action => "LoadAnalysis.aspx") do |form|
93
- form['__EVENTTARGET'] = "objChartSelect$butSubmit"
94
- form['objTimePeriods$objExport$hidChart'] = "Hourly Usage"
95
- form['objTimePeriods$objExport$hidChartID'] = 8
96
- form['objChartSelect$ddChart'] = 8 # Hourly usage
97
-
98
- form['objTimePeriods$objExport$hidTimePeriod'] = "Week"
99
- form['objTimePeriods$objExport$hidTimePeriodID'] = 3
100
- form['objTimePeriods$rlPeriod'] = 3
101
-
102
- form['objChartSelect$ccSelectedDate1'] = date.strftime("%m/%d/%Y")
103
- end.submit
104
-
105
- # Now the beautiful data...
106
- hourly_csv = hourly_data.form_with(:action => "LoadAnalysis.aspx") do |form|
107
- form['__EVENTTARGET'] = "objTimePeriods$objExport$butExport"
108
- end.submit
109
-
110
- hourly_csv.body
65
+ form = @data_page.forms.first
66
+ begin
67
+ form.radiobutton_with(:value => 'ESPI_INTERVAL').click
68
+ form.field_with(:name => 'from').value = date.strftime("%m/%d/%Y")
69
+ form.field_with(:name => 'to').value = date.strftime("%m/%d/%Y")
70
+ end
71
+ espi_xml_zip = form.submit
72
+
73
+ # This has to be one of the stupidest implementations I've seen
74
+ # of a ruby library. Why on earth can you not pass in an IO
75
+ # object to the rubyzip library?
76
+ file = Tempfile.new('espi-zip')
77
+ begin
78
+ file.binmode
79
+ file << espi_xml_zip.body
80
+ file.flush
81
+ file.close
82
+
83
+ Zip::ZipInputStream::open(file.path) do |contents|
84
+ while (entry = contents.get_next_entry)
85
+ if (entry.name =~ /pge_electric_interval_data/) then
86
+ return contents.read
87
+ end
88
+ end
89
+ end
90
+ ensure
91
+ file.close
92
+ file.unlink
93
+ end
111
94
  rescue Exception => e
112
95
  @last_exception = e
113
96
  return ""
@@ -0,0 +1,2 @@
1
+ <% samples.select { |s| s.kwh }.each do |sample| %>
2
+ <%= sample.time.utc.iso8601 %>,<%= sample.kwh %><% end %>
@@ -1,13 +1,13 @@
1
1
  require 'net/https'
2
- require 'erb'
3
2
 
4
3
  module SmarterMeter
5
4
  module Services
6
- class GooglePowerMeter
5
+ class Pachube
7
6
  def initialize(config)
8
7
  @config = config
9
- raise "The Google PowerMeter token must be configured" unless @config[:token]
10
- raise "The Google PowerMeter variable must be configured" unless @config[:variable]
8
+ raise "The Pachube token must be configured" unless @config[:api_key]
9
+ raise "The Pachube feed id must be configured" unless @config[:feed_id]
10
+ raise "The Pachube datastream id must be configured" unless @config[:datastream_id]
11
11
  end
12
12
 
13
13
  # Public: Uploads an array of Samples to Google PowerMeter
@@ -16,12 +16,12 @@ module SmarterMeter
16
16
  #
17
17
  # Returns true on success and false otherwise
18
18
  def upload(samples)
19
- url = URI.parse('https://www.google.com/powermeter/feeds/event')
19
+ url = URI.parse("https://api.pachube.com/v2/feeds/#{@config[:feed_id]}/datastreams/#{@config[:datastream_id]}/datapoints.csv")
20
20
  http = Net::HTTP.new(url.host, url.port)
21
21
  http.use_ssl = true
22
22
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
23
23
  http.ca_file = File.join(File.dirname(__FILE__), "cacert.pem")
24
- res, body = http.post(url.path, request_body(samples), {"Authorization" => "AuthSub token=\"#{@config[:token]}\"", "Content-Type" => "application/atom+xml"})
24
+ res, body = http.post(url.path, request_body(samples), {"X-PachubeApiKey" => @config[:api_key], "Content-Type" => "text/csv"})
25
25
  case res
26
26
  when Net::HTTPSuccess
27
27
  true
@@ -30,12 +30,12 @@ module SmarterMeter
30
30
  end
31
31
  end
32
32
 
33
- # Creates the proper XML request to send to Google.
33
+ # Creates the proper XML request to send to Pachube
34
34
  #
35
- # Returns the proper atom/xml response to send to google
35
+ # Returns the proper text/csv request to send to pachube
36
36
  def request_body(samples)
37
- template = ERB.new(File.read(File.join(File.dirname(__FILE__), "google_powermeter.erb")))
38
- template.result(binding)
37
+ template = ERB.new(File.read(File.join(File.dirname(__FILE__), "pachube.erb")))
38
+ template.result(binding).gsub(/^\n/, '')
39
39
  end
40
40
  end
41
41
  end
data/lib/smartermeter.rb CHANGED
@@ -2,10 +2,10 @@ require 'smartermeter/sample'
2
2
  require 'smartermeter/samples'
3
3
  require 'smartermeter/service'
4
4
  require 'smartermeter/daemon'
5
- require 'smartermeter/services/google_powermeter'
5
+ require 'smartermeter/services/pachube'
6
6
  require 'smartermeter/services/brighterplanet'
7
7
  require 'smartermeter/interfaces/cli'
8
8
 
9
9
  module SmarterMeter
10
- VERSION = "0.3.3"
10
+ VERSION = "0.4.0"
11
11
  end
data/smartermeter.gemspec CHANGED
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
13
13
  ## If your rubyforge_project name is different, then edit it and comment out
14
14
  ## the sub! line in the Rakefile
15
15
  s.name = 'smartermeter'
16
- s.version = '0.3.3'
17
- s.date = '2011-04-21'
16
+ s.version = '0.4.0'
17
+ s.date = '2012-01-28'
18
18
  s.rubyforge_project = 'smartermeter'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
@@ -52,12 +52,13 @@ Gem::Specification.new do |s|
52
52
  s.add_dependency('crypt19', ["= 1.2.1"])
53
53
  s.add_dependency('rest-client', ["= 1.6.1"])
54
54
  s.add_dependency('json_pure', ["= 1.5.1"])
55
+ s.add_dependency('rubyzip', ["= 0.9.5"])
55
56
 
56
57
  ## List your development dependencies here. Development dependencies are
57
58
  ## those that are only needed during development
58
- s.add_development_dependency('rspec', ["~> 2.5.0"])
59
- s.add_development_dependency('vcr', ["~> 1.7.0"])
60
- s.add_development_dependency('webmock', ["~> 1.6.2"])
59
+ s.add_development_dependency('rspec', ["~> 2.8.0"])
60
+ s.add_development_dependency('vcr', ["~> 1.11.3"])
61
+ s.add_development_dependency('webmock', ["~> 1.7.10"])
61
62
  s.add_development_dependency('minitar', ["~> 0.5.2"])
62
63
 
63
64
  ## Leave this section as-is. It will be automatically generated from the
@@ -87,18 +88,9 @@ Gem::Specification.new do |s|
87
88
  lib/smartermeter/service.rb
88
89
  lib/smartermeter/services/brighterplanet.rb
89
90
  lib/smartermeter/services/cacert.pem
90
- lib/smartermeter/services/google_powermeter.erb
91
- lib/smartermeter/services/google_powermeter.rb
91
+ lib/smartermeter/services/pachube.erb
92
+ lib/smartermeter/services/pachube.rb
92
93
  smartermeter.gemspec
93
- spec/fixtures/data.csv
94
- spec/fixtures/expected_google_request.xml
95
- spec/fixtures/vcr_cassettes/brighterplanet.yml
96
- spec/sample_spec.rb
97
- spec/samples_spec.rb
98
- spec/service_spec.rb
99
- spec/services/brighterplanet_spec.rb
100
- spec/services/google_powermeter_spec.rb
101
- spec/spec_helper.rb
102
94
  ]
103
95
  # = MANIFEST =
104
96