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 +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
|
|