smartermeter 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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