smartermeter 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +6 -0
- data/Gemfile +3 -2
- data/README.md +21 -1
- data/Rakefile +9 -1
- data/lib/smartermeter/daemon.rb +6 -3
- data/lib/smartermeter/interfaces/swing.rb +2 -0
- data/lib/smartermeter/sample.rb +16 -43
- data/lib/smartermeter/samples.rb +65 -0
- data/lib/smartermeter/service.rb +22 -1
- data/lib/smartermeter/services/brighterplanet.rb +59 -0
- data/lib/smartermeter/{transports → services}/cacert.pem +0 -0
- data/lib/smartermeter/{transports → services}/google_powermeter.erb +0 -0
- data/lib/smartermeter/{transports → services}/google_powermeter.rb +1 -1
- data/lib/smartermeter.rb +4 -2
- data/smartermeter.gemspec +23 -14
- data/{specs → spec}/fixtures/data.csv +0 -0
- data/{specs → spec}/fixtures/expected_google_request.xml +0 -0
- data/spec/fixtures/vcr_cassettes/brighterplanet.yml +106 -0
- data/spec/sample_spec.rb +8 -0
- data/spec/samples_spec.rb +39 -0
- data/{specs → spec}/service_spec.rb +0 -1
- data/spec/services/brighterplanet_spec.rb +26 -0
- data/{specs/transports → spec/services}/google_powermeter_spec.rb +4 -4
- data/{specs → spec}/spec_helper.rb +6 -0
- metadata +75 -44
- data/specs/sample_spec.rb +0 -24
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.3.2 (March 29, 2011)
|
4
|
+
* Added support for Brighter Planet's Electricity API.
|
5
|
+
* Fixed encryption of passwords that had lengths other than multiples of
|
6
|
+
8.
|
7
|
+
* Added more extensive documentation of the library.
|
8
|
+
|
3
9
|
## 0.3.1 (March 15th, 2011)
|
4
10
|
* Changed name from "Smartermeter" to "SmarterMeter"
|
5
11
|
* Updated the .detect\_charset monkeypatch for the newer mechanize
|
data/Gemfile
CHANGED
@@ -2,5 +2,6 @@ source :rubygems
|
|
2
2
|
gemspec
|
3
3
|
|
4
4
|
# Used only in the Java client, which isn't distributed as a gem
|
5
|
-
gem "
|
6
|
-
gem "jruby
|
5
|
+
gem "nokogiri", ">= 1.4.4", :platforms => :jruby
|
6
|
+
gem "profligacy", :platforms => :jruby
|
7
|
+
gem "jruby-openssl", :platforms => :jruby
|
data/README.md
CHANGED
@@ -15,6 +15,25 @@ Getting Started
|
|
15
15
|
gem install smartermeter
|
16
16
|
smartermeter
|
17
17
|
|
18
|
+
Manipulating the data
|
19
|
+
---------------------
|
20
|
+
|
21
|
+
After you've successfully downloaded one set of data, you should be able
|
22
|
+
to manipulate it using ruby like so:
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'smartermeter'
|
26
|
+
|
27
|
+
config = YAML.load_file(File.expand_path("~/.smartermeter"))
|
28
|
+
csv_file = Dir.glob(File.join(config[:data_dir], "*.csv")).last
|
29
|
+
|
30
|
+
samples = SmarterMeter::Samples.parse_csv(csv_file)
|
31
|
+
kwh_used = samples.total_kwh
|
32
|
+
api = SmarterMeter::Services::BrighterPlanet.new
|
33
|
+
puts api.calculate_kg_carbon(kwh_used)
|
34
|
+
|
35
|
+
For futher information see the [API docs][rdoc]
|
36
|
+
|
18
37
|
Google PowerMeter
|
19
38
|
-----------------
|
20
39
|
|
@@ -44,7 +63,7 @@ In order to build the self contained binaries, you'll need Java 1.6 and
|
|
44
63
|
apt-get install nsis sun-java6-jre
|
45
64
|
git clone git://github.com/mcolyer/smartermeter.git
|
46
65
|
cd smartermeter
|
47
|
-
bundle install --path vendor/gems
|
66
|
+
bundle install --path vendor/gems
|
48
67
|
rake package
|
49
68
|
|
50
69
|
The installer will be generated in pkg/
|
@@ -76,3 +95,4 @@ part of their [Fellowship Program][3]
|
|
76
95
|
[1]: http://nsis.sourceforge.net/
|
77
96
|
[2]: http://brighterplanet.com/
|
78
97
|
[3]: http://brighterplanet.github.com/fellowship.html
|
98
|
+
[rdoc]: http://rdoc.info/github/mcolyer/smartermeter/master/frames
|
data/Rakefile
CHANGED
@@ -48,7 +48,7 @@ task :default => :test
|
|
48
48
|
require 'rake/testtask'
|
49
49
|
Rake::TestTask.new(:test) do |test|
|
50
50
|
test.libs << 'lib' << 'test'
|
51
|
-
test.pattern = '
|
51
|
+
test.pattern = 'spec/**/*_spec.rb'
|
52
52
|
test.verbose = true
|
53
53
|
end
|
54
54
|
|
@@ -122,6 +122,10 @@ task :package_gems do
|
|
122
122
|
specifications.reject! { |s| s.include? "rspec" }
|
123
123
|
specifications.reject! { |s| s.include? "minitar" }
|
124
124
|
specifications.reject! { |s| s.include? "ruby-debug" }
|
125
|
+
specifications.reject! { |s| s.include? "addressable" }
|
126
|
+
specifications.reject! { |s| s.include? "diff-lcs" }
|
127
|
+
specifications.reject! { |s| s.include? "vcr" }
|
128
|
+
specifications.reject! { |s| s.include? "webmock" }
|
125
129
|
dest_dir = File.join(File.dirname(__FILE__), "pkg", "base", "gems", "specifications")
|
126
130
|
FileUtils.rm_rf(dest_dir)
|
127
131
|
FileUtils.mkdir_p(dest_dir)
|
@@ -131,6 +135,10 @@ task :package_gems do
|
|
131
135
|
gems.reject! { |s| s.include? "rspec" }
|
132
136
|
gems.reject! { |s| s.include? "minitar" }
|
133
137
|
gems.reject! { |s| s.include? "ruby-debug" }
|
138
|
+
gems.reject! { |s| s.include? "addressable" }
|
139
|
+
gems.reject! { |s| s.include? "diff-lcs" }
|
140
|
+
gems.reject! { |s| s.include? "vcr" }
|
141
|
+
gems.reject! { |s| s.include? "webmock" }
|
134
142
|
dest_dir = File.join(File.dirname(__FILE__), "pkg", "base", "gems", "gems")
|
135
143
|
FileUtils.rm_rf(dest_dir)
|
136
144
|
FileUtils.mkdir_p(dest_dir)
|
data/lib/smartermeter/daemon.rb
CHANGED
@@ -4,6 +4,7 @@ require 'yaml'
|
|
4
4
|
require 'date'
|
5
5
|
|
6
6
|
module SmarterMeter
|
7
|
+
# @private
|
7
8
|
class Daemon
|
8
9
|
|
9
10
|
def initialize(interface)
|
@@ -63,6 +64,8 @@ module SmarterMeter
|
|
63
64
|
|
64
65
|
# Takes the unencrypted password and encrypts it.
|
65
66
|
def password=(unencrypted)
|
67
|
+
padding_length = 8 - (unencrypted.bytesize % 8)
|
68
|
+
unencrypted = unencrypted + ("\0" * padding_length)
|
66
69
|
@config[:password] = cipher.encrypt_block(unencrypted)
|
67
70
|
end
|
68
71
|
|
@@ -70,7 +73,7 @@ module SmarterMeter
|
|
70
73
|
def password
|
71
74
|
password = @config.fetch(:password, nil)
|
72
75
|
if password
|
73
|
-
cipher.decrypt_block(password)
|
76
|
+
cipher.decrypt_block(password).gsub("\0", "")
|
74
77
|
else
|
75
78
|
password
|
76
79
|
end
|
@@ -180,7 +183,7 @@ module SmarterMeter
|
|
180
183
|
next if data.empty?
|
181
184
|
|
182
185
|
@ui.log.info("Verifying #{date}")
|
183
|
-
samples =
|
186
|
+
samples = Samples.parse_csv(data).values.first
|
184
187
|
first_sample = samples.first
|
185
188
|
|
186
189
|
if first_sample.kwh
|
@@ -207,7 +210,7 @@ module SmarterMeter
|
|
207
210
|
case @config[:transport]
|
208
211
|
when :google_powermeter
|
209
212
|
@ui.log.info("Uploading #{date} to Google PowerMeter")
|
210
|
-
transport = SmarterMeter::
|
213
|
+
transport = SmarterMeter::Services::GooglePowerMeter.new(@config[:google_powermeter])
|
211
214
|
if transport.upload(samples)
|
212
215
|
@ui.log.info("Upload for #{date} complete")
|
213
216
|
else
|
@@ -3,6 +3,7 @@ require 'profligacy/swing'
|
|
3
3
|
require 'profligacy/lel'
|
4
4
|
|
5
5
|
module SmarterMeter
|
6
|
+
# @private
|
6
7
|
module Interfaces
|
7
8
|
class Swing
|
8
9
|
include_package "java.awt"
|
@@ -147,6 +148,7 @@ module SmarterMeter
|
|
147
148
|
end
|
148
149
|
end
|
149
150
|
|
151
|
+
# @private
|
150
152
|
class Wizard
|
151
153
|
include_package "javax.swing"
|
152
154
|
include_package "java.awt"
|
data/lib/smartermeter/sample.rb
CHANGED
@@ -2,67 +2,40 @@ require 'date'
|
|
2
2
|
require 'time'
|
3
3
|
|
4
4
|
module SmarterMeter
|
5
|
+
# Represents a single hourly sample taken from the PG&E hourly electricity
|
6
|
+
# data. It's the most basic data unit of SmarterMeter.
|
5
7
|
class Sample
|
6
8
|
attr_accessor :time, :kwh
|
7
9
|
|
8
|
-
#
|
10
|
+
# Creates a new Sample.
|
9
11
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
def self.parse_csv(data)
|
14
|
-
samples = {}
|
15
|
-
date_re = /([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})/
|
16
|
-
|
17
|
-
# Apparently they felt the need to put a = outside of the correct place
|
18
|
-
data = data.gsub('=','')
|
19
|
-
|
20
|
-
hour_increment = 60*60
|
21
|
-
CSV.parse(data) do |row|
|
22
|
-
next unless row.length > 0 and date_re.match row[0]
|
23
|
-
|
24
|
-
month, day, year = date_re.match(row[0]).captures
|
25
|
-
month = month.to_i
|
26
|
-
day = day.to_i
|
27
|
-
year = year.to_i
|
28
|
-
|
29
|
-
timestamp = Time.local(year, month, day, 0) - hour_increment
|
30
|
-
next if row[1].include? "$"
|
31
|
-
hourly_samples = row[1..24].map do |v|
|
32
|
-
if v == "-"
|
33
|
-
kwh = nil
|
34
|
-
else
|
35
|
-
kwh = v.to_f
|
36
|
-
end
|
37
|
-
|
38
|
-
timestamp = timestamp + hour_increment
|
39
|
-
Sample.new(timestamp, kwh)
|
40
|
-
end
|
41
|
-
samples[Date.new(year, month, day)] = hourly_samples
|
42
|
-
end
|
43
|
-
samples
|
44
|
-
end
|
45
|
-
|
12
|
+
# @param [Time] time the start of the sample period.
|
13
|
+
# @param [Float] kwh the number of killowatt hours used during the period of this sample.
|
14
|
+
# @return [Sample] the new Sample.
|
46
15
|
def initialize(time, kwh)
|
47
16
|
@time = time
|
48
17
|
@kwh = kwh
|
49
18
|
end
|
50
19
|
|
51
|
-
#
|
20
|
+
# The start time of this measurement in UTC.
|
52
21
|
#
|
53
|
-
#
|
22
|
+
# @return [String] the time in the format of 2011-01-06T09:00:00.000Z
|
54
23
|
def utc_start_time
|
55
24
|
@time.utc.strftime("%Y-%m-%dT%H:%M:%S.000Z")
|
56
25
|
end
|
57
26
|
|
58
|
-
#
|
59
|
-
# the start.
|
27
|
+
# The stop time of this measurement in UTC. It's assumed to be 1 hour after
|
28
|
+
# the start as that is the current resolution of PG&E's data.
|
60
29
|
#
|
61
|
-
#
|
30
|
+
# @return [String] the time in the format of 2011-01-06T09:00:00.000Z
|
62
31
|
def utc_stop_time
|
63
|
-
|
32
|
+
one_hour = 60*60
|
33
|
+
(@time + one_hour).utc.strftime("%Y-%m-%dT%H:%M:%S.000Z")
|
64
34
|
end
|
65
35
|
|
36
|
+
# Interrogates the current state of the Sample.
|
37
|
+
#
|
38
|
+
# Returns [String] a compact representation of the Sample.
|
66
39
|
def inspect
|
67
40
|
"<Sample #{@time} #{@kwh}>"
|
68
41
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module SmarterMeter
|
5
|
+
# Represents a collection of samples. In some cases it's useful to operate on
|
6
|
+
# groups of samples and this class provides that functionality.
|
7
|
+
class Samples < Hash
|
8
|
+
# Parses the CSV returned by PG&E and creates a Samples collection.
|
9
|
+
#
|
10
|
+
# @param [String] data the string containing the CSV returned by PG&E
|
11
|
+
# @return [Samples] creates a Samples collection from the given data.
|
12
|
+
def self.parse_csv(data)
|
13
|
+
samples = Samples.new
|
14
|
+
date_re = /([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})/
|
15
|
+
|
16
|
+
# Apparently they felt the need to put a = outside of the correct place
|
17
|
+
data = data.gsub('=','')
|
18
|
+
|
19
|
+
hour_increment = 60*60
|
20
|
+
CSV.parse(data) do |row|
|
21
|
+
next unless row.length > 0 and date_re.match row[0]
|
22
|
+
|
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
|
27
|
+
|
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
|
36
|
+
|
37
|
+
timestamp = timestamp + hour_increment
|
38
|
+
Sample.new(timestamp, kwh)
|
39
|
+
end
|
40
|
+
samples[Date.new(year, month, day)] = hourly_samples
|
41
|
+
end
|
42
|
+
samples
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calculates the total number of kilowatt hours for all samples.
|
46
|
+
#
|
47
|
+
# @return [Float] the sum of kilowatt hours for all samples within this collection. If no samples are found 0 is returned.
|
48
|
+
def total_kwh
|
49
|
+
self.keys.reduce(0) { |sum, d| sum + total_kwh_on(d) }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Calculates the total number of kilowatt hours
|
53
|
+
#
|
54
|
+
# @param [Date] date The date of the samples to include within the total.
|
55
|
+
#
|
56
|
+
# @return [Float] the sum of kilowatt hours for samples made of the given day. If none are found 0 is returned.
|
57
|
+
def total_kwh_on(date)
|
58
|
+
if self[date]
|
59
|
+
self[date].reduce(0) { |sum, s| sum + (s.kwh or 0) }
|
60
|
+
else
|
61
|
+
0
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/smartermeter/service.rb
CHANGED
@@ -2,12 +2,21 @@ require 'mechanize'
|
|
2
2
|
require 'csv'
|
3
3
|
|
4
4
|
module SmarterMeter
|
5
|
+
# Provides access to the PG&E SmartMeter data through a ruby interface. This
|
6
|
+
# class depends on the PG&E website to function the same that it does today,
|
7
|
+
# so if something stops working its likely that something changed on PG&E's
|
8
|
+
# site and this class will need to be adapted.
|
5
9
|
class Service
|
6
10
|
LOGIN_URL = "http://www.pge.com/myhome/"
|
7
11
|
OVERVIEW_URL = "https://www.pge.com/csol/actions/login.do?aw"
|
8
12
|
ENERGYGUIDE_AUTH_URL = "https://www.energyguide.com/LoadAnalysis/LoadAnalysis.aspx?Referrerid=154"
|
9
13
|
|
14
|
+
# Provides access to the last page retrieved by mechanize. Useful for
|
15
|
+
# debugging and reporting errors.
|
10
16
|
attr_reader :last_page
|
17
|
+
|
18
|
+
# Provides access to the last exception thrown while accessing PG&E's site.
|
19
|
+
# Useful for debugging/error reporting.
|
11
20
|
attr_reader :last_exception
|
12
21
|
|
13
22
|
def initialize
|
@@ -16,7 +25,10 @@ module SmarterMeter
|
|
16
25
|
}
|
17
26
|
end
|
18
27
|
|
19
|
-
#
|
28
|
+
# Authenticates to the PG&E's website. Only needs to be performed once per
|
29
|
+
# instance of this class.
|
30
|
+
#
|
31
|
+
# @return [Boolean] true upon succesful login and false otherwise
|
20
32
|
def login(username, password)
|
21
33
|
begin
|
22
34
|
@agent.get(LOGIN_URL) do |page|
|
@@ -61,6 +73,15 @@ module SmarterMeter
|
|
61
73
|
end
|
62
74
|
end
|
63
75
|
|
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.
|
79
|
+
#
|
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.
|
64
85
|
def fetch_csv(date)
|
65
86
|
raise RuntimeException, "login must be called before fetch_csv" unless @authenticated
|
66
87
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'restclient'
|
2
|
+
require 'json'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
module SmarterMeter
|
6
|
+
module Services
|
7
|
+
# Allows users to calculate the carbon produced by their energy usage by
|
8
|
+
# accessing the Brighter Planet CM1 Electricity API.
|
9
|
+
#
|
10
|
+
# More details can be found here:
|
11
|
+
# http://carbon.brighterplanet.com/models/electricity_use
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
# api = SmarterMeter::Services::BrighterPlanet.new
|
15
|
+
# puts api.calculate_kg_carbon(10.1)
|
16
|
+
#
|
17
|
+
# Prints:
|
18
|
+
# 0.6460529833458619
|
19
|
+
#
|
20
|
+
class BrighterPlanet
|
21
|
+
# The api key required for commericial access to the API. For more detail see:
|
22
|
+
# http://carbon.brighterplanet.com/pricing
|
23
|
+
attr_accessor :api_key
|
24
|
+
|
25
|
+
# Initializes the Brigher Planet CM1 Electricity API.
|
26
|
+
#
|
27
|
+
# Example:
|
28
|
+
#
|
29
|
+
# SmarterMeter::Services::BrighterPlanet.new do |c|
|
30
|
+
# c.api_key = "key"
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# @return [BrighterPlanet]
|
34
|
+
def initialize
|
35
|
+
yield self if block_given?
|
36
|
+
end
|
37
|
+
|
38
|
+
# Calculates the number of kilograms of carbon produced from the given
|
39
|
+
# electrical energy usage.
|
40
|
+
#
|
41
|
+
# Note: this is an estimate and depends on many factors, to improve
|
42
|
+
# accuracy include an optional zipcode of where this energy was consumed.
|
43
|
+
#
|
44
|
+
# @param [Float] kwh the number of kilowatt hours consumed.
|
45
|
+
# @params [Hash] opts can include `:zip_code` and/or `:date` to increase the accuracy of the estimate. `:date` should be in the following form: YYYY-MM-DD.
|
46
|
+
# @return [Float] kilograms of carbon produced by this much energy.
|
47
|
+
def calculate_kg_carbon(kwh, opts={})
|
48
|
+
allowed_keys = [:zip_code, :date]
|
49
|
+
non_permitted_keys = opts.keys.reject { |k| allowed_keys.include? k }
|
50
|
+
raise ArgumentError, "opts contains keys not found in #{allowed_keys}" unless non_permitted_keys.empty?
|
51
|
+
|
52
|
+
params = {:energy => kwh}
|
53
|
+
params.merge!(opts)
|
54
|
+
response = RestClient.get 'http://carbon.brighterplanet.com/electricity_uses.json', :params => params
|
55
|
+
JSON.parse(response.body)["emission"]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
File without changes
|
File without changes
|
data/lib/smartermeter.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'smartermeter/sample'
|
2
|
+
require 'smartermeter/samples'
|
2
3
|
require 'smartermeter/service'
|
3
4
|
require 'smartermeter/daemon'
|
4
|
-
require 'smartermeter/
|
5
|
+
require 'smartermeter/services/google_powermeter'
|
6
|
+
require 'smartermeter/services/brighterplanet'
|
5
7
|
require 'smartermeter/interfaces/cli'
|
6
8
|
|
7
9
|
module SmarterMeter
|
8
|
-
VERSION = "0.3.
|
10
|
+
VERSION = "0.3.2"
|
9
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.
|
17
|
-
s.date = '2011-03-
|
16
|
+
s.version = '0.3.2'
|
17
|
+
s.date = '2011-03-29'
|
18
18
|
s.rubyforge_project = 'smartermeter'
|
19
19
|
|
20
20
|
## Make sure your summary is short. The description may be as long
|
@@ -49,11 +49,15 @@ Gem::Specification.new do |s|
|
|
49
49
|
## List your runtime dependencies here. Runtime dependencies are those
|
50
50
|
## that are needed for an end user to actually USE your code.
|
51
51
|
s.add_dependency('mechanize', ["= 1.0.0"])
|
52
|
-
s.add_dependency('
|
52
|
+
s.add_dependency('crypt19', ["= 1.2.1"])
|
53
|
+
s.add_dependency('rest-client', ["= 1.6.1"])
|
54
|
+
s.add_dependency('json_pure', ["= 1.5.1"])
|
53
55
|
|
54
56
|
## List your development dependencies here. Development dependencies are
|
55
57
|
## those that are only needed during development
|
56
|
-
s.add_development_dependency('rspec', ["~> 2.
|
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"])
|
57
61
|
s.add_development_dependency('minitar', ["~> 0.5.2"])
|
58
62
|
|
59
63
|
## Leave this section as-is. It will be automatically generated from the
|
@@ -79,21 +83,26 @@ Gem::Specification.new do |s|
|
|
79
83
|
lib/smartermeter/interfaces/cli.rb
|
80
84
|
lib/smartermeter/interfaces/swing.rb
|
81
85
|
lib/smartermeter/sample.rb
|
86
|
+
lib/smartermeter/samples.rb
|
82
87
|
lib/smartermeter/service.rb
|
83
|
-
lib/smartermeter/
|
84
|
-
lib/smartermeter/
|
85
|
-
lib/smartermeter/
|
88
|
+
lib/smartermeter/services/brighterplanet.rb
|
89
|
+
lib/smartermeter/services/cacert.pem
|
90
|
+
lib/smartermeter/services/google_powermeter.erb
|
91
|
+
lib/smartermeter/services/google_powermeter.rb
|
86
92
|
smartermeter.gemspec
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
93
102
|
]
|
94
103
|
# = MANIFEST =
|
95
104
|
|
96
105
|
## Test files will be grabbed from the file list. Make sure the path glob
|
97
106
|
## matches what you actually use.
|
98
|
-
s.test_files = s.files.select { |path| path =~ /^
|
107
|
+
s.test_files = s.files.select { |path| path =~ /^spec\/*_spec.rb/ }
|
99
108
|
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,106 @@
|
|
1
|
+
---
|
2
|
+
- !ruby/struct:VCR::HTTPInteraction
|
3
|
+
request: !ruby/struct:VCR::Request
|
4
|
+
method: :get
|
5
|
+
uri: http://carbon.brighterplanet.com:80/electricity_uses.json?energy=1
|
6
|
+
body:
|
7
|
+
headers:
|
8
|
+
accept:
|
9
|
+
- "*/*; q=0.5, application/xml"
|
10
|
+
accept-encoding:
|
11
|
+
- gzip, deflate
|
12
|
+
response: !ruby/struct:VCR::Response
|
13
|
+
status: !ruby/struct:VCR::ResponseStatus
|
14
|
+
code: 200
|
15
|
+
message: OK
|
16
|
+
headers:
|
17
|
+
server:
|
18
|
+
- nginx/0.6.35
|
19
|
+
date:
|
20
|
+
- Sat, 19 Mar 2011 18:49:12 GMT
|
21
|
+
content-type:
|
22
|
+
- application/json; charset=utf-8
|
23
|
+
transfer-encoding:
|
24
|
+
- chunked
|
25
|
+
status:
|
26
|
+
- 200 OK
|
27
|
+
etag:
|
28
|
+
- "\"f8891da38901f85f0b33fdd539c84199\""
|
29
|
+
cache-control:
|
30
|
+
- max-age=0, private, must-revalidate
|
31
|
+
x-ua-compatible:
|
32
|
+
- IE=Edge,chrome=1
|
33
|
+
x-runtime:
|
34
|
+
- "0.174617"
|
35
|
+
body: "{\"timeframe\":\"2011-01-01/2012-01-01\",\"emission\":0.6460529833458619,\"emission_units\":\"kilograms\",\"errors\":[],\"reports\":[{\"committee\":{\"name\":\"emission\",\"quorums\":[{\"name\":\"from date, emission factor, loss factor and energy\",\"requirements\":[\"date\",\"emission_factor\",\"loss_factor\",\"energy\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":0.6460529833458619,\"quorum\":{\"name\":\"from date, emission factor, loss factor and energy\",\"requirements\":[\"date\",\"emission_factor\",\"loss_factor\",\"energy\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"emission_factor\",\"quorums\":[{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":0.606076,\"quorum\":{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"loss_factor\",\"quorums\":[{\"name\":\"from eGRID region\",\"requirements\":[\"egrid_region\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":0.0618788,\"quorum\":{\"name\":\"from eGRID region\",\"requirements\":[\"egrid_region\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"egrid_region\",\"quorums\":[{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":{\"egrid_region\":{\"loss_factor\":0.0618788,\"name\":\"US\"}},\"quorum\":{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"egrid_subregion\",\"quorums\":[{\"name\":\"from zip code\",\"requirements\":[\"zip_code\"],\"appreciates\":[],\"complies\":[]},{\"name\":\"default\",\"requirements\":[],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":{\"egrid_subregion\":{\"abbreviation\":\"US\",\"egrid_region_name\":\"US\",\"electricity_ch4_emission_factor\":0.000309198,\"electricity_ch4_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_co2_biogenic_emission_factor\":0.0,\"electricity_co2_biogenic_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_co2_emission_factor\":0.602982,\"electricity_co2_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_emission_factor\":0.606076,\"electricity_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_n2o_emission_factor\":0.00278485,\"electricity_n2o_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"name\":\"United States\",\"nerc_abbreviation\":\"US\",\"net_generation\":4056440000.0,\"net_generation_units\":\"megawatt_hours\"}},\"quorum\":{\"name\":\"default\",\"requirements\":[],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"date\",\"quorums\":[{\"name\":\"from timeframe\",\"requirements\":[],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":\"2011-01-01\",\"quorum\":{\"name\":\"from timeframe\",\"requirements\":[],\"appreciates\":[],\"complies\":[]}}],\"methodology\":\"http://carbon.brighterplanet.com/electricity_uses.html?energy=1&timeframe=2011-01-01%2F2012-01-01\",\"execution_id\":\"cd84d18a392c2782e8b0a114dd65a225184823a4be28aea4896d2d6c01d18dae959051b2a26b06b6\",\"compliance\":[],\"scope\":null,\"energy\":1.0,\"date\":\"2011-01-01\",\"egrid_subregion\":{\"egrid_subregion\":{\"abbreviation\":\"US\",\"egrid_region_name\":\"US\",\"electricity_ch4_emission_factor\":0.000309198,\"electricity_ch4_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_co2_biogenic_emission_factor\":0.0,\"electricity_co2_biogenic_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_co2_emission_factor\":0.602982,\"electricity_co2_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_emission_factor\":0.606076,\"electricity_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_n2o_emission_factor\":0.00278485,\"electricity_n2o_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"name\":\"United States\",\"nerc_abbreviation\":\"US\",\"net_generation\":4056440000.0,\"net_generation_units\":\"megawatt_hours\"}},\"egrid_region\":{\"egrid_region\":{\"loss_factor\":0.0618788,\"name\":\"US\"}},\"loss_factor\":0.0618788,\"emission_factor\":0.606076}"
|
36
|
+
http_version: "1.1"
|
37
|
+
- !ruby/struct:VCR::HTTPInteraction
|
38
|
+
request: !ruby/struct:VCR::Request
|
39
|
+
method: :get
|
40
|
+
uri: http://carbon.brighterplanet.com:80/electricity_uses.json?energy=1&zip_code=94110
|
41
|
+
body:
|
42
|
+
headers:
|
43
|
+
accept:
|
44
|
+
- "*/*; q=0.5, application/xml"
|
45
|
+
accept-encoding:
|
46
|
+
- gzip, deflate
|
47
|
+
response: !ruby/struct:VCR::Response
|
48
|
+
status: !ruby/struct:VCR::ResponseStatus
|
49
|
+
code: 200
|
50
|
+
message: OK
|
51
|
+
headers:
|
52
|
+
server:
|
53
|
+
- nginx/0.6.35
|
54
|
+
date:
|
55
|
+
- Sat, 19 Mar 2011 19:15:01 GMT
|
56
|
+
content-type:
|
57
|
+
- application/json; charset=utf-8
|
58
|
+
transfer-encoding:
|
59
|
+
- chunked
|
60
|
+
status:
|
61
|
+
- 200 OK
|
62
|
+
etag:
|
63
|
+
- "\"91afe2c319e60d06dd3b10f60c90a15d\""
|
64
|
+
cache-control:
|
65
|
+
- max-age=0, private, must-revalidate
|
66
|
+
x-ua-compatible:
|
67
|
+
- IE=Edge,chrome=1
|
68
|
+
x-runtime:
|
69
|
+
- "0.041468"
|
70
|
+
body: "{\"timeframe\":\"2011-01-01/2012-01-01\",\"emission\":0.3484751557886446,\"emission_units\":\"kilograms\",\"errors\":[],\"reports\":[{\"committee\":{\"name\":\"emission\",\"quorums\":[{\"name\":\"from date, emission factor, loss factor and energy\",\"requirements\":[\"date\",\"emission_factor\",\"loss_factor\",\"energy\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":0.3484751557886446,\"quorum\":{\"name\":\"from date, emission factor, loss factor and energy\",\"requirements\":[\"date\",\"emission_factor\",\"loss_factor\",\"energy\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"emission_factor\",\"quorums\":[{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":0.32989,\"quorum\":{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"loss_factor\",\"quorums\":[{\"name\":\"from eGRID region\",\"requirements\":[\"egrid_region\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":0.0533328,\"quorum\":{\"name\":\"from eGRID region\",\"requirements\":[\"egrid_region\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"egrid_region\",\"quorums\":[{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":{\"egrid_region\":{\"loss_factor\":0.0533328,\"name\":\"W\"}},\"quorum\":{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"egrid_subregion\",\"quorums\":[{\"name\":\"from zip code\",\"requirements\":[\"zip_code\"],\"appreciates\":[],\"complies\":[]},{\"name\":\"default\",\"requirements\":[],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":{\"egrid_subregion\":{\"abbreviation\":\"CAMX\",\"egrid_region_name\":\"W\",\"electricity_ch4_emission_factor\":0.000342876,\"electricity_ch4_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_co2_biogenic_emission_factor\":0.0,\"electricity_co2_biogenic_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_co2_emission_factor\":0.328455,\"electricity_co2_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_emission_factor\":0.32989,\"electricity_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_n2o_emission_factor\":0.00109161,\"electricity_n2o_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"name\":\"WECC California\",\"nerc_abbreviation\":\"WECC\",\"net_generation\":219612000.0,\"net_generation_units\":\"megawatt_hours\"}},\"quorum\":{\"name\":\"from zip code\",\"requirements\":[\"zip_code\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"date\",\"quorums\":[{\"name\":\"from timeframe\",\"requirements\":[],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":\"2011-01-01\",\"quorum\":{\"name\":\"from timeframe\",\"requirements\":[],\"appreciates\":[],\"complies\":[]}}],\"methodology\":\"http://carbon.brighterplanet.com/electricity_uses.html?energy=1&timeframe=2011-01-01%2F2012-01-01&zip_code=94110\",\"execution_id\":\"863c7b45fcba5ba36bb2389f518b51be1665166db5f6a3c8d7ce3a3950c38d7e07d551fad2e7f4e7\",\"compliance\":[],\"scope\":null,\"energy\":1.0,\"zip_code\":{\"zip_code\":{\"climate_division_name\":\"CA4\",\"description\":\"San Francisco\",\"egrid_subregion_abbreviation\":\"CAMX\",\"latitude\":\"37.74873\",\"longitude\":\"-122.41545\",\"name\":\"94110\",\"state_postal_abbreviation\":\"CA\"}},\"date\":\"2011-01-01\",\"egrid_subregion\":{\"egrid_subregion\":{\"abbreviation\":\"CAMX\",\"egrid_region_name\":\"W\",\"electricity_ch4_emission_factor\":0.000342876,\"electricity_ch4_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_co2_biogenic_emission_factor\":0.0,\"electricity_co2_biogenic_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_co2_emission_factor\":0.328455,\"electricity_co2_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_emission_factor\":0.32989,\"electricity_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_n2o_emission_factor\":0.00109161,\"electricity_n2o_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"name\":\"WECC California\",\"nerc_abbreviation\":\"WECC\",\"net_generation\":219612000.0,\"net_generation_units\":\"megawatt_hours\"}},\"egrid_region\":{\"egrid_region\":{\"loss_factor\":0.0533328,\"name\":\"W\"}},\"loss_factor\":0.0533328,\"emission_factor\":0.32989}"
|
71
|
+
http_version: "1.1"
|
72
|
+
- !ruby/struct:VCR::HTTPInteraction
|
73
|
+
request: !ruby/struct:VCR::Request
|
74
|
+
method: :get
|
75
|
+
uri: http://carbon.brighterplanet.com:80/electricity_uses.json?date=2011-03-29&energy=1
|
76
|
+
body:
|
77
|
+
headers:
|
78
|
+
accept:
|
79
|
+
- "*/*; q=0.5, application/xml"
|
80
|
+
accept-encoding:
|
81
|
+
- gzip, deflate
|
82
|
+
response: !ruby/struct:VCR::Response
|
83
|
+
status: !ruby/struct:VCR::ResponseStatus
|
84
|
+
code: 200
|
85
|
+
message: OK
|
86
|
+
headers:
|
87
|
+
server:
|
88
|
+
- nginx/0.6.35
|
89
|
+
date:
|
90
|
+
- Tue, 29 Mar 2011 15:36:35 GMT
|
91
|
+
content-type:
|
92
|
+
- application/json; charset=utf-8
|
93
|
+
transfer-encoding:
|
94
|
+
- chunked
|
95
|
+
status:
|
96
|
+
- 200 OK
|
97
|
+
etag:
|
98
|
+
- "\"642c6acc898b96f4017bc27c2f321ee4\""
|
99
|
+
cache-control:
|
100
|
+
- max-age=0, private, must-revalidate
|
101
|
+
x-ua-compatible:
|
102
|
+
- IE=Edge,chrome=1
|
103
|
+
x-runtime:
|
104
|
+
- "0.026969"
|
105
|
+
body: "{\"timeframe\":\"2011-01-01/2012-01-01\",\"emission\":0.6460529833458619,\"emission_units\":\"kilograms\",\"errors\":[],\"reports\":[{\"committee\":{\"name\":\"emission\",\"quorums\":[{\"name\":\"from date, emission factor, loss factor and energy\",\"requirements\":[\"date\",\"emission_factor\",\"loss_factor\",\"energy\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":0.6460529833458619,\"quorum\":{\"name\":\"from date, emission factor, loss factor and energy\",\"requirements\":[\"date\",\"emission_factor\",\"loss_factor\",\"energy\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"emission_factor\",\"quorums\":[{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":0.606076,\"quorum\":{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"loss_factor\",\"quorums\":[{\"name\":\"from eGRID region\",\"requirements\":[\"egrid_region\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":0.0618788,\"quorum\":{\"name\":\"from eGRID region\",\"requirements\":[\"egrid_region\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"egrid_region\",\"quorums\":[{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":{\"egrid_region\":{\"loss_factor\":0.0618788,\"name\":\"US\"}},\"quorum\":{\"name\":\"from eGRID subregion\",\"requirements\":[\"egrid_subregion\"],\"appreciates\":[],\"complies\":[]}},{\"committee\":{\"name\":\"egrid_subregion\",\"quorums\":[{\"name\":\"from zip code\",\"requirements\":[\"zip_code\"],\"appreciates\":[],\"complies\":[]},{\"name\":\"default\",\"requirements\":[],\"appreciates\":[],\"complies\":[]}]},\"conclusion\":{\"egrid_subregion\":{\"abbreviation\":\"US\",\"egrid_region_name\":\"US\",\"electricity_ch4_emission_factor\":0.000309198,\"electricity_ch4_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_co2_biogenic_emission_factor\":0.0,\"electricity_co2_biogenic_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_co2_emission_factor\":0.602982,\"electricity_co2_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_emission_factor\":0.606076,\"electricity_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_n2o_emission_factor\":0.00278485,\"electricity_n2o_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"name\":\"United States\",\"nerc_abbreviation\":\"US\",\"net_generation\":4056440000.0,\"net_generation_units\":\"megawatt_hours\"}},\"quorum\":{\"name\":\"default\",\"requirements\":[],\"appreciates\":[],\"complies\":[]}}],\"methodology\":\"http://carbon.brighterplanet.com/electricity_uses.html?date=2011-03-29&energy=1&timeframe=2011-01-01%2F2012-01-01\",\"execution_id\":\"dec9aeca7a2b8750494fdd452337a4b6d8ca3649a7aebafce76d3976f91b0943e4669cf580a7a225\",\"compliance\":[],\"scope\":null,\"date\":\"2011-03-29\",\"energy\":1.0,\"egrid_subregion\":{\"egrid_subregion\":{\"abbreviation\":\"US\",\"egrid_region_name\":\"US\",\"electricity_ch4_emission_factor\":0.000309198,\"electricity_ch4_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_co2_biogenic_emission_factor\":0.0,\"electricity_co2_biogenic_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_co2_emission_factor\":0.602982,\"electricity_co2_emission_factor_units\":\"kilograms_per_kilowatt_hour\",\"electricity_emission_factor\":0.606076,\"electricity_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"electricity_n2o_emission_factor\":0.00278485,\"electricity_n2o_emission_factor_units\":\"kilograms_co2e_per_kilowatt_hour\",\"name\":\"United States\",\"nerc_abbreviation\":\"US\",\"net_generation\":4056440000.0,\"net_generation_units\":\"megawatt_hours\"}},\"egrid_region\":{\"egrid_region\":{\"loss_factor\":0.0618788,\"name\":\"US\"}},\"loss_factor\":0.0618788,\"emission_factor\":0.606076}"
|
106
|
+
http_version: "1.1"
|
data/spec/sample_spec.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SmarterMeter::Samples do
|
4
|
+
before(:all) do
|
5
|
+
@data = File.read(File.join($FIXTURES_DIR, 'data.csv'))
|
6
|
+
@date_present = Date.new(2009, 9, 27)
|
7
|
+
end
|
8
|
+
|
9
|
+
subject { SmarterMeter::Samples.parse_csv(@data) }
|
10
|
+
|
11
|
+
it "can calculate the total number of kilowatt hours used" do
|
12
|
+
subject.total_kwh.should be_within(0.1).of(43.72)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "can calculate the total number of kilowatt hours used on a specific day" do
|
16
|
+
subject.total_kwh_on(Date.new(2009, 9, 27)).should be_within(0.1).of(16.65)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can calculate the total number of kilowatt hours used on a day not given" do
|
20
|
+
subject.total_kwh_on(Date.new(2010, 9, 27)).should == 0
|
21
|
+
end
|
22
|
+
|
23
|
+
it "can access samples for a given day" do
|
24
|
+
subject.length.should == 7
|
25
|
+
subject.keys.should include(@date_present)
|
26
|
+
subject[@date_present].length.should == 24
|
27
|
+
subject[@date_present].each{|s| s.should be_kind_of(SmarterMeter::Sample)}
|
28
|
+
end
|
29
|
+
|
30
|
+
it "can handle an incomplete sample" do
|
31
|
+
subject[@date_present][0].kwh.should == nil
|
32
|
+
subject[@date_present][0].time.should == Time.local(2009, 9, 27, 0)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "can handle a complete sample" do
|
36
|
+
subject[@date_present][1].kwh.should == 0.205
|
37
|
+
subject[@date_present][1].time.should == Time.local(2009, 9, 27, 1)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..")
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe SmarterMeter::Services::BrighterPlanet do
|
5
|
+
it "can determine kilograms of carbon produced from the number of killowatt hours" do
|
6
|
+
VCR.use_cassette('brighterplanet', :record => :new_episodes) do
|
7
|
+
subject.calculate_kg_carbon(1).should be_within(0.1).of(0.64)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it "can query for electricity from a specific zipcode" do
|
12
|
+
VCR.use_cassette('brighterplanet', :record => :new_episodes) do
|
13
|
+
subject.calculate_kg_carbon(1, :zip_code => "94110").should be_within(0.1).of(0.34)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "can query for electricity on a specific date" do
|
18
|
+
VCR.use_cassette('brighterplanet', :record => :new_episodes) do
|
19
|
+
subject.calculate_kg_carbon(1, :date => "2011-03-29").should be_within(0.1).of(0.64)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "doesn't allow electricity queries to include keys other than those specified" do
|
24
|
+
expect { subject.calculate_kg_carbon(1, :energy => 2) }.to raise_error(ArgumentError)
|
25
|
+
end
|
26
|
+
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
$:.unshift File.join(File.dirname(__FILE__), "..")
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
|
-
describe SmarterMeter::
|
4
|
+
describe SmarterMeter::Services::GooglePowerMeter do
|
5
5
|
before(:each) do
|
6
|
-
@subject = SmarterMeter::
|
7
|
-
|
6
|
+
@subject = SmarterMeter::Services::GooglePowerMeter.new(:token => "secret",
|
7
|
+
:variable => "/path")
|
8
8
|
data = File.read(File.join($FIXTURES_DIR, 'data.csv'))
|
9
|
-
@results = SmarterMeter::
|
9
|
+
@results = SmarterMeter::Samples.parse_csv(data)
|
10
10
|
end
|
11
11
|
|
12
12
|
it "can format a request for the API" do
|
@@ -2,8 +2,14 @@ require 'rubygems'
|
|
2
2
|
require 'bundler/setup'
|
3
3
|
Bundler.require(:default)
|
4
4
|
require 'rspec'
|
5
|
+
require 'vcr'
|
5
6
|
|
6
7
|
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
7
8
|
require 'smartermeter'
|
8
9
|
|
9
10
|
$FIXTURES_DIR = File.expand_path(File.join(File.dirname(__FILE__), "fixtures"))
|
11
|
+
|
12
|
+
VCR.config do |c|
|
13
|
+
c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
|
14
|
+
c.stub_with :webmock
|
15
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smartermeter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 3
|
8
|
-
- 1
|
9
|
-
version: 0.3.1
|
4
|
+
prerelease:
|
5
|
+
version: 0.3.2
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- Matt Colyer
|
@@ -14,65 +10,97 @@ autorequire:
|
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
12
|
|
17
|
-
date: 2011-03-
|
13
|
+
date: 2011-03-29 00:00:00 -07:00
|
18
14
|
default_executable: smartermeter
|
19
15
|
dependencies:
|
20
16
|
- !ruby/object:Gem::Dependency
|
21
17
|
name: mechanize
|
22
18
|
prerelease: false
|
23
19
|
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
24
21
|
requirements:
|
25
22
|
- - "="
|
26
23
|
- !ruby/object:Gem::Version
|
27
|
-
segments:
|
28
|
-
- 1
|
29
|
-
- 0
|
30
|
-
- 0
|
31
24
|
version: 1.0.0
|
32
25
|
type: :runtime
|
33
26
|
version_requirements: *id001
|
34
27
|
- !ruby/object:Gem::Dependency
|
35
|
-
name:
|
28
|
+
name: crypt19
|
36
29
|
prerelease: false
|
37
30
|
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
38
32
|
requirements:
|
39
33
|
- - "="
|
40
34
|
- !ruby/object:Gem::Version
|
41
|
-
|
42
|
-
- 1
|
43
|
-
- 1
|
44
|
-
- 4
|
45
|
-
version: 1.1.4
|
35
|
+
version: 1.2.1
|
46
36
|
type: :runtime
|
47
37
|
version_requirements: *id002
|
48
38
|
- !ruby/object:Gem::Dependency
|
49
|
-
name:
|
39
|
+
name: rest-client
|
50
40
|
prerelease: false
|
51
41
|
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - "="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.6.1
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: json_pure
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - "="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 1.5.1
|
58
|
+
type: :runtime
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: rspec
|
62
|
+
prerelease: false
|
63
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
52
65
|
requirements:
|
53
66
|
- - ~>
|
54
67
|
- !ruby/object:Gem::Version
|
55
|
-
|
56
|
-
- 2
|
57
|
-
- 4
|
58
|
-
- 0
|
59
|
-
version: 2.4.0
|
68
|
+
version: 2.5.0
|
60
69
|
type: :development
|
61
|
-
version_requirements: *
|
70
|
+
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: vcr
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ~>
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 1.7.0
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id006
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: webmock
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 1.6.2
|
91
|
+
type: :development
|
92
|
+
version_requirements: *id007
|
62
93
|
- !ruby/object:Gem::Dependency
|
63
94
|
name: minitar
|
64
95
|
prerelease: false
|
65
|
-
requirement: &
|
96
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
66
98
|
requirements:
|
67
99
|
- - ~>
|
68
100
|
- !ruby/object:Gem::Version
|
69
|
-
segments:
|
70
|
-
- 0
|
71
|
-
- 5
|
72
|
-
- 2
|
73
101
|
version: 0.5.2
|
74
102
|
type: :development
|
75
|
-
version_requirements: *
|
103
|
+
version_requirements: *id008
|
76
104
|
description: Fetches SmartMeter data from PG&E and can upload to Google PowerMeter
|
77
105
|
email: matt.removethis@nospam.colyer.name
|
78
106
|
executables:
|
@@ -100,17 +128,22 @@ files:
|
|
100
128
|
- lib/smartermeter/interfaces/cli.rb
|
101
129
|
- lib/smartermeter/interfaces/swing.rb
|
102
130
|
- lib/smartermeter/sample.rb
|
131
|
+
- lib/smartermeter/samples.rb
|
103
132
|
- lib/smartermeter/service.rb
|
104
|
-
- lib/smartermeter/
|
105
|
-
- lib/smartermeter/
|
106
|
-
- lib/smartermeter/
|
133
|
+
- lib/smartermeter/services/brighterplanet.rb
|
134
|
+
- lib/smartermeter/services/cacert.pem
|
135
|
+
- lib/smartermeter/services/google_powermeter.erb
|
136
|
+
- lib/smartermeter/services/google_powermeter.rb
|
107
137
|
- smartermeter.gemspec
|
108
|
-
-
|
109
|
-
-
|
110
|
-
-
|
111
|
-
-
|
112
|
-
-
|
113
|
-
-
|
138
|
+
- spec/fixtures/data.csv
|
139
|
+
- spec/fixtures/expected_google_request.xml
|
140
|
+
- spec/fixtures/vcr_cassettes/brighterplanet.yml
|
141
|
+
- spec/sample_spec.rb
|
142
|
+
- spec/samples_spec.rb
|
143
|
+
- spec/service_spec.rb
|
144
|
+
- spec/services/brighterplanet_spec.rb
|
145
|
+
- spec/services/google_powermeter_spec.rb
|
146
|
+
- spec/spec_helper.rb
|
114
147
|
has_rdoc: true
|
115
148
|
homepage: http://github.com/mcolyer/smartermeter
|
116
149
|
licenses: []
|
@@ -121,23 +154,21 @@ rdoc_options: []
|
|
121
154
|
require_paths:
|
122
155
|
- lib
|
123
156
|
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
none: false
|
124
158
|
requirements:
|
125
159
|
- - ">="
|
126
160
|
- !ruby/object:Gem::Version
|
127
|
-
segments:
|
128
|
-
- 0
|
129
161
|
version: "0"
|
130
162
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
|
+
none: false
|
131
164
|
requirements:
|
132
165
|
- - ">="
|
133
166
|
- !ruby/object:Gem::Version
|
134
|
-
segments:
|
135
|
-
- 0
|
136
167
|
version: "0"
|
137
168
|
requirements: []
|
138
169
|
|
139
170
|
rubyforge_project: smartermeter
|
140
|
-
rubygems_version: 1.
|
171
|
+
rubygems_version: 1.5.0
|
141
172
|
signing_key:
|
142
173
|
specification_version: 2
|
143
174
|
summary: Fetches SmartMeter data from PG&E
|
data/specs/sample_spec.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
$:.unshift File.join(File.dirname(__FILE__))
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe SmarterMeter::Sample do
|
5
|
-
before(:all) do
|
6
|
-
@data = File.read(File.join($FIXTURES_DIR, 'data.csv'))
|
7
|
-
end
|
8
|
-
|
9
|
-
it "should be able to parse csv data returned by the api" do
|
10
|
-
date = Date.new(2009, 9, 27)
|
11
|
-
results = SmarterMeter::Sample.parse_csv(@data)
|
12
|
-
|
13
|
-
results.length.should == 7
|
14
|
-
results.keys.should include(date)
|
15
|
-
results[date].length.should == 24
|
16
|
-
results[date].each{|s| s.should be_kind_of(SmarterMeter::Sample)}
|
17
|
-
|
18
|
-
results[date][0].kwh.should == nil
|
19
|
-
results[date][0].time.should == Time.local(2009, 9, 27, 0)
|
20
|
-
|
21
|
-
results[date][1].kwh.should == 0.205
|
22
|
-
results[date][1].time.should == Time.local(2009, 9, 27, 1)
|
23
|
-
end
|
24
|
-
end
|