the_energy_detective 0.1.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.
- checksums.yaml +7 -0
- data/Rakefile +24 -0
- data/lib/ted/ecc.rb +310 -0
- data/lib/ted/mtu.rb +33 -0
- data/lib/ted/spyder/ct.rb +22 -0
- data/lib/ted/spyder/group.rb +33 -0
- data/lib/ted/spyder.rb +22 -0
- data/lib/ted/version.rb +3 -0
- data/lib/the_energy_detective.rb +1 -0
- metadata +80 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: d4edee41131dbc88b75d2553880f43b25085409e
|
|
4
|
+
data.tar.gz: a898bcaa30fde51f9617720442f749b2c4f55d5f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 72188fb54a41a3fd04a2f55f7f7ef867e780b0dc12dbfed3925fadfa674091fe8493121ed0e19ed6ce5779bc7085ffbd425bc4f9b2d666ad878c8aa6cbad6d74
|
|
7
|
+
data.tar.gz: 24ba5cc8ca71d5b2534c4e929038934ba1d45cc012c1109e1abd17d3148e5e2dca86b918b1a01767f0690e2ca9e3a1fcb1562103fd5586b6cd8f87750e75cdc0
|
data/Rakefile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
begin
|
|
8
|
+
require 'rdoc/task'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
require 'rdoc/rdoc'
|
|
11
|
+
require 'rake/rdoctask'
|
|
12
|
+
RDoc::Task = Rake::RDocTask
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
17
|
+
rdoc.title = 'The Energy Detective'
|
|
18
|
+
rdoc.options << '--line-numbers'
|
|
19
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Bundler::GemHelper.install_tasks
|
|
23
|
+
|
|
24
|
+
task :default => :rdoc
|
data/lib/ted/ecc.rb
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
require 'csv'
|
|
3
|
+
require 'net/http'
|
|
4
|
+
|
|
5
|
+
require 'nokogiri'
|
|
6
|
+
|
|
7
|
+
require 'ted/mtu'
|
|
8
|
+
require 'ted/spyder'
|
|
9
|
+
|
|
10
|
+
module TED
|
|
11
|
+
class ECC
|
|
12
|
+
def initialize(host)
|
|
13
|
+
if host.is_a?(String)
|
|
14
|
+
@host = URI.parse(host)
|
|
15
|
+
else
|
|
16
|
+
@host = host.dup
|
|
17
|
+
end
|
|
18
|
+
@user = @host.user
|
|
19
|
+
@password = @host.password
|
|
20
|
+
@host.user = nil
|
|
21
|
+
@http = Net::HTTP.new(@host.host, @host.port)
|
|
22
|
+
@http.use_ssl = (@host.scheme == 'https')
|
|
23
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Removes the cached system layout, allowing access to newly defined
|
|
27
|
+
# MTUs[rdoc-ref:#mtus] and Spyders[rdoc-ref:#spyders]
|
|
28
|
+
def refresh
|
|
29
|
+
@mtus = nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def current(source = :net)
|
|
33
|
+
params = {}
|
|
34
|
+
params[:T] = 0 # Power
|
|
35
|
+
|
|
36
|
+
params[:D] = case source
|
|
37
|
+
when :net
|
|
38
|
+
0
|
|
39
|
+
when :load
|
|
40
|
+
1
|
|
41
|
+
when :generation
|
|
42
|
+
2
|
|
43
|
+
when MTU
|
|
44
|
+
params[:M] = source.index
|
|
45
|
+
255
|
|
46
|
+
when :spyders
|
|
47
|
+
return spyders_current
|
|
48
|
+
else
|
|
49
|
+
raise ArgumentError, 'source must be :net, :load, :generation, or :spyders'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
dashboard_data(Nokogiri::XML(query("api/DashData.xml", params)))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# A hash of the MTUs[rdoc-ref:MTU] connected to this ECC.
|
|
56
|
+
# It is indexed by both description and numerical index
|
|
57
|
+
def mtus
|
|
58
|
+
build_system_layout
|
|
59
|
+
@mtus
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# A hash of the Spyders[rdoc-ref:Spyder::Group] connected to this ECC.
|
|
63
|
+
# It is index by both description and numerical index
|
|
64
|
+
def spyders
|
|
65
|
+
build_system_layout
|
|
66
|
+
@spyders
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Returns history for all connected MTUs[rdoc-ref:MTU] and Spyders[rdoc-ref:Spyder::Group]
|
|
70
|
+
# The return value is a hash indexed by the MTU or Spyder::Group, and a hash of timestamp,
|
|
71
|
+
# energy or power, and cost
|
|
72
|
+
def history(interval: :seconds)
|
|
73
|
+
raise ArgumentError, "invalid interval" unless INTERVALS.include?(interval)
|
|
74
|
+
|
|
75
|
+
params = {}
|
|
76
|
+
|
|
77
|
+
params[:T] = INTERVALS.index(interval) + 1
|
|
78
|
+
|
|
79
|
+
response = query("history/exportAll.csv", params)
|
|
80
|
+
result = {}
|
|
81
|
+
response.strip!
|
|
82
|
+
CSV.parse(response) do |(channel_name, timestamp, kwh, cost)|
|
|
83
|
+
channel = mtus[channel_name] || spyders[channel_name]
|
|
84
|
+
result[channel] ||= []
|
|
85
|
+
timestamp = case interval
|
|
86
|
+
when :seconds, :minutes, :hours
|
|
87
|
+
DateTime.strptime(timestamp, "%m/%d/%Y %H:%M:%S").to_time
|
|
88
|
+
when :days, :months
|
|
89
|
+
month, day, year = timestamp.split('/').map(&:to_i)
|
|
90
|
+
Date.new(year, month, day)
|
|
91
|
+
end
|
|
92
|
+
energy_key = [:seconds, :minutes].include?(interval) ? :power : :energy
|
|
93
|
+
result[channel] << {
|
|
94
|
+
timestamp: timestamp,
|
|
95
|
+
energy_key => (kwh.to_f * 1000).to_i,
|
|
96
|
+
cost: cost.to_f
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
result
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# :nodoc:
|
|
103
|
+
def inspect
|
|
104
|
+
"#<TED::ECC:#{@host}>"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
INTERVALS = [:seconds, :minutes, :hours, :days, :months].freeze
|
|
110
|
+
private_constant :INTERVALS
|
|
111
|
+
|
|
112
|
+
def history_by_source(source, interval, offset_range, date_range)
|
|
113
|
+
raise ArgumentError, "invalid interval" unless INTERVALS.include?(interval)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
case source
|
|
117
|
+
when MTU
|
|
118
|
+
source_type = :mtu
|
|
119
|
+
when Spyder::Group
|
|
120
|
+
source_type = :spyder
|
|
121
|
+
end
|
|
122
|
+
raise ArgumentError, "interval cannot be seconds for a Spyder" if source_type == :spyder && interval == :seconds
|
|
123
|
+
|
|
124
|
+
params = {}
|
|
125
|
+
|
|
126
|
+
params[:D] = (source_type == :mtu ? 0 : 1)
|
|
127
|
+
params[:M] = source.index
|
|
128
|
+
params[:T] = case interval
|
|
129
|
+
when :seconds
|
|
130
|
+
1
|
|
131
|
+
when :minutes
|
|
132
|
+
2
|
|
133
|
+
when :hours
|
|
134
|
+
3
|
|
135
|
+
when :days
|
|
136
|
+
4
|
|
137
|
+
when :months
|
|
138
|
+
5
|
|
139
|
+
end
|
|
140
|
+
params[:T] -= 1 if source_type == :spyder
|
|
141
|
+
|
|
142
|
+
if offset_range
|
|
143
|
+
if offset_range.end != Float::INFINITY
|
|
144
|
+
params[:C] = offset_range.end + 1
|
|
145
|
+
params[:C] -= 1 if offset_range.exclude_end?
|
|
146
|
+
end
|
|
147
|
+
if offset_range.begin != -Float::INFINITY && offset_range.begin != 0
|
|
148
|
+
raise ArgumentError, "cannot specify an offset for anything besides seconds" unless interval == :seconds
|
|
149
|
+
params[:I] = offset_range.begin
|
|
150
|
+
params[:C] -= params[:I] if params[:C]
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
if date_range
|
|
155
|
+
params[:S] = date_range.begin.to_i unless date_range.begin == -Float::INFINITY
|
|
156
|
+
if date_range.end != Float::INFINITY
|
|
157
|
+
end_timestamp = date_range.end.to_i
|
|
158
|
+
end_timestamp -= 1 if date_range.exclude_end?
|
|
159
|
+
params[:E] = end_timestamp
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
response = query("history/export.raw", params)
|
|
164
|
+
response.split("\n").map do |line|
|
|
165
|
+
data = Base64.decode64(line)
|
|
166
|
+
bytes = data.unpack('C*')
|
|
167
|
+
raise "Unknown header" unless bytes[0] == 0xa4
|
|
168
|
+
checksum = bytes[0..-2].inject(0, :+) % 256
|
|
169
|
+
raise "Wrong checksum" unless bytes[-1] == checksum
|
|
170
|
+
case source_type
|
|
171
|
+
when :mtu
|
|
172
|
+
case interval
|
|
173
|
+
when :seconds
|
|
174
|
+
_, timestamp, energy, cost, voltage, _ = data.unpack('CL<l<2S<C')
|
|
175
|
+
when :minutes
|
|
176
|
+
_, timestamp, energy, cost, voltage, _pf, _ = data.unpack('CL<l<2S<2C')
|
|
177
|
+
when :hours, :days
|
|
178
|
+
_, timestamp, energy, cost, _ = data.unpack('CL<l<2C')
|
|
179
|
+
when :months
|
|
180
|
+
_, timestamp, energy, cost, _min_charge, _fixed_charge, _demand_charge, _demand_charge_peak_power_average, _demand_charge_time, _demand_charge_tou, _ = data.unpack('CL<l<2L<2l<2L<C2')
|
|
181
|
+
end
|
|
182
|
+
when :spyder
|
|
183
|
+
_, timestamp, energy, cost, _ = data.unpack('CL<l<2C')
|
|
184
|
+
end
|
|
185
|
+
timestamp = Time.at(timestamp)
|
|
186
|
+
timestamp = timestamp.to_date if interval == :days || interval == :months
|
|
187
|
+
cost = cost.to_f / 100
|
|
188
|
+
voltage = voltage.to_f / 10 if voltage
|
|
189
|
+
energy_key = [:seconds, :minutes].include?(interval) ? :power : :energy
|
|
190
|
+
result = { timestamp: timestamp, energy_key => energy, cost: cost }
|
|
191
|
+
result[:voltage] = voltage if voltage
|
|
192
|
+
result
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def spyders_current
|
|
197
|
+
xml = Nokogiri::XML(query('api/SpyderData.xml'))
|
|
198
|
+
|
|
199
|
+
result = {}
|
|
200
|
+
net_xml = xml.css("DashData")
|
|
201
|
+
result[:net] = dashboard_data(net_xml)
|
|
202
|
+
|
|
203
|
+
xml.css("Group").each_with_index do |group_xml, idx|
|
|
204
|
+
next unless (group = spyders[idx + 1])
|
|
205
|
+
result[group] = dashboard_data(group_xml)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
result
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def build_system_layout
|
|
212
|
+
return if @mtus
|
|
213
|
+
|
|
214
|
+
xml = Nokogiri::XML(query("api/SystemSettings.xml"))
|
|
215
|
+
|
|
216
|
+
mtus = []
|
|
217
|
+
xml.css("MTU").each do |mtu_xml|
|
|
218
|
+
description = mtu_xml.at_css("MTUDescription").text
|
|
219
|
+
mtus << MTU.new(self, mtus.length, description)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
group_index = 1
|
|
223
|
+
@spyders = {}
|
|
224
|
+
xml.css("Spyder").each do |spyder_xml|
|
|
225
|
+
enabled = spyder_xml.at_css("Enabled").text == '1'
|
|
226
|
+
if !enabled
|
|
227
|
+
group_index += 8
|
|
228
|
+
next
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
cts = spyder_xml.css("CT").map do |ct_xml|
|
|
232
|
+
twenty_amp = ct_xml.at_css("Type").text == '1'
|
|
233
|
+
multiplier = ct_xml.at_css("Mult").text.to_i
|
|
234
|
+
multiplier = -(multiplier - 4) if multiplier > 4
|
|
235
|
+
description = ct_xml.at_css("Description").text
|
|
236
|
+
Spyder::CT.new(twenty_amp, multiplier, description)
|
|
237
|
+
end
|
|
238
|
+
groups = []
|
|
239
|
+
spyder_xml.css("Group").each do |group_xml|
|
|
240
|
+
description = group_xml.at_css("Description").text
|
|
241
|
+
ct_mask = group_xml.at_css("UseCT").text.to_i
|
|
242
|
+
group_cts = []
|
|
243
|
+
ct_index = 0
|
|
244
|
+
while ct_mask != 0
|
|
245
|
+
if (ct_mask & 1) == 1
|
|
246
|
+
group_cts << cts[ct_index]
|
|
247
|
+
end
|
|
248
|
+
ct_mask /= 2
|
|
249
|
+
ct_index += 1
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
unless group_cts.empty?
|
|
253
|
+
group = Spyder::Group.new(group_index, description, group_cts)
|
|
254
|
+
groups << group
|
|
255
|
+
@spyders[group_index] = @spyders[description] = group
|
|
256
|
+
end
|
|
257
|
+
group_index += 1
|
|
258
|
+
end
|
|
259
|
+
mtu_index = spyder_xml.at_css("MTUParent").text.to_i
|
|
260
|
+
mtu = mtus[mtu_index]
|
|
261
|
+
spyder = Spyder.new(mtu, cts, groups)
|
|
262
|
+
mtu.spyders << spyder
|
|
263
|
+
cts.each { |ct| ct.instance_variable_set(:@spyder, spyder) }
|
|
264
|
+
groups.each { |group| group.instance_variable_set(:@spyder, spyder) }
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
@mtus = {}
|
|
268
|
+
mtus.each { |mtu| @mtus[mtu.index] = @mtus[mtu.description] = mtu; mtu.spyders.freeze }
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def query(path, params = nil)
|
|
272
|
+
uri = @host.merge(path)
|
|
273
|
+
|
|
274
|
+
uri.query = self.class.hash_to_query(params) if params
|
|
275
|
+
get = Net::HTTP::Get.new(uri)
|
|
276
|
+
get.basic_auth @user, @password if @user
|
|
277
|
+
response = @http.request(get)
|
|
278
|
+
response.body
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def dashboard_data(xml)
|
|
282
|
+
now = xml.at_css('Now').text.to_i
|
|
283
|
+
today = xml.at_css('TDY').text.to_i
|
|
284
|
+
mtd = xml.at_css('MTD').text.to_i
|
|
285
|
+
{ now: now, today: today, mtd: mtd }
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def self.hash_to_query(hash)
|
|
289
|
+
hash.map{|k,v| "#{k}=#{v}" }.join("&")
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def self.interpret_offsets(offset, limit)
|
|
293
|
+
return nil unless offset || limit
|
|
294
|
+
if offset.is_a?(Range)
|
|
295
|
+
raise ArgumentError, 'limit cannot be provided if offset is a range' if limit
|
|
296
|
+
return offset
|
|
297
|
+
end
|
|
298
|
+
return offset...(offset + limit) if offset && limit
|
|
299
|
+
return offset...Float::INFINITY if offset
|
|
300
|
+
return 0...limit # if limit
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def self.interpret_dates(date_range, start_time, end_time)
|
|
304
|
+
raise ArgumentError, 'start_time cannot be specified with date_range' if date_range && start_time
|
|
305
|
+
raise ArgumentError, 'end_time cannot be specified with date_range' if date_range && start_time
|
|
306
|
+
return date_range if date_range
|
|
307
|
+
return (start_time && start_time.to_i || -Float::INFINITY)...(end_time && end_time.to_i || Float::INFINITY)
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
data/lib/ted/mtu.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module TED
|
|
2
|
+
class MTU
|
|
3
|
+
# The ECC this MTU belongs to
|
|
4
|
+
attr_reader :ecc
|
|
5
|
+
|
|
6
|
+
# The 0-based index of this MTU in the ECC
|
|
7
|
+
attr_reader :index
|
|
8
|
+
|
|
9
|
+
attr_reader :description
|
|
10
|
+
|
|
11
|
+
# An Array of Spyders[rdoc-ref:Spyder] connected to this MTU
|
|
12
|
+
attr_reader :spyders
|
|
13
|
+
|
|
14
|
+
def initialize(ecc, index, description)
|
|
15
|
+
@ecc, @index, @description, @spyders = ecc, index, description, []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def current
|
|
19
|
+
ecc.current(self)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def history(interval: :seconds, offset: nil, limit: nil, date_range: nil, start_time: nil, end_time: nil)
|
|
23
|
+
offset = ECC.send(:interpret_offsets, offset, limit)
|
|
24
|
+
date_range = ECC.send(:interpret_dates, date_range, start_time, end_time)
|
|
25
|
+
ecc.send(:history_by_source, self, interval, offset, date_range)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# :nodoc:
|
|
29
|
+
def inspect
|
|
30
|
+
"#<TED::MTU:#{index} #{description}>"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module TED
|
|
2
|
+
class Spyder
|
|
3
|
+
class CT
|
|
4
|
+
# The Spyder this CT belongs to
|
|
5
|
+
attr_reader :spyder
|
|
6
|
+
attr_reader :multiplier, :description
|
|
7
|
+
|
|
8
|
+
def initialize(twenty_amp, multiplier, description)
|
|
9
|
+
@twenty_amp, @multiplier, @description = twenty_amp, multiplier, description
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def twenty_amp?
|
|
13
|
+
@twenty_amp
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# :nodoc:
|
|
17
|
+
def inspect
|
|
18
|
+
"#<TED::Spyder::CT #{description} multiplier=#{multiplier}>"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module TED
|
|
2
|
+
class Spyder
|
|
3
|
+
class Group
|
|
4
|
+
# The Spyder this Group belongs to
|
|
5
|
+
attr_reader :spyder
|
|
6
|
+
# The one-based index of this Group in the entire ECC
|
|
7
|
+
attr_reader :index
|
|
8
|
+
attr_reader :description
|
|
9
|
+
# An Array of CTs[rdoc-ref:CT] that compose this Group
|
|
10
|
+
attr_reader :cts
|
|
11
|
+
|
|
12
|
+
def initialize(index, description, cts)
|
|
13
|
+
@index, @description, @cts = index, description, cts
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Current data for this Group
|
|
17
|
+
def current
|
|
18
|
+
spyder.mtu.ecc.current(:spyders)[self]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def history(interval: :minutes, offset: nil, limit: nil, date_range: nil, start_time: nil, end_time: nil)
|
|
22
|
+
offset = ECC.send(:interpret_offsets, offset, limit)
|
|
23
|
+
date_range = ECC.send(:interpret_dates, date_range, start_time, end_time)
|
|
24
|
+
spyder.mtu.ecc.send(:history_by_source, self, interval, offset, date_range)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# :nodoc:
|
|
28
|
+
def inspect
|
|
29
|
+
"#<TED::Spyder::Group:#{index} #{description} cts=#{cts.inspect}>"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/ted/spyder.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'ted/spyder/ct'
|
|
2
|
+
require 'ted/spyder/group'
|
|
3
|
+
|
|
4
|
+
module TED
|
|
5
|
+
class Spyder
|
|
6
|
+
# The MTU this Spyder belongs to
|
|
7
|
+
attr_reader :mtu
|
|
8
|
+
# An Array of CTs[rdoc-ref:CT] connected to this Spyder
|
|
9
|
+
attr_reader :cts
|
|
10
|
+
# An Array of Groups[rdoc-ref:Group] defined on this Spyder
|
|
11
|
+
attr_reader :groups
|
|
12
|
+
|
|
13
|
+
def initialize(mtu, cts, groups)
|
|
14
|
+
@mtu, @cts, @groups = mtu, cts, groups
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# :nodoc:
|
|
18
|
+
def inspect
|
|
19
|
+
"#<Ted::Spyder>"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/ted/version.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'ted/ecc'
|
metadata
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: the_energy_detective
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Cody Cutrer
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-06-15 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: nokogiri
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 1.6.6.2
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 1.6.6.2
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 10.4.2
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 10.4.2
|
|
41
|
+
description:
|
|
42
|
+
email:
|
|
43
|
+
- cody@cutrer.us
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- Rakefile
|
|
49
|
+
- lib/ted/ecc.rb
|
|
50
|
+
- lib/ted/mtu.rb
|
|
51
|
+
- lib/ted/spyder.rb
|
|
52
|
+
- lib/ted/spyder/ct.rb
|
|
53
|
+
- lib/ted/spyder/group.rb
|
|
54
|
+
- lib/ted/version.rb
|
|
55
|
+
- lib/the_energy_detective.rb
|
|
56
|
+
homepage: http://www.theenergydetective.com/
|
|
57
|
+
licenses:
|
|
58
|
+
- MIT
|
|
59
|
+
metadata: {}
|
|
60
|
+
post_install_message:
|
|
61
|
+
rdoc_options: []
|
|
62
|
+
require_paths:
|
|
63
|
+
- lib
|
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '0'
|
|
74
|
+
requirements: []
|
|
75
|
+
rubyforge_project:
|
|
76
|
+
rubygems_version: 2.4.5
|
|
77
|
+
signing_key:
|
|
78
|
+
specification_version: 4
|
|
79
|
+
summary: Client library for talking to a TED Home Pro
|
|
80
|
+
test_files: []
|