smartermeter 0.1.0 → 0.2.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 +4 -0
- data/Gemfile +3 -0
- data/Rakefile +5 -2
- data/bin/smartermeter +2 -1
- data/lib/smartermeter/daemon.rb +52 -54
- data/lib/smartermeter/interfaces/cli.rb +41 -0
- data/lib/smartermeter/interfaces/swing.rb +85 -0
- data/lib/smartermeter/main.rb +3 -1
- data/lib/smartermeter/service.rb +66 -48
- data/lib/smartermeter.rb +2 -1
- data/smartermeter.gemspec +5 -2
- metadata +6 -3
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -86,7 +86,7 @@ namespace :rawr do
|
|
86
86
|
dir = File.join(File.dirname(__FILE__), "vendor", "gems")
|
87
87
|
FileUtils.rm_rf(dir)
|
88
88
|
FileUtils.mkdir_p(dir)
|
89
|
-
["nokogiri", "mechanize", "crypt"].each do |gem|
|
89
|
+
["nokogiri", "mechanize", "crypt", "profligacy"].each do |gem|
|
90
90
|
`gem unpack -t "#{dir}" #{gem}`
|
91
91
|
end
|
92
92
|
|
@@ -100,6 +100,9 @@ namespace :rawr do
|
|
100
100
|
Dir.glob(File.join(dir, "nokogiri.old", "lib", "*")).each do |file|
|
101
101
|
FileUtils.mv(file, File.join(dir))
|
102
102
|
end
|
103
|
+
Dir.glob(File.join(dir, "profligacy.old", "lib", "*")).each do |file|
|
104
|
+
FileUtils.mv(file, File.join(dir))
|
105
|
+
end
|
103
106
|
FileUtils.mv(File.join(dir, "crypt.old", "crypt"), File.join(dir, "crypt"))
|
104
107
|
Dir.glob(File.join(dir, "mechanize.old", "lib", "*")).each do |file|
|
105
108
|
FileUtils.mv(file, File.join(dir))
|
@@ -127,7 +130,7 @@ task :release => :build do
|
|
127
130
|
sh "git tag v#{version}"
|
128
131
|
sh "git push origin master"
|
129
132
|
sh "git push origin v#{version}"
|
130
|
-
|
133
|
+
sh "gem push pkg/#{name}-#{version}.gem"
|
131
134
|
end
|
132
135
|
|
133
136
|
desc "Build #{gem_file} into the pkg directory"
|
data/bin/smartermeter
CHANGED
data/lib/smartermeter/daemon.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
|
+
require 'fileutils'
|
1
2
|
require 'crypt/blowfish'
|
2
3
|
require 'yaml'
|
3
|
-
require 'logger'
|
4
4
|
require 'date'
|
5
5
|
|
6
6
|
module SmarterMeter
|
7
7
|
class Daemon
|
8
8
|
|
9
|
+
def initialize(interface)
|
10
|
+
@ui = interface
|
11
|
+
end
|
12
|
+
|
9
13
|
# Loads the configuration, and starts
|
10
14
|
#
|
11
15
|
# Never returns.
|
@@ -28,13 +32,6 @@ module SmarterMeter
|
|
28
32
|
File.expand_path(File.join(@config[:data_dir], date.strftime("%Y-%m-%d.csv")))
|
29
33
|
end
|
30
34
|
|
31
|
-
def log
|
32
|
-
return @logger if @logger
|
33
|
-
@logger = Logger.new STDOUT
|
34
|
-
@logger.level = Logger::INFO
|
35
|
-
@logger
|
36
|
-
end
|
37
|
-
|
38
35
|
# Loads the configuration and prompts for required settings if they are
|
39
36
|
# missing.
|
40
37
|
#
|
@@ -49,7 +46,7 @@ module SmarterMeter
|
|
49
46
|
# Returns the configuration hash.
|
50
47
|
def load_configuration
|
51
48
|
@config = {
|
52
|
-
:start_date => Date.today,
|
49
|
+
:start_date => Date.today - 1,
|
53
50
|
:data_dir => default_data_dir
|
54
51
|
}
|
55
52
|
|
@@ -79,32 +76,22 @@ module SmarterMeter
|
|
79
76
|
end
|
80
77
|
end
|
81
78
|
|
79
|
+
# Returns true if the all of the required configuration has been set.
|
80
|
+
def has_configuration?
|
81
|
+
@config[:username] and @config[:password]
|
82
|
+
end
|
83
|
+
|
82
84
|
# Prompts the user for required settings that are blank.
|
83
85
|
#
|
84
86
|
# Returns nothing.
|
85
87
|
def verify_configuration
|
86
|
-
return if
|
87
|
-
|
88
|
-
puts
|
89
|
-
puts "Smartermeter: Initial Configuration"
|
90
|
-
puts "--------------------------------------------------------------------------------"
|
91
|
-
puts "This program stores your PG&E account username and password on disk. The"
|
92
|
-
puts "password is encrypted but could be retrieved fairly easily. If this makes you"
|
93
|
-
puts "uncomfortable quit now (use ctrl-c)."
|
94
|
-
puts "--------------------------------------------------------------------------------"
|
95
|
-
|
96
|
-
unless @config[:username]
|
97
|
-
print "PG&E account username: "
|
98
|
-
@config[:username] = gets.strip
|
99
|
-
end
|
88
|
+
return if has_configuration?
|
100
89
|
|
101
|
-
|
102
|
-
|
103
|
-
self.password =
|
90
|
+
@ui.setup do |config|
|
91
|
+
@config.merge!(config)
|
92
|
+
self.password = config[:password] if config.has_key? :password
|
93
|
+
save_configuration
|
104
94
|
end
|
105
|
-
|
106
|
-
save_configuration
|
107
|
-
puts "Setup complete"
|
108
95
|
end
|
109
96
|
|
110
97
|
# Saves the current configuration to disk.
|
@@ -124,13 +111,19 @@ module SmarterMeter
|
|
124
111
|
one_hour = 60 * 60
|
125
112
|
|
126
113
|
while true
|
114
|
+
unless has_configuration?
|
115
|
+
@ui.log.info("Waiting for configuration")
|
116
|
+
sleep(5)
|
117
|
+
next
|
118
|
+
end
|
119
|
+
|
127
120
|
dates = dates_requiring_data
|
128
121
|
unless dates.empty?
|
129
|
-
log.info("Attempting to fetch data for: #{dates.join(",")}")
|
122
|
+
@ui.log.info("Attempting to fetch data for: #{dates.join(",")}")
|
130
123
|
results = fetch_dates(dates)
|
131
|
-
log.info("Successfully fetched: #{results.join(",")}")
|
124
|
+
@ui.log.info("Successfully fetched: #{results.join(",")}")
|
132
125
|
else
|
133
|
-
log.info("Sleeping")
|
126
|
+
@ui.log.info("Sleeping")
|
134
127
|
end
|
135
128
|
sleep(one_hour)
|
136
129
|
end
|
@@ -141,32 +134,34 @@ module SmarterMeter
|
|
141
134
|
# Note: An authorization failure will cause an exits, as it is a dire
|
142
135
|
# condition.
|
143
136
|
#
|
144
|
-
# Returns a new Service instance which has been properly authorized
|
137
|
+
# Returns a new Service instance which has been properly authorized and nil
|
138
|
+
# otherwise.
|
145
139
|
def service
|
146
140
|
service = Service.new
|
147
|
-
log.info("Logging in as #{@config[:username]}")
|
148
|
-
|
149
|
-
log.
|
150
|
-
|
151
|
-
|
141
|
+
@ui.log.info("Logging in as #{@config[:username]}")
|
142
|
+
if service.login(@config[:username], password)
|
143
|
+
@ui.log.info("Logged in as #{@config[:username]}")
|
144
|
+
service
|
145
|
+
else
|
146
|
+
@ui.log.error("Login failed.")
|
147
|
+
@ui.log.error(service.last_page) if service.last_page
|
148
|
+
@ui.log.error(service.last_exception) if service.last_exception
|
149
|
+
@ui.log.error("If this happens repeatedly your login information may be incorrect")
|
150
|
+
@ui.log.error("Remove ~/.smartermeter and restart to re-configure smartermeter.")
|
151
|
+
nil
|
152
152
|
end
|
153
|
-
log.info("Logged in as #{@config[:username]}")
|
154
|
-
service
|
155
153
|
end
|
156
154
|
|
157
155
|
# Connect and authenticate to the PG&E Website.
|
158
156
|
#
|
159
157
|
# It provides an instance of Service to the provided block
|
160
|
-
# for direct manipulation.
|
158
|
+
# for direct manipulation. If there was a failure logging into the service
|
159
|
+
# the block will not be executed.
|
161
160
|
#
|
162
161
|
# Returns nothing.
|
163
162
|
def connect
|
164
163
|
s = service
|
165
|
-
|
166
|
-
yield s
|
167
|
-
rescue SocketError => e
|
168
|
-
log.error("Could not access the PG&E site, are you connected to the Internet?")
|
169
|
-
end
|
164
|
+
yield s if s
|
170
165
|
end
|
171
166
|
|
172
167
|
# Attempts to retrieve power data for each of the dates in the list.
|
@@ -179,25 +174,28 @@ module SmarterMeter
|
|
179
174
|
|
180
175
|
connect do |service|
|
181
176
|
dates.each do |date|
|
182
|
-
log.info("Fetching #{date}")
|
177
|
+
@ui.log.info("Fetching #{date}")
|
178
|
+
|
183
179
|
data = service.fetch_csv(date)
|
180
|
+
next if data.empty?
|
184
181
|
|
185
|
-
log.info("Verifying #{date}")
|
182
|
+
@ui.log.info("Verifying #{date}")
|
186
183
|
samples = Sample.parse_csv(data).values.first
|
187
184
|
first_sample = samples.first
|
188
185
|
|
189
186
|
if first_sample.kwh
|
190
|
-
log.info("Saving #{date}")
|
187
|
+
@ui.log.info("Saving #{date}")
|
188
|
+
FileUtils.mkdir_p(File.dirname(data_file(date)))
|
191
189
|
File.open(data_file(date), "w") do |f|
|
192
190
|
f.write(data)
|
193
191
|
end
|
194
192
|
|
195
193
|
upload(date, samples)
|
196
194
|
|
197
|
-
log.info("Completed #{date}")
|
195
|
+
@ui.log.info("Completed #{date}")
|
198
196
|
completed << date
|
199
197
|
else
|
200
|
-
log.info("Incomplete #{date}")
|
198
|
+
@ui.log.info("Incomplete #{date}")
|
201
199
|
end
|
202
200
|
end
|
203
201
|
end
|
@@ -208,12 +206,12 @@ module SmarterMeter
|
|
208
206
|
def upload(date, samples)
|
209
207
|
case @config[:transport]
|
210
208
|
when :google_powermeter
|
211
|
-
log.info("Uploading #{date} to Google PowerMeter")
|
209
|
+
@ui.log.info("Uploading #{date} to Google PowerMeter")
|
212
210
|
transport = SmarterMeter::Transports::GooglePowerMeter.new(@config[:google_powermeter])
|
213
211
|
if transport.upload(samples)
|
214
|
-
log.info("Upload for #{date} complete")
|
212
|
+
@ui.log.info("Upload for #{date} complete")
|
215
213
|
else
|
216
|
-
log.info("Upload for #{date} failed")
|
214
|
+
@ui.log.info("Upload for #{date} failed")
|
217
215
|
end
|
218
216
|
end
|
219
217
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module SmarterMeter
|
4
|
+
module Interfaces
|
5
|
+
class CLI
|
6
|
+
# Returns a logger like interface to log errors and warnings to.
|
7
|
+
def log
|
8
|
+
return @logger if @logger
|
9
|
+
@logger = Logger.new STDOUT
|
10
|
+
@logger.level = Logger::INFO
|
11
|
+
@logger
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: Called when ~/.smartermeter needs to be configured.
|
15
|
+
# Yields a hash containing the configuration by the user.
|
16
|
+
#
|
17
|
+
# Returns nothing
|
18
|
+
def setup
|
19
|
+
puts
|
20
|
+
puts "Smartermeter: Initial Configuration"
|
21
|
+
puts "--------------------------------------------------------------------------------"
|
22
|
+
puts "This program stores your PG&E account username and password on disk. The"
|
23
|
+
puts "password is encrypted but could be retrieved fairly easily. If this makes you"
|
24
|
+
puts "uncomfortable quit now (use ctrl-c)."
|
25
|
+
puts "--------------------------------------------------------------------------------"
|
26
|
+
|
27
|
+
config = {}
|
28
|
+
|
29
|
+
print "PG&E account username: "
|
30
|
+
config[:username] = gets.strip
|
31
|
+
|
32
|
+
print "PG&E account password: "
|
33
|
+
config[:password] = gets.strip
|
34
|
+
|
35
|
+
puts "Configuration finished"
|
36
|
+
|
37
|
+
yield config
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'profligacy/swing'
|
3
|
+
require 'profligacy/lel'
|
4
|
+
|
5
|
+
module SmarterMeter
|
6
|
+
module Interfaces
|
7
|
+
class Swing
|
8
|
+
include_package "java.awt"
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
# TODO: Implement a way to update the settings
|
12
|
+
#settings_item = MenuItem.new("Settings")
|
13
|
+
#settings_item.add_action_listener { SettingsWindow.new do |config|
|
14
|
+
# puts config.inspect
|
15
|
+
# end
|
16
|
+
#}
|
17
|
+
|
18
|
+
exit_item = MenuItem.new("Exit")
|
19
|
+
exit_item.add_action_listener {java.lang.System::exit(0)}
|
20
|
+
|
21
|
+
popup = PopupMenu.new
|
22
|
+
#popup.add(settings_item)
|
23
|
+
popup.add(exit_item)
|
24
|
+
|
25
|
+
image = Toolkit::default_toolkit.get_image("icon.png")
|
26
|
+
tray_icon = TrayIcon.new(image, "Smartermeter", popup)
|
27
|
+
tray_icon.image_auto_size = true
|
28
|
+
|
29
|
+
tray = SystemTray::system_tray
|
30
|
+
tray.add(tray_icon)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns a logger like interface to log errors and warnings to.
|
34
|
+
def log
|
35
|
+
return @logger if @logger
|
36
|
+
@logger = Logger.new(File.expand_path("~/.smartermeter.log"))
|
37
|
+
@logger.level = Logger::INFO
|
38
|
+
@logger
|
39
|
+
end
|
40
|
+
|
41
|
+
# Public: Called when ~/.smartermeter needs to be configured.
|
42
|
+
# Yields a hash containing the configuration specified by the user.
|
43
|
+
#
|
44
|
+
# Returns nothing.
|
45
|
+
def setup
|
46
|
+
SettingsWindow.new do |config|
|
47
|
+
yield config
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class SettingsWindow
|
53
|
+
include_package "javax.swing"
|
54
|
+
|
55
|
+
def initialize(&block)
|
56
|
+
layout = "
|
57
|
+
[ username_label | (150)username_field ]
|
58
|
+
[ password_label | (150)password_field ]
|
59
|
+
[ _ | >save_button ]
|
60
|
+
"
|
61
|
+
|
62
|
+
@ui = Profligacy::Swing::LEL.new(JFrame, layout) do |c,i|
|
63
|
+
c.username_label = JLabel.new "PG&E Username:"
|
64
|
+
c.username_field = JTextField.new
|
65
|
+
c.password_label = JLabel.new "PG&E Password:"
|
66
|
+
c.password_field = JPasswordField.new
|
67
|
+
c.save_button = JButton.new("Save")
|
68
|
+
|
69
|
+
i.save_button = { :action => proc do |t, e|
|
70
|
+
config = {
|
71
|
+
:username => @ui.username_field.text,
|
72
|
+
:password => @ui.password_field.text
|
73
|
+
}
|
74
|
+
@frame.dispose
|
75
|
+
yield config
|
76
|
+
end
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
@frame = @ui.build(:args => "Smartermeter Settings")
|
81
|
+
@frame.defaultCloseOperation = JFrame::DISPOSE_ON_CLOSE
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/smartermeter/main.rb
CHANGED
data/lib/smartermeter/service.rb
CHANGED
@@ -7,6 +7,9 @@ module SmarterMeter
|
|
7
7
|
OVERVIEW_URL = "https://www.pge.com/csol/actions/login.do?aw"
|
8
8
|
ENERGYGUIDE_AUTH_URL = "https://www.energyguide.com/LoadAnalysis/LoadAnalysis.aspx?Referrerid=154"
|
9
9
|
|
10
|
+
attr_reader :last_page
|
11
|
+
attr_reader :last_exception
|
12
|
+
|
10
13
|
def initialize
|
11
14
|
@agent = WWW::Mechanize.new { |agent|
|
12
15
|
agent.user_agent_alias = 'Mac Safari'
|
@@ -15,64 +18,79 @@ module SmarterMeter
|
|
15
18
|
|
16
19
|
# Returns true upon succesful login and false otherwise
|
17
20
|
def login(username, password)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
begin
|
22
|
+
@agent.get(LOGIN_URL) do |page|
|
23
|
+
logged_in_page = page.form_with(:action => 'https://www.pge.com/eum/login') do |login|
|
24
|
+
login.USER = username
|
25
|
+
login.PASSWORD = password
|
26
|
+
end.submit
|
27
|
+
end
|
28
|
+
|
29
|
+
# There is a crazy meta-reload thing here that mechanize doesn't handle
|
30
|
+
# correctly by itself so let's help it along...
|
31
|
+
@agent.get(OVERVIEW_URL) do |page|
|
32
|
+
|
33
|
+
return false if page.title =~ /PG&E Login/
|
34
|
+
|
35
|
+
# Load the PG&E Terms of Use page
|
36
|
+
tou_link = page.link_with(:href => '/csol/actions/billingDisclaimer.do?actionType=hourly')
|
37
|
+
unless tou_link
|
38
|
+
@last_page = page
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
tou_page = @agent.click(tou_link)
|
42
|
+
form = tou_page.forms().first
|
43
|
+
agree_button = form.button_with(:value => 'I Understand - Proceed')
|
44
|
+
|
45
|
+
# Agree to the terms of use
|
46
|
+
form['agreement'] = 'yes'
|
47
|
+
|
48
|
+
# Load up the PG&E frame page for historical data
|
49
|
+
hourly_usage_container = form.submit(agree_button)
|
50
|
+
|
51
|
+
# Now load up the frame with the content
|
52
|
+
hourly_usage = @agent.click(hourly_usage_container.frames.select{|f| f.href == "/csol/nexus/content.jsp"}.first)
|
53
|
+
|
54
|
+
# Now post the authentication information from PG&E to energyguide.com
|
55
|
+
@data_page = hourly_usage.form_with(:action => ENERGYGUIDE_AUTH_URL).submit
|
56
|
+
end
|
57
|
+
@authenticated = true
|
58
|
+
rescue Exception => e
|
59
|
+
@last_exception = e
|
60
|
+
return false
|
23
61
|
end
|
24
|
-
|
25
|
-
# There is a crazy meta-reload thing here that mechanize doesn't handle
|
26
|
-
# correctly by itself so let's help it along...
|
27
|
-
@agent.get(OVERVIEW_URL) do |page|
|
28
|
-
|
29
|
-
return false if page.title =~ /PG&E Login/
|
30
|
-
|
31
|
-
# Load the PG&E Terms of Use page
|
32
|
-
tou_page = @agent.click(page.link_with(:href => '/csol/actions/billingDisclaimer.do?actionType=hourly'))
|
33
|
-
form = tou_page.forms().first
|
34
|
-
agree_button = form.button_with(:value => 'I Understand - Proceed')
|
35
|
-
|
36
|
-
# Agree to the terms of use
|
37
|
-
form['agreement'] = 'yes'
|
38
|
-
|
39
|
-
# Load up the PG&E frame page for historical data
|
40
|
-
hourly_usage_container = form.submit(agree_button)
|
41
|
-
|
42
|
-
# Now load up the frame with the content
|
43
|
-
hourly_usage = @agent.click(hourly_usage_container.frames.select{|f| f.href == "/csol/nexus/content.jsp"}.first)
|
44
|
-
|
45
|
-
# Now post the authentication information from PG&E to energyguide.com
|
46
|
-
@data_page = hourly_usage.form_with(:action => ENERGYGUIDE_AUTH_URL).submit
|
47
|
-
end
|
48
|
-
true
|
49
62
|
end
|
50
63
|
|
51
64
|
def fetch_csv(date)
|
52
|
-
|
65
|
+
raise RuntimeException, "login must be called before fetch_csv" unless @authenticated
|
53
66
|
|
54
67
|
# Now we almost actually have data. However we need to setup the desired
|
55
68
|
# parameters first before we can get the exportable data. This really shouldn't
|
56
69
|
# be necessary.
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
70
|
+
begin
|
71
|
+
hourly_data = @data_page.form_with(:action => "/LoadAnalysis/LoadAnalysis.aspx") do |form|
|
72
|
+
form['__EVENTTARGET'] = "objChartSelect$butSubmit"
|
73
|
+
form['objTimePeriods$objExport$hidChart'] = "Hourly Usage"
|
74
|
+
form['objTimePeriods$objExport$hidChartID'] = 8
|
75
|
+
form['objChartSelect$ddChart'] = 8 # Hourly usage
|
76
|
+
|
77
|
+
form['objTimePeriods$objExport$hidTimePeriod'] = "Week"
|
78
|
+
form['objTimePeriods$objExport$hidTimePeriodID'] = 3
|
79
|
+
form['objTimePeriods$rlPeriod'] = 3
|
80
|
+
|
81
|
+
form['objChartSelect$ccSelectedDate1'] = date.strftime("%m/%d/%Y")
|
82
|
+
end.submit
|
69
83
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
84
|
+
# Now the beautiful data...
|
85
|
+
hourly_csv = hourly_data.form_with(:action => "/LoadAnalysis/LoadAnalysis.aspx") do |form|
|
86
|
+
form['__EVENTTARGET'] = "objTimePeriods$objExport$butExport"
|
87
|
+
end.submit
|
74
88
|
|
75
|
-
|
89
|
+
hourly_csv.body
|
90
|
+
rescue Timeout::Error => e
|
91
|
+
@last_exception = e
|
92
|
+
return ""
|
93
|
+
end
|
76
94
|
end
|
77
95
|
end
|
78
96
|
end
|
data/lib/smartermeter.rb
CHANGED
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 = '2011-01-
|
16
|
+
s.version = '0.2.0'
|
17
|
+
s.date = '2011-01-25'
|
18
18
|
s.rubyforge_project = 'smartermeter'
|
19
19
|
|
20
20
|
## Make sure your summary is short. The description may be as long
|
@@ -61,6 +61,7 @@ Gem::Specification.new do |s|
|
|
61
61
|
## THE MANIFEST COMMENTS, they are used as delimiters by the task.
|
62
62
|
# = MANIFEST =
|
63
63
|
s.files = %w[
|
64
|
+
CHANGELOG.md
|
64
65
|
Gemfile
|
65
66
|
README.md
|
66
67
|
Rakefile
|
@@ -68,6 +69,8 @@ Gem::Specification.new do |s|
|
|
68
69
|
build_configuration.rb
|
69
70
|
lib/smartermeter.rb
|
70
71
|
lib/smartermeter/daemon.rb
|
72
|
+
lib/smartermeter/interfaces/cli.rb
|
73
|
+
lib/smartermeter/interfaces/swing.rb
|
71
74
|
lib/smartermeter/main.rb
|
72
75
|
lib/smartermeter/sample.rb
|
73
76
|
lib/smartermeter/service.rb
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 2
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Matt Colyer
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-25 00:00:00 -08:00
|
18
18
|
default_executable: smartermeter
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -82,6 +82,7 @@ extensions: []
|
|
82
82
|
extra_rdoc_files: []
|
83
83
|
|
84
84
|
files:
|
85
|
+
- CHANGELOG.md
|
85
86
|
- Gemfile
|
86
87
|
- README.md
|
87
88
|
- Rakefile
|
@@ -89,6 +90,8 @@ files:
|
|
89
90
|
- build_configuration.rb
|
90
91
|
- lib/smartermeter.rb
|
91
92
|
- lib/smartermeter/daemon.rb
|
93
|
+
- lib/smartermeter/interfaces/cli.rb
|
94
|
+
- lib/smartermeter/interfaces/swing.rb
|
92
95
|
- lib/smartermeter/main.rb
|
93
96
|
- lib/smartermeter/sample.rb
|
94
97
|
- lib/smartermeter/service.rb
|