itiscold 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 80b7a340af5d1b8a90b096f0f6484a566d3a75c7
4
+ data.tar.gz: 139a60181371e9e20fc7bcb1dbb5372982505e3f
5
+ SHA512:
6
+ metadata.gz: bdd0edbd38a6a30703c6350e2bad504481cf7f589045c3c37c0ebd9b7e31663003f9ccb543c9183292bf45d076e3bd67f3c6e8036b2b1e8b4fa2507d8d8b4750
7
+ data.tar.gz: b55e71a1b272bc680840c0cf540c85e7675067942db869d212df2a4bb3e818a575945d17e2daeaddd5eef6ecf020bc756aad198c10b0a4cd8ee883ed0b8ab88f
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2016-12-13
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2016 Aaron Patterson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ 'Software'), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,9 @@
1
+ CHANGELOG.md
2
+ LICENSE.md
3
+ Manifest.txt
4
+ README.md
5
+ Rakefile
6
+ bin/itiscold
7
+ lib/itiscold.rb
8
+ lib/itiscold/public/index.html
9
+ test/test_itiscold.rb
@@ -0,0 +1,48 @@
1
+ # itiscold
2
+
3
+ * https://github.com/tenderlove/itiscold
4
+
5
+ ## DESCRIPTION:
6
+
7
+ A thing that reads data from Elitech RC-5 temp sensor. Protocol documentation
8
+ can be found here:
9
+
10
+ https://github.com/civic/elitech-datareader/blob/master/rc-4-data.md
11
+
12
+ I've tested this on my RC-5, but not an RC-4.
13
+
14
+ ## SYNOPSIS:
15
+
16
+ Print device information
17
+
18
+ ```ruby
19
+ temp = Itiscold.open '/dev/tty.wchusbserial14140'
20
+ info = temp.device_info
21
+ puts Psych.dump info
22
+ ```
23
+
24
+ Set device information (Note that this will clear the memory)
25
+
26
+ ```ruby
27
+ # Get the info
28
+ temp = Itiscold.open '/dev/tty.wchusbserial14140'
29
+ info = temp.device_info
30
+
31
+ # Mutate it and upload to the device
32
+ info.stop_button = :prohibit
33
+ temp.device_params = info
34
+
35
+ # Print info again
36
+ info = temp.device_info
37
+ puts Psych.dump info
38
+ ```
39
+
40
+ Set device time
41
+ ```ruby
42
+ temp = Itiscold.open '/dev/tty.wchusbserial14140'
43
+ temp.set_device_time! # defaults to Time.now
44
+ ```
45
+
46
+ ## INSTALL:
47
+
48
+ * gem install itiscold
@@ -0,0 +1,19 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.plugins.delete :rubyforge
7
+ Hoe.plugin :minitest
8
+ Hoe.plugin :gemspec # `gem install hoe-gemspec`
9
+ Hoe.plugin :git # `gem install hoe-git`
10
+
11
+ Hoe.spec 'itiscold' do
12
+ developer('Aaron Patterson', 'tenderlove@ruby-lang.org')
13
+ self.readme_file = 'README.md'
14
+ self.history_file = 'CHANGELOG.md'
15
+ self.extra_rdoc_files = FileList['*.md']
16
+ self.extra_deps << ['ruby-termios', '~> 1']
17
+ end
18
+
19
+ # vim: syntax=ruby
@@ -0,0 +1,59 @@
1
+ require 'itiscold'
2
+ require 'optparse'
3
+ require 'time'
4
+
5
+ options = {
6
+ tty: ARGV.shift,
7
+ command: ARGV.shift,
8
+ table_name: 'samples'
9
+ }
10
+
11
+ def quit op
12
+ puts op
13
+ exit
14
+ end
15
+
16
+ op = OptionParser.new do |opts|
17
+ opts.banner = "Usage: itiscold path_to_tty [samples|info|server] [options]"
18
+
19
+ opts.on '--csv', 'output samples as CSV' do
20
+ quit(opts) if options[:format]
21
+ options[:format] = :csv
22
+ end
23
+
24
+ opts.on '--json', 'output samples as JSON' do
25
+ quit(opts) if options[:format]
26
+ options[:format] = :json
27
+ end
28
+ end
29
+
30
+ op.parse!
31
+
32
+ quit(op) unless options[:command] && options[:tty]
33
+
34
+ case options[:command]
35
+ when 'samples'
36
+ temp = Itiscold.open options[:tty]
37
+ case options[:format]
38
+ when :csv
39
+ require 'csv'
40
+ data = temp.samples
41
+ CSV { |csv| csv << ['SampleTime', 'Temp']; data.each { |s| csv << s } }
42
+ when :json
43
+ require 'json'
44
+ puts JSON.dump temp.device_info.to_h
45
+ temp.samples.each { |s|
46
+ puts JSON.dump({ time: s.first.iso8601, temp: s.last })
47
+ }
48
+ end
49
+ when 'info'
50
+ require 'psych'
51
+ puts Psych.dump temp.device_info
52
+ when 'server'
53
+ Itiscold::WebServer.start options[:tty]
54
+ else
55
+ puts op
56
+ exit
57
+ end
58
+
59
+ # vim: syntax=ruby
@@ -0,0 +1,416 @@
1
+ require 'termios'
2
+ require 'fcntl'
3
+ require 'webrick'
4
+ require 'json'
5
+
6
+ class Itiscold
7
+ VERSION = '1.0.0'
8
+
9
+ class TTY
10
+ include Termios
11
+
12
+ def self.open filename, speed, mode
13
+ if mode =~ /^(\d)(\w)(\d)$/
14
+ t.data_bits = $1.to_i
15
+ t.stop_bits = $3.to_i
16
+ t.parity = { 'N' => :none, 'E' => :even, 'O' => :odd }[$2]
17
+ t.speed = speed
18
+ t.read_timeout = 5
19
+ t.reading = true
20
+ t.update!
21
+ end
22
+ end
23
+
24
+ def self.data_bits t, val
25
+ t.cflag &= ~CSIZE # clear previous values
26
+ t.cflag |= const_get("CS#{val}") # Set the data bits
27
+ t
28
+ end
29
+
30
+ def self.stop_bits t, val
31
+ case val
32
+ when 1 then t.cflag &= ~CSTOPB
33
+ when 2 then t.cflag |= CSTOPB
34
+ else
35
+ raise
36
+ end
37
+ t
38
+ end
39
+
40
+ def self.parity t, val
41
+ case val
42
+ when :none
43
+ t.cflag &= ~PARENB
44
+ when :even
45
+ t.cflag |= PARENB # Enable parity
46
+ t.cflag &= ~PARODD # Make it not odd
47
+ when :odd
48
+ t.cflag |= PARENB # Enable parity
49
+ t.cflag |= PARODD # Make it odd
50
+ else
51
+ raise
52
+ end
53
+ t
54
+ end
55
+
56
+ def self.speed t, speed
57
+ t.ispeed = const_get("B#{speed}")
58
+ t.ospeed = const_get("B#{speed}")
59
+ t
60
+ end
61
+
62
+ def self.read_timeout t, val
63
+ t.cc[VTIME] = val
64
+ t.cc[VMIN] = 0
65
+ t
66
+ end
67
+
68
+ def self.reading t
69
+ t.cflag |= CLOCAL | CREAD
70
+ t
71
+ end
72
+ end
73
+
74
+ def self.open filename, speed = 115200
75
+ f = File.open filename, File::RDWR|Fcntl::O_NOCTTY|Fcntl::O_NDELAY
76
+ f.sync = true
77
+
78
+ # enable blocking reads, otherwise read timeout won't work
79
+ f.fcntl Fcntl::F_SETFL, f.fcntl(Fcntl::F_GETFL, 0) & ~Fcntl::O_NONBLOCK
80
+
81
+ t = Termios.tcgetattr f
82
+ t = TTY.data_bits t, 8
83
+ t = TTY.stop_bits t, 1
84
+ t = TTY.parity t, :none
85
+ t = TTY.speed t, speed
86
+ t = TTY.read_timeout t, 5
87
+ t = TTY.reading t
88
+
89
+ Termios.tcsetattr f, Termios::TCSANOW, t
90
+ Termios.tcflush f, Termios::TCIOFLUSH
91
+
92
+ temp = new f
93
+
94
+ # Try reading the version a few times before giving up
95
+ temp.flush
96
+ temp
97
+ end
98
+
99
+ def initialize tty
100
+ @tty = tty
101
+ end
102
+
103
+ DeviceInfo = Struct.new :station_number,
104
+ :model_number,
105
+ :sample_interval, # in seconds
106
+ :upper_limit,
107
+ :lower_limit,
108
+ :last_online,
109
+ :work_status,
110
+ :start_time,
111
+ :stop_button,
112
+ :sample_count,
113
+ :current_time,
114
+ :user_info,
115
+ :number,
116
+ :delay_time, # in seconds
117
+ :tone_set,
118
+ :alarm,
119
+ :temp_unit,
120
+ :temp_calibration,
121
+ :new_station_number
122
+
123
+ def device_info
124
+ val = retry_comm(1) do
125
+ @tty.write with_checksum([0xCC, 0x00, 0x06, 0x00]).pack('C5')
126
+ @tty.read 160
127
+ end
128
+ _, # C set number
129
+ station_no, # C station number
130
+ _, # C
131
+ model_no, # C model number
132
+ _, # C
133
+ rec_int_h, # C record interval hour
134
+ rec_int_m, # C record interval min
135
+ rec_int_s, # C record interval sec
136
+ upper_limit, # n upper limit
137
+ lower_limit, # s> lower limit
138
+ last_online, # A7 (datetime)
139
+ ws, # C work status
140
+ start_time, # A7 (datetime)
141
+ sb, # C stop button
142
+ _, # C
143
+ sample_count, # n number of records
144
+ current_time, # A7 (datetime)
145
+ user_info, # A100
146
+ number, # A10
147
+ delaytime, # C
148
+ tone_set, # C
149
+ alrm, # C
150
+ tmp_unit, # C
151
+ temp_calib, # C temp calibration
152
+ _, # A6 padding
153
+ check, # C padding
154
+ = val.unpack('C8ns>A7CA7CCnA7A100A10C5A6C')
155
+ check_checksum check, checksum(val.bytes.first(val.bytesize - 1))
156
+ DeviceInfo.new station_no, model_no,
157
+ (rec_int_h * 3600 + rec_int_m * 60 + rec_int_s),
158
+ upper_limit / 10.0, lower_limit / 10.0, unpack_datetime(last_online),
159
+ work_status(ws), unpack_datetime(start_time), allowed_decode(sb),
160
+ sample_count, unpack_datetime(current_time), user_info, number,
161
+ delay_time_decode(delaytime), allowed_decode(tone_set), alrm, temp_unit_decode(tmp_unit),
162
+ temp_calib / 10.0
163
+ end
164
+
165
+ def device_params= values
166
+ data = [
167
+ 0x33, # C
168
+ values.station_number, # C
169
+ 0x05, 0x00, # CC
170
+ ] + split_time(values.sample_interval) + # CCC
171
+ [
172
+ (values.upper_limit * 10).to_i, # n
173
+ (values.lower_limit * 10).to_i, # s>
174
+ values.new_station_number || values.station_number, # C
175
+ allowed_encode(values.stop_button), # C
176
+ delay_time_encode(values.delay_time), # C
177
+ allowed_encode(values.tone_set), # C
178
+ values.alarm, # C
179
+ temp_unit_encode(values.temp_unit), # C
180
+ (values.temp_calibration * 10).to_i, # C
181
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 # C6
182
+ ]
183
+ retry_comm(1) do
184
+ @tty.write with_checksum(data).pack('CCCCCCCns>CCCCCCCC6C')
185
+ @tty.read 3
186
+ end
187
+ end
188
+
189
+ def set_device_number station_number, dev_number
190
+ str = dev_number.bytes.first(10)
191
+ str.concat([0x00] * (10 - str.length))
192
+ data = [ 0x33, station_number, 0x0b, 0x00 ] + str
193
+
194
+ retry_comm(1) do
195
+ @tty.write with_checksum(data).pack("C#{data.length + 1}")
196
+ @tty.read 3
197
+ end
198
+ end
199
+
200
+ def set_user_info info, station_number = device_info.station_number
201
+ str = info.bytes.first(100)
202
+ str.concat([0x00] * (100 - str.length))
203
+ data = [ 0x33, station_number, 0x09, 0x00 ] + str
204
+
205
+ retry_comm(1) do
206
+ @tty.write with_checksum(data).pack("C#{data.length + 1}")
207
+ @tty.read 3
208
+ end
209
+ end
210
+
211
+ def set_device_time! time = Time.now, station_number = device_info.station_number
212
+ data = [0x33, station_number, 0x07, 0x00] + encode_datetime(time)
213
+
214
+ retry_comm(1) do
215
+ @tty.write with_checksum(data).pack("C4nC6")
216
+ @tty.read 3
217
+ end
218
+ end
219
+
220
+ DataHeader = Struct.new :sample_count, :start_time
221
+
222
+ def data_header station
223
+ buf = retry_comm(1) do
224
+ @tty.write with_checksum([0x33, station, 0x1, 0x0]).pack('C5')
225
+ @tty.read 11
226
+ end
227
+ _, sample_count, start_time, check = buf.unpack 'CnA7C'
228
+
229
+ check_checksum check, checksum(buf.bytes.first(buf.bytesize - 1))
230
+
231
+ DataHeader.new sample_count, unpack_datetime(start_time)
232
+ end
233
+
234
+ def data_body station, page
235
+ buf = retry_comm(1) do
236
+ @tty.write with_checksum([0x33, station, 0x2, page]).pack('C5')
237
+ @tty.read
238
+ end
239
+ temps = buf.unpack("Cn#{(buf.bytesize - 2) / 2}C")
240
+ temps.shift # header
241
+ temps.pop # checksum
242
+ temps.map { |t| t / 10.0 }
243
+ end
244
+
245
+ def samples
246
+ info = device_info
247
+ header = data_header info.station_number
248
+ records = header.sample_count
249
+ page = 0
250
+ list = []
251
+ while records > 0
252
+ samples = data_body info.station_number, page
253
+ records -= samples.length
254
+ list += samples
255
+ page += 1
256
+ end
257
+ st = header.start_time
258
+ list.map.with_index { |v, i| [st + (i * info.sample_interval), v] }
259
+ end
260
+
261
+ def flush
262
+ Termios.tcflush @tty, Termios::TCIOFLUSH
263
+ end
264
+
265
+ private
266
+
267
+ def split_time time_s
268
+ h = time_s / 3600
269
+ time_s -= (h * 3600)
270
+ m = time_s / 60
271
+ time_s -= (m * 60)
272
+ [h, m, time_s]
273
+ end
274
+
275
+ def retry_comm times
276
+ x = nil
277
+ loop do
278
+ x = yield
279
+ break if x || times == 0
280
+ flush
281
+ times -= 1
282
+ end
283
+ x
284
+ end
285
+
286
+ def temp_unit_decode u
287
+ case u
288
+ when 0x13 then 'F'
289
+ when 0x31 then 'C'
290
+ else
291
+ raise u.to_s
292
+ end
293
+ end
294
+
295
+ def temp_unit_encode u
296
+ case u
297
+ when 'F' then 0x13
298
+ when 'C' then 0x31
299
+ else
300
+ raise u.to_s
301
+ end
302
+ end
303
+
304
+ def delay_time_decode dt
305
+ case dt
306
+ when 0x00 then 0
307
+ when 0x01 then 30 * 60 # 30min in sec
308
+ when 0x10 then 60 * 60 # 60min in sec
309
+ when 0x11 then 90 * 60 # 90min in sec
310
+ when 0x20 then 120 * 60 # 120min in sec
311
+ when 0x21 then 150 * 60 # 150min in sec
312
+ end
313
+ end
314
+
315
+ def delay_time_encode dt
316
+ case dt
317
+ when 0 then 0x00
318
+ when 30 * 60 then 0x01 # 30min in sec
319
+ when 60 * 60 then 0x10 # 60min in sec
320
+ when 90 * 60 then 0x11 # 90min in sec
321
+ when 120 * 60 then 0x20 # 120min in sec
322
+ when 150 * 60 then 0x21 # 150min in sec
323
+ end
324
+ end
325
+
326
+ def allowed_decode sb
327
+ case sb
328
+ when 0x13 then :permit
329
+ when 0x31 then :prohibit
330
+ else
331
+ raise sb.to_s
332
+ end
333
+ end
334
+
335
+ def allowed_encode sb
336
+ case sb
337
+ when :permit then 0x13
338
+ when :prohibit then 0x31
339
+ else
340
+ raise sb.to_s
341
+ end
342
+ end
343
+
344
+ def work_status ws
345
+ case ws
346
+ when 0 then :not_started
347
+ when 1 then :start
348
+ when 2 then :stop
349
+ when 3 then :unknown
350
+ end
351
+ end
352
+
353
+ def check_checksum expected, actual
354
+ raise "invalid checksum #{expected} == #{actual}" unless expected == actual
355
+ end
356
+
357
+ def unpack_datetime bytes
358
+ Time.new(*bytes.unpack('nC5'))
359
+ end
360
+
361
+ def encode_datetime time
362
+ [time.year, time.month, time.day, time.hour, time.min, time.sec]
363
+ end
364
+
365
+ def with_checksum list
366
+ list + [checksum(list)]
367
+ end
368
+
369
+ def checksum list
370
+ list.inject(:+) % 256
371
+ end
372
+
373
+ module WebServer
374
+ class TTYServlet < WEBrick::HTTPServlet::AbstractServlet
375
+ def initialize server, tty, mutex
376
+ super server
377
+ @tty = tty
378
+ @mutex = mutex
379
+ end
380
+ end
381
+
382
+ class InfoServlet < TTYServlet
383
+ def do_GET request, response
384
+ response.status = 200
385
+ response['Content-Type'] = 'text/json'
386
+ @mutex.synchronize do
387
+ json = JSON.dump(@tty.device_info.to_h)
388
+ response.body = json
389
+ end
390
+ end
391
+ end
392
+
393
+ class SampleServlet < TTYServlet
394
+ def do_GET request, response
395
+ response.status = 200
396
+ response['Content-Type'] = 'text/json'
397
+ @mutex.synchronize do
398
+ response.body = JSON.dump @tty.samples.map { |s|
399
+ { time: s.first.iso8601, temp: s.last }
400
+ }
401
+ end
402
+ end
403
+ end
404
+
405
+ def self.start tty
406
+ root = File.expand_path(File.join File.dirname(__FILE__), 'itiscold', 'public')
407
+ mutex = Mutex.new
408
+ temp = Itiscold.open tty
409
+ server = WEBrick::HTTPServer.new(:Port => 8000, :DocumentRoot => root)
410
+ server.mount "/samples", SampleServlet, temp, mutex
411
+ server.mount "/info", InfoServlet, temp, mutex
412
+ trap "INT" do server.shutdown end
413
+ server.start
414
+ end
415
+ end
416
+ end
@@ -0,0 +1,71 @@
1
+ <html>
2
+ <head>
3
+ <link href='https://cdnjs.cloudflare.com/ajax/libs/metrics-graphics/2.11.0/metricsgraphics.css' rel='stylesheet' type='text/css'>
4
+ <link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet' type='text/css'>
5
+
6
+ <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js'></script>
7
+ <script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>
8
+ <script src="https://d3js.org/d3.v4.min.js"></script>
9
+ <script src='https://cdnjs.cloudflare.com/ajax/libs/metrics-graphics/2.11.0/metricsgraphics.js'></script>
10
+ <script>
11
+ var info;
12
+ var samples;
13
+ function capitalizeFirstLetter(string) {
14
+ return string.charAt(0).toUpperCase() + string.slice(1);
15
+ }
16
+ $.when($.getJSON("/info", function(data) {
17
+ info = data;
18
+ }), $.getJSON("/samples", function(data) {
19
+ console.log("hello");
20
+ samples = data.map(function(sample) {
21
+ return { time: new Date(sample.time), temp: sample.temp };
22
+ });
23
+ })
24
+ ).then(function() {
25
+ console.log(samples);
26
+ console.log(info);
27
+ $(function() {
28
+ var table = $("#device-info > tbody");
29
+ for (var key in info) {
30
+ if (info.hasOwnProperty(key)) {
31
+ var value = info[key];
32
+ if (value) {
33
+ var words = key.split("_");
34
+ key = words.map(capitalizeFirstLetter).join(" ");
35
+ table.append("<tr><td>" + key + "</td><td>" + value + "</td></tr>");
36
+ }
37
+ }
38
+ }
39
+ MG.data_graphic({
40
+ title: "Temperature",
41
+ description: "This graph shose a time series of temperatures",
42
+ data: samples,
43
+ width: 600,
44
+ height: 250,
45
+ target: '#temp-graph',
46
+ x_accessor: 'time',
47
+ y_accessor: 'temp',
48
+ })
49
+ });
50
+ });
51
+ </script>
52
+ </head>
53
+ <body>
54
+ <div class="container">
55
+ <h1 class="text-center">Temperature Graph</h1>
56
+
57
+ <div class="row">
58
+ <div class="col-xs-4">
59
+ <h2>Device Info</h2>
60
+ <table id="device-info" class="table table-striped table-condensed table-bordered">
61
+ <tbody>
62
+ </tbody>
63
+ </table>
64
+ </div>
65
+ <div class="col-xs-8 text-center">
66
+ <div id='temp-graph'></div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </body>
71
+ </html>
@@ -0,0 +1,8 @@
1
+ require 'minitest/autorun'
2
+ require 'itiscold'
3
+
4
+ class TestItiscold < Minitest::Test
5
+ def test_split_time
6
+ assert true # lol this is a project I did for fun
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: itiscold
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Patterson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-01-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby-termios
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rdoc
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: hoe
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.16'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.16'
55
+ description: |-
56
+ A thing that reads data from Elitech RC-5 temp sensor. Protocol documentation
57
+ can be found here:
58
+
59
+ https://github.com/civic/elitech-datareader/blob/master/rc-4-data.md
60
+
61
+ I've tested this on my RC-5, but not an RC-4.
62
+ email:
63
+ - tenderlove@ruby-lang.org
64
+ executables:
65
+ - itiscold
66
+ extensions: []
67
+ extra_rdoc_files:
68
+ - CHANGELOG.md
69
+ - LICENSE.md
70
+ - Manifest.txt
71
+ - README.md
72
+ files:
73
+ - CHANGELOG.md
74
+ - LICENSE.md
75
+ - Manifest.txt
76
+ - README.md
77
+ - Rakefile
78
+ - bin/itiscold
79
+ - lib/itiscold.rb
80
+ - lib/itiscold/public/index.html
81
+ - test/test_itiscold.rb
82
+ homepage: https://github.com/tenderlove/itiscold
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options:
88
+ - "--main"
89
+ - README.md
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.6.8
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: A thing that reads data from Elitech RC-5 temp sensor
108
+ test_files: []