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