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 +6 -0
- data/README.md +16 -16
- data/Rakefile +9 -14
- data/lib/smartermeter/daemon.rb +9 -10
- data/lib/smartermeter/interfaces/swing.rb +35 -30
- data/lib/smartermeter/samples.rb +21 -25
- data/lib/smartermeter/service.rb +49 -66
- data/lib/smartermeter/services/pachube.erb +2 -0
- data/lib/smartermeter/services/{google_powermeter.rb → pachube.rb} +10 -10
- data/lib/smartermeter.rb +2 -2
- data/smartermeter.gemspec +8 -16
- metadata +147 -154
- data/lib/smartermeter/services/google_powermeter.erb +0 -16
- data/spec/fixtures/data.csv +0 -21
- data/spec/fixtures/expected_google_request.xml +0 -292
- data/spec/fixtures/vcr_cassettes/brighterplanet.yml +0 -106
- data/spec/sample_spec.rb +0 -8
- data/spec/samples_spec.rb +0 -39
- data/spec/service_spec.rb +0 -7
- data/spec/services/brighterplanet_spec.rb +0 -26
- data/spec/services/google_powermeter_spec.rb +0 -19
- data/spec/spec_helper.rb +0 -15
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
|
10
|
-
|
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
|
-
|
37
|
+
Pachube
|
38
38
|
-----------------
|
39
39
|
|
40
40
|
Once you've configured SmarterMeter once, you might want to use it with
|
41
|
-
|
42
|
-
|
43
|
-
1. Visit
|
44
|
-
1.
|
45
|
-
1.
|
46
|
-
|
47
|
-
|
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
|
-
|
49
|
+
automatically upload data as it's retrieved from PG&E.
|
51
50
|
|
52
|
-
:transport: :
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
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 => :
|
46
|
+
task :default => :spec
|
47
47
|
|
48
|
-
require '
|
49
|
-
|
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
|
56
|
-
|
57
|
-
|
58
|
-
|
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 '
|
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
|
|
data/lib/smartermeter/daemon.rb
CHANGED
@@ -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
|
-
|
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.
|
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.
|
181
|
+
data = service.fetch_espi(date)
|
183
182
|
next if data.empty?
|
184
183
|
|
185
184
|
@ui.log.info("Verifying #{date}")
|
186
|
-
samples = Samples.
|
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 :
|
212
|
-
@ui.log.info("Uploading #{date} to
|
213
|
-
transport = SmarterMeter::Services::
|
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 => :
|
191
|
-
:
|
192
|
-
:
|
193
|
-
:
|
194
|
-
:
|
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,
|
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
|
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
|
409
|
-
message = "In order to view your power data on
|
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
|
-
[
|
415
|
-
[
|
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
|
419
|
-
ii.create = { :action => method(:
|
420
|
-
cc.
|
421
|
-
cc.
|
422
|
-
|
423
|
-
cc.
|
424
|
-
cc.
|
425
|
-
ii.
|
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
|
432
|
-
|
435
|
+
def api_key
|
436
|
+
@controls.api_key.text.strip
|
433
437
|
end
|
434
438
|
|
435
|
-
def
|
436
|
-
|
439
|
+
def feed_id
|
440
|
+
@controls.feed_id.text.strip
|
437
441
|
end
|
438
442
|
|
439
|
-
def
|
440
|
-
@controls.
|
443
|
+
def datastream_id
|
444
|
+
@controls.datastream_id.text.strip
|
441
445
|
end
|
442
446
|
|
443
|
-
# Opens the
|
444
|
-
# their data.
|
447
|
+
# Opens the Pachube plans page so that users can register
|
445
448
|
#
|
446
449
|
# Returns nothing.
|
447
|
-
def
|
450
|
+
def open_pachube_registration(*ignored_args)
|
448
451
|
desktop = Desktop.getDesktop()
|
449
|
-
uri = Java::JavaNet::URI.new("https://
|
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
|
-
|
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
|
|
data/lib/smartermeter/samples.rb
CHANGED
@@ -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
|
9
|
+
# Parses the XML returned by PG&E and creates a Samples collection.
|
9
10
|
#
|
10
|
-
# @param [String] data the string containing the
|
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.
|
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
|
-
|
17
|
-
data = data.gsub('=','')
|
16
|
+
doc = Nokogiri::HTML(data)
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
24
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
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
|
|
data/lib/smartermeter/service.rb
CHANGED
@@ -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
|
-
|
11
|
-
|
12
|
-
|
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(
|
35
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
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
|
77
|
-
#
|
78
|
-
# you request.
|
56
|
+
# Downloads an ESPI file containing data in 15 minute windows on the
|
57
|
+
# given date.
|
79
58
|
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
|
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
|
-
|
93
|
-
|
94
|
-
form
|
95
|
-
form
|
96
|
-
form
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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 ""
|
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'net/https'
|
2
|
-
require 'erb'
|
3
2
|
|
4
3
|
module SmarterMeter
|
5
4
|
module Services
|
6
|
-
class
|
5
|
+
class Pachube
|
7
6
|
def initialize(config)
|
8
7
|
@config = config
|
9
|
-
raise "The
|
10
|
-
raise "The
|
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(
|
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), {"
|
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
|
33
|
+
# Creates the proper XML request to send to Pachube
|
34
34
|
#
|
35
|
-
# Returns the proper
|
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__), "
|
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/
|
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.
|
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.
|
17
|
-
s.date = '
|
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.
|
59
|
-
s.add_development_dependency('vcr', ["~> 1.
|
60
|
-
s.add_development_dependency('webmock', ["~> 1.
|
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/
|
91
|
-
lib/smartermeter/services/
|
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
|
|