lightwaverf 0.6.6 → 0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/app/views/_graphs.ejs +21 -26
- data/bin/lightwaverf +19 -14
- data/bin/lightwaverf-config-json +3 -1
- data/lib/lightwaverf.rb +146 -86
- metadata +4 -3
data/app/views/_graphs.ejs
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
<script
|
2
|
-
<script
|
3
|
-
<script
|
1
|
+
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
|
2
|
+
<script src="//www.google.com/jsapi"></script>
|
3
|
+
<script>
|
4
4
|
var gauge, gauge_data, gauge_options;
|
5
5
|
google.load( 'visualization', '1.0', { packages: [ 'corechart', 'gauge', 'annotatedtimeline' ] } );
|
6
6
|
google.setOnLoadCallback( function ( ) {
|
@@ -9,39 +9,35 @@
|
|
9
9
|
energy_data.addColumn( 'number', 'Electricity used' );
|
10
10
|
energy_data.addColumn( 'string', 'title1' );
|
11
11
|
energy_data.addColumn( 'string', 'text1' );
|
12
|
-
if (!Array.prototype.map) {
|
13
|
-
Array.prototype.map = function(callback, thisArg) {
|
12
|
+
if ( ! Array.prototype.map ) { // polyfill
|
13
|
+
Array.prototype.map = function( callback, thisArg ) {
|
14
14
|
var T, A, k;
|
15
|
-
if (this == null)
|
16
|
-
throw new TypeError(" this is null or not defined");
|
17
|
-
}
|
15
|
+
if ( this == null ) throw new TypeError( 'this is null or not defined' );
|
18
16
|
var O = Object(this);
|
19
17
|
var len = O.length >>> 0;
|
20
|
-
if (typeof callback !==
|
21
|
-
|
22
|
-
|
23
|
-
if (thisArg) {
|
24
|
-
T = thisArg;
|
25
|
-
}
|
26
|
-
A = new Array(len);
|
18
|
+
if ( typeof callback !== 'function' ) throw new TypeError( callback + ' is not a function' );
|
19
|
+
if ( thisArg ) T = thisArg;
|
20
|
+
A = new Array( len );
|
27
21
|
k = 0;
|
28
|
-
while(k < len) {
|
22
|
+
while ( k < len ) {
|
29
23
|
var kValue, mappedValue;
|
30
|
-
if (k in O) {
|
24
|
+
if ( k in O ) {
|
31
25
|
kValue = O[ k ];
|
32
26
|
mappedValue = callback.call(T, kValue, k, O);
|
33
27
|
A[ k ] = mappedValue;
|
34
28
|
}
|
35
|
-
k++;
|
29
|
+
k ++;
|
36
30
|
}
|
37
31
|
return A;
|
38
32
|
};
|
39
33
|
}
|
40
|
-
var
|
41
|
-
var
|
42
|
-
|
43
|
-
|
44
|
-
|
34
|
+
var rawData = <%- summary %>;
|
35
|
+
// var startDate = rawData[0][0];
|
36
|
+
var i = 0;
|
37
|
+
energy_data.addRows( rawData.map( function ( e ) {
|
38
|
+
++ i;
|
39
|
+
// if ( e[0] !== startDate ) e[0] += startDate;
|
40
|
+
e[0] = new Date( e[0] * 1000 ); // as it is now a timestamp
|
45
41
|
e[1] = e[1] * 10;
|
46
42
|
e[2] = e[2] || '';
|
47
43
|
e[3] = e[3] || '';
|
@@ -49,9 +45,8 @@
|
|
49
45
|
} ));
|
50
46
|
var chart = new google.visualization.AnnotatedTimeLine( document.getElementById( 'energy_chart' ));
|
51
47
|
chart.draw( energy_data, { displayAnnotations: true, title: '24 hours electricity usage' } );
|
52
|
-
|
53
48
|
gauge = new google.visualization.Gauge( document.getElementById( 'gauge_div' ));
|
54
|
-
gauge_data = google.visualization.arrayToDataTable( [
|
49
|
+
gauge_data = google.visualization.arrayToDataTable( [[ 'Label', 'Value' ], [ 'Electric', rawData.pop[ 1 ]]] );
|
55
50
|
gauge_options = {
|
56
51
|
width: '200',
|
57
52
|
height: '200',
|
@@ -64,7 +59,7 @@
|
|
64
59
|
gauge.draw( gauge_data, gauge_options );
|
65
60
|
} );
|
66
61
|
$( function ( ) {
|
67
|
-
var key = '
|
62
|
+
var key = ''; // @todo prompt for an pi key and save it in local storage like https://github.com/pauly/robot-butler does
|
68
63
|
$('dt a').click( function ( ) {
|
69
64
|
$(this).parent( ).next('dd').slideDown( );
|
70
65
|
return false;
|
data/bin/lightwaverf
CHANGED
@@ -1,32 +1,37 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
require 'lightwaverf'
|
3
|
+
obj = LightWaveRF.new
|
3
4
|
case ARGV[0]
|
4
5
|
when 'help'
|
5
|
-
puts
|
6
|
+
puts obj.help
|
7
|
+
when 'initlink'
|
8
|
+
puts obj.firmware ARGV[1]
|
9
|
+
when 'firmware'
|
10
|
+
puts obj.firmware ARGV[1]
|
6
11
|
when 'timezone'
|
7
|
-
puts
|
12
|
+
puts obj.timezone ARGV[1]
|
8
13
|
when 'configure'
|
9
|
-
puts
|
14
|
+
puts obj.configure ARGV[1]
|
10
15
|
when 'sequence'
|
11
|
-
puts
|
16
|
+
puts obj.sequence ARGV[1], ARGV[2]
|
12
17
|
when 'mood'
|
13
|
-
puts
|
18
|
+
puts obj.mood ARGV[1], ARGV[2], ARGV[3]
|
14
19
|
when 'learnmood'
|
15
|
-
puts
|
20
|
+
puts obj.learnmood ARGV[1], ARGV[2], ARGV[3]
|
16
21
|
when 'energy'
|
17
|
-
puts
|
22
|
+
puts obj.energy ARGV[1], ARGV[2], ARGV[3]
|
18
23
|
when 'update_timers'
|
19
|
-
puts
|
24
|
+
puts obj.update_timers ARGV[1], ARGV[2], ARGV[3]
|
20
25
|
when 'timer'
|
21
|
-
puts
|
26
|
+
puts obj.run_timers ARGV[1], ARGV[2]
|
22
27
|
when 'run_timers'
|
23
|
-
puts
|
28
|
+
puts obj.run_timers ARGV[1], ARGV[2]
|
24
29
|
when 'update'
|
25
|
-
puts
|
30
|
+
puts obj.update_config ARGV[1], ARGV[2]
|
26
31
|
when 'web'
|
27
|
-
puts
|
32
|
+
puts obj.build_web_page ARGV[1]
|
28
33
|
when 'summarise'
|
29
|
-
puts
|
34
|
+
puts obj.summarise ARGV[1], ARGV[2]
|
30
35
|
else
|
31
|
-
|
36
|
+
obj.send ARGV[0], ARGV[1], ARGV[2], ARGV[3]
|
32
37
|
end
|
data/bin/lightwaverf-config-json
CHANGED
data/lib/lightwaverf.rb
CHANGED
@@ -22,9 +22,10 @@ class LightWaveRF
|
|
22
22
|
@config_file = nil
|
23
23
|
@log_file = nil
|
24
24
|
@summary_file = nil
|
25
|
-
@
|
25
|
+
@timer_log_file = nil
|
26
26
|
@config = nil
|
27
27
|
@timers = nil
|
28
|
+
@time = nil
|
28
29
|
|
29
30
|
# Display usage info
|
30
31
|
def usage room = nil
|
@@ -40,6 +41,12 @@ class LightWaveRF
|
|
40
41
|
config
|
41
42
|
end
|
42
43
|
|
44
|
+
# For debug timing, why is this so slow?
|
45
|
+
def time label = nil
|
46
|
+
@time = @time || Time.now
|
47
|
+
label.to_s + ' (' + ( Time.now - @time ).to_s + ')'
|
48
|
+
end
|
49
|
+
|
43
50
|
# Display help
|
44
51
|
def help
|
45
52
|
help = self.usage + "\n"
|
@@ -53,19 +60,15 @@ class LightWaveRF
|
|
53
60
|
# Configure, build config file. Interactive command line stuff
|
54
61
|
#
|
55
62
|
# Arguments:
|
56
|
-
# debug: (Boolean
|
63
|
+
# debug: (Boolean)
|
57
64
|
def configure debug = false
|
58
65
|
config = self.get_config
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
# end
|
64
|
-
puts 'What is the address of your google calendar? (' + self.get_config['calendar'] + '). Optional!'
|
66
|
+
puts 'What is the ip address of your wifi link? (currently "' + self.get_config['host'].to_s + '"). Enter a blank line to broadcast UDP commands (ok to just hit enter here).'
|
67
|
+
host = STDIN.gets.chomp
|
68
|
+
config['host'] = host if ! host.to_s.empty?
|
69
|
+
puts 'What is the address of your google calendar? (currently "' + self.get_config['calendar'].to_s + '"). Optional (ok to just hit enter here).'
|
65
70
|
calendar = STDIN.gets.chomp
|
66
|
-
if ! calendar.to_s.empty?
|
67
|
-
config['calendar'] = calendar
|
68
|
-
end
|
71
|
+
config['calendar'] = calendar if ! calendar.to_s.empty?
|
69
72
|
device = 'x'
|
70
73
|
while ! device.to_s.empty?
|
71
74
|
puts 'Enter the name of a room and its devices, space separated. For example "lounge light socket tv". Enter a blank line to finish.'
|
@@ -77,6 +80,9 @@ class LightWaveRF
|
|
77
80
|
found = false
|
78
81
|
config['room'].each do | room |
|
79
82
|
if room['name'] == new_room
|
83
|
+
parts.map! do | device |
|
84
|
+
{ 'name' => device, 'type' => 'O' }
|
85
|
+
end
|
80
86
|
room['device'] = parts
|
81
87
|
found = true
|
82
88
|
end
|
@@ -85,12 +91,13 @@ class LightWaveRF
|
|
85
91
|
if ! found
|
86
92
|
config['room'].push 'name' => new_room, 'device' => parts, 'mood' => nil
|
87
93
|
end
|
88
|
-
debug and ( p 'added ' + parts.to_s + ' to ' + new_room )
|
94
|
+
debug and ( p 'added ' + parts.to_s + ' to ' + new_room.to_s )
|
89
95
|
end
|
90
96
|
end
|
91
97
|
end
|
92
98
|
debug and ( p 'end of configure, config is now ' + config.to_s )
|
93
|
-
self.put_config config
|
99
|
+
file = self.put_config config
|
100
|
+
'Saved config file ' + file
|
94
101
|
end
|
95
102
|
|
96
103
|
# Config file setter
|
@@ -136,7 +143,7 @@ class LightWaveRF
|
|
136
143
|
end
|
137
144
|
unless message.nil?
|
138
145
|
File.open( self.get_timer_log_file, 'a' ) do | f |
|
139
|
-
f.write("\n" + Time.now.to_s + ' - ' + message + ' - ' + ( result ? 'SUCCESS!' : 'FAILED!' ))
|
146
|
+
f.write( "\n" + Time.now.to_s + ' - ' + message + ' - ' + ( result ? 'SUCCESS!' : 'FAILED!' ))
|
140
147
|
end
|
141
148
|
end
|
142
149
|
end
|
@@ -164,10 +171,12 @@ class LightWaveRF
|
|
164
171
|
end
|
165
172
|
end
|
166
173
|
|
167
|
-
|
174
|
+
# Write the config file
|
175
|
+
def put_config config = { 'room' => [ { 'name' => 'our', 'device' => [ 'light' => { 'name' => 'light' }, 'lights' => { 'name' => 'lights' } ] } ] }
|
168
176
|
File.open( self.get_config_file, 'w' ) do | handle |
|
169
177
|
handle.write YAML.dump( config )
|
170
178
|
end
|
179
|
+
self.get_config_file
|
171
180
|
end
|
172
181
|
|
173
182
|
# Get the config file, create it if it does not exist
|
@@ -179,15 +188,15 @@ class LightWaveRF
|
|
179
188
|
end
|
180
189
|
@config = YAML.load_file self.get_config_file
|
181
190
|
# fix where update made names and devices into arrays
|
182
|
-
if @config['room']
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
end
|
191
|
+
# if @config['room']
|
192
|
+
# @config['room'].map! do | room |
|
193
|
+
# room['name'] = room['name'].kind_of?( Array ) ? room['name'][0] : room['name']
|
194
|
+
# room['device'].map! do | device |
|
195
|
+
# device = device.kind_of?( Array ) ? device[0] : device
|
196
|
+
# end
|
197
|
+
# room
|
198
|
+
# end
|
199
|
+
# end
|
191
200
|
end
|
192
201
|
@config
|
193
202
|
end
|
@@ -209,9 +218,7 @@ class LightWaveRF
|
|
209
218
|
# Login to LightWaveRF Host server
|
210
219
|
uri = URI.parse 'https://lightwaverfhost.co.uk/manager/index.php'
|
211
220
|
http = Net::HTTP.new uri.host, uri.port
|
212
|
-
if uri.scheme == 'https'
|
213
|
-
http.use_ssl = true
|
214
|
-
end
|
221
|
+
http.use_ssl = true if uri.scheme == 'https'
|
215
222
|
data = 'pin=' + pin + '&email=' + email
|
216
223
|
headers = { 'Content-Type'=> 'application/x-www-form-urlencoded' }
|
217
224
|
resp, data = http.post uri.request_uri, data, headers
|
@@ -262,7 +269,7 @@ class LightWaveRF
|
|
262
269
|
# o: All Off
|
263
270
|
deviceStatusIndex = roomIndex * 10 + deviceIndex
|
264
271
|
if variables['gDeviceStatus'] and variables['gDeviceStatus'][deviceStatusIndex] and variables['gDeviceStatus'][deviceStatusIndex][0] != 'I'
|
265
|
-
roomDevices << deviceName
|
272
|
+
roomDevices << { 'name' => deviceName, 'type' => variables['gDeviceStatus'][deviceStatusIndex][0] }
|
266
273
|
end
|
267
274
|
end
|
268
275
|
# Create a hash of the active room and active devices and add to rooms array
|
@@ -298,18 +305,19 @@ class LightWaveRF
|
|
298
305
|
end
|
299
306
|
|
300
307
|
# Get a cleaned up version of the rooms and devices from the config file
|
301
|
-
def self.get_rooms config = { 'room' => [ ]}, debug = false
|
308
|
+
def self.get_rooms config = { 'room' => [ ] }, debug = false
|
302
309
|
rooms = { }
|
303
310
|
r = 1
|
304
311
|
config['room'].each do | room |
|
305
|
-
|
312
|
+
room = room.first if room.is_a? Array
|
306
313
|
rooms[room['name']] = { 'id' => 'R' + r.to_s, 'name' => room['name'], 'device' => { }, 'mood' => { }, 'learnmood' => { }}
|
307
314
|
d = 1
|
308
315
|
unless room['device'].nil?
|
309
316
|
room['device'].each do | device |
|
310
|
-
|
311
|
-
|
312
|
-
|
317
|
+
device = device.first if device.is_a? Array
|
318
|
+
device = { 'name' => device } if device.is_a? String
|
319
|
+
device['id'] = 'D' + d.to_s
|
320
|
+
rooms[room['name']]['device'][device['name']] = device
|
313
321
|
d += 1
|
314
322
|
end
|
315
323
|
end
|
@@ -374,9 +382,10 @@ class LightWaveRF
|
|
374
382
|
# state: (String)
|
375
383
|
def command room, device, state
|
376
384
|
# @todo get the device name in here...
|
385
|
+
device = device.to_s
|
377
386
|
# Command structure is <transaction number>,<Command>|<Action>|<State><cr>
|
378
387
|
if room and device and !device.empty? and state
|
379
|
-
'666,!' + room['id'] + room['device'][device] + state + '|Turn ' + room['name'] + ' ' + device + '|' + state + ' via @pauly'
|
388
|
+
'666,!' + room['id'] + room['device'][device]['id'] + state + '|Turn ' + room['name'] + ' ' + device + '|' + state + ' via @pauly'
|
380
389
|
else
|
381
390
|
'666,!' + room['id'] + state + '|Turn ' + room['name'] + '|' + state + ' via @pauly'
|
382
391
|
end
|
@@ -390,11 +399,9 @@ class LightWaveRF
|
|
390
399
|
# Arguments:
|
391
400
|
# debug: (Boolean)
|
392
401
|
def timezone debug = false
|
393
|
-
command = '666,!FzP' + (Time.now.gmt_offset/60/60).to_s
|
394
|
-
|
395
|
-
data
|
396
|
-
debug and ( puts '[Info - LightWaveRF] timezone: response is ' + data )
|
397
|
-
return (data == "666,OK\r\n")
|
402
|
+
command = '666,!FzP' + ( Time.now.gmt_offset/60/60 ).to_s
|
403
|
+
data = self.raw command, true, debug
|
404
|
+
return data == "666,OK\r\n"
|
398
405
|
end
|
399
406
|
|
400
407
|
# Turn one of your devices on or off or all devices in a room off
|
@@ -411,9 +418,11 @@ class LightWaveRF
|
|
411
418
|
# device: (String)
|
412
419
|
# state: (String)
|
413
420
|
def send room = nil, device = nil, state = 'on', debug = false
|
421
|
+
debug and ( p self.time 'send' )
|
414
422
|
success = false
|
415
423
|
debug and ( p 'Executing send on device: ' + device + ' in room: ' + room + ' with state: ' + state )
|
416
424
|
rooms = self.class.get_rooms self.get_config, debug
|
425
|
+
debug and ( p self.time 'got rooms' )
|
417
426
|
|
418
427
|
unless rooms[room] and state
|
419
428
|
debug and ( p 'Missing room (' + room.to_s + ') or state (' + state.to_s + ')' );
|
@@ -432,9 +441,9 @@ class LightWaveRF
|
|
432
441
|
elsif device and rooms[room]['device'][device]
|
433
442
|
state = self.class.get_state state
|
434
443
|
command = self.command rooms[room], device, state
|
435
|
-
debug and ( p 'command is ' + command )
|
444
|
+
debug and ( p self.time 'command is ' + command )
|
436
445
|
data = self.raw command
|
437
|
-
debug and ( p 'response is ' + data )
|
446
|
+
debug and ( p self.time 'response is ' + data.to_s )
|
438
447
|
success = true
|
439
448
|
else
|
440
449
|
STDERR.puts self.usage( room );
|
@@ -482,15 +491,14 @@ class LightWaveRF
|
|
482
491
|
# mood: (String)
|
483
492
|
def mood room = nil, mood = nil, debug = false
|
484
493
|
success = false
|
485
|
-
debug and (p 'Executing mood: ' + mood + ' in room: ' + room)
|
486
|
-
#debug and ( puts 'config is ' + self.get_config.to_s )
|
494
|
+
debug and ( p 'Executing mood: ' + mood + ' in room: ' + room )
|
487
495
|
rooms = self.class.get_rooms self.get_config
|
488
496
|
# support for setting a mood in all rooms (recursive)
|
489
497
|
if room == 'all'
|
490
|
-
debug and ( p
|
498
|
+
debug and ( p 'Processing all rooms...' )
|
491
499
|
rooms.each do | config, each_room |
|
492
500
|
room = each_room['name']
|
493
|
-
debug and ( p
|
501
|
+
debug and ( p 'Room is: ' + room )
|
494
502
|
success = self.mood room, mood, debug
|
495
503
|
sleep 1
|
496
504
|
end
|
@@ -508,8 +516,8 @@ class LightWaveRF
|
|
508
516
|
state = mood[3..-1]
|
509
517
|
debug and (p 'Selected state is: ' + state)
|
510
518
|
rooms[room]['device'].each do | device |
|
511
|
-
p 'Processing device: ' + device[0]
|
512
|
-
self.send room, device[0], state, debug
|
519
|
+
p 'Processing device: ' + device[0].to_s
|
520
|
+
self.send room, device[0]['name'], state, debug
|
513
521
|
sleep 1
|
514
522
|
end
|
515
523
|
success = true
|
@@ -530,8 +538,7 @@ class LightWaveRF
|
|
530
538
|
# room: (String)
|
531
539
|
# mood: (String)
|
532
540
|
def learnmood room = nil, mood = nil, debug = false
|
533
|
-
debug and (p 'Learning mood: ' + mood)
|
534
|
-
#debug and ( puts 'config is ' + self.get_config.to_s )
|
541
|
+
debug and ( p 'Learning mood: ' + mood )
|
535
542
|
rooms = self.class.get_rooms self.get_config
|
536
543
|
if rooms[room] and mood and rooms[room]['learnmood'][mood]
|
537
544
|
command = self.command rooms[room], nil, rooms[room]['learnmood'][mood]
|
@@ -542,10 +549,9 @@ class LightWaveRF
|
|
542
549
|
end
|
543
550
|
end
|
544
551
|
|
545
|
-
def energy title = nil,
|
546
|
-
debug and
|
547
|
-
data = self.raw '666,@?'
|
548
|
-
debug and ( p data )
|
552
|
+
def energy title = nil, text = nil, debug = false
|
553
|
+
debug and text and ( p 'energy: ' + text )
|
554
|
+
data = self.raw '666,@?', true
|
549
555
|
# /W=(?<usage>\d+),(?<max>\d+),(?<today>\d+),(?<yesterday>\d+)/.match data # ruby 1.9 only?
|
550
556
|
match = /W=(\d+),(\d+),(\d+),(\d+)/.match data
|
551
557
|
debug and ( p match )
|
@@ -558,8 +564,38 @@ class LightWaveRF
|
|
558
564
|
}
|
559
565
|
}
|
560
566
|
data['timestamp'] = Time.now.to_s
|
561
|
-
if
|
562
|
-
data['message']['annotation'] = { 'title' => title.to_s, 'text' =>
|
567
|
+
if text
|
568
|
+
data['message']['annotation'] = { 'title' => title.to_s, 'text' => text.to_s }
|
569
|
+
end
|
570
|
+
|
571
|
+
if text
|
572
|
+
if self.get_config['spreadsheet']
|
573
|
+
spreadsheet = self.get_config['spreadsheet']['url']
|
574
|
+
match = /key=([\w-]+)/.match spreadsheet
|
575
|
+
debug and ( p match )
|
576
|
+
if match
|
577
|
+
spreadsheet = match[1]
|
578
|
+
end
|
579
|
+
debug and ( p 'spreadsheet is ' + spreadsheet )
|
580
|
+
if spreadsheet
|
581
|
+
require 'google_drive'
|
582
|
+
session = GoogleDrive.login self.get_config['spreadsheet']['username'], self.get_config['spreadsheet']['password']
|
583
|
+
ws = session.spreadsheet_by_key( spreadsheet ).worksheets[0]
|
584
|
+
rows = ws.num_rows
|
585
|
+
debug and ( p rows.to_s + ' rows in ' + spreadsheet )
|
586
|
+
row = rows + 1
|
587
|
+
ws[ row, 1 ] = data['timestamp']
|
588
|
+
ws[ row, 2 ] = data['message']['usage']
|
589
|
+
ws[ row, 3 ] = data['message']['max']
|
590
|
+
ws[ row, 4 ] = data['message']['today']
|
591
|
+
ws[ row, 5 ] = data['message']['annotation']['title']
|
592
|
+
ws[ row, 6 ] = data['message']['annotation']['text']
|
593
|
+
ws.save( )
|
594
|
+
end
|
595
|
+
else
|
596
|
+
debug and ( p 'no spreadsheet in your config file...' )
|
597
|
+
end
|
598
|
+
|
563
599
|
end
|
564
600
|
debug and ( p data )
|
565
601
|
begin
|
@@ -567,13 +603,7 @@ class LightWaveRF
|
|
567
603
|
f.write( data.to_json + "\n" )
|
568
604
|
end
|
569
605
|
file = self.get_summary_file.gsub 'summary', 'daily'
|
570
|
-
|
571
|
-
begin
|
572
|
-
data['message']['history'] = JSON.parse json
|
573
|
-
rescue => e
|
574
|
-
data['message']['error'] = 'error parsing ' + file + '; ' + e.to_s
|
575
|
-
data['message']['history_json'] = json
|
576
|
-
end
|
606
|
+
data['message']['history'] = self.class.get_json file
|
577
607
|
data['message']
|
578
608
|
rescue
|
579
609
|
puts 'error writing to log'
|
@@ -581,37 +611,49 @@ class LightWaveRF
|
|
581
611
|
end
|
582
612
|
end
|
583
613
|
|
584
|
-
def raw command
|
614
|
+
def raw command, listen = false, debug = false
|
615
|
+
debug and ( p self.time + ' ' + __method__.to_s + ' ' + command )
|
585
616
|
response = nil
|
586
617
|
# Get host address or broadcast address
|
587
618
|
host = self.get_config['host'] || '255.255.255.255'
|
619
|
+
debug and ( p self.time 'got ' + host )
|
588
620
|
# Create socket
|
589
621
|
listener = UDPSocket.new
|
622
|
+
debug and ( p self.time 'got listener' )
|
590
623
|
# Add broadcast socket options if necessary
|
591
|
-
if
|
592
|
-
listener.setsockopt
|
624
|
+
if host == '255.255.255.255'
|
625
|
+
listener.setsockopt Socket::SOL_SOCKET, Socket::SO_BROADCAST, true
|
593
626
|
end
|
594
627
|
if listener
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
628
|
+
if listen
|
629
|
+
# Bind socket to listen for response
|
630
|
+
begin
|
631
|
+
listener.bind '0.0.0.0', 9761
|
632
|
+
rescue
|
633
|
+
response = "can't bind to listen for a reply"
|
634
|
+
end
|
600
635
|
end
|
601
636
|
# Broadcast command to server
|
602
|
-
|
637
|
+
debug and ( p self.time 'sending...' )
|
638
|
+
listener.send command, 0, host, 9760
|
639
|
+
debug and ( p self.time 'sent' )
|
603
640
|
# Receive response
|
604
|
-
if ! response
|
641
|
+
if listen and ! response
|
642
|
+
debug and ( p self.time 'receiving...' )
|
605
643
|
response, addr = listener.recvfrom 200
|
644
|
+
debug and ( p self.time 'received' )
|
606
645
|
end
|
646
|
+
debug and ( p self.time 'closing...' )
|
607
647
|
listener.close
|
648
|
+
debug and ( p self.time 'closed' )
|
608
649
|
end
|
650
|
+
debug and ( puts '[Info - LightWaveRF] ' + __method__.to_s + ': response is ' + response.to_s )
|
609
651
|
response
|
610
652
|
end
|
611
653
|
|
612
654
|
def update_timers past = 60, future = 1440, debug = false
|
613
655
|
p '----------------'
|
614
|
-
p
|
656
|
+
p 'Updating timers...'
|
615
657
|
|
616
658
|
# determine the window to query
|
617
659
|
now = Time.new
|
@@ -983,15 +1025,16 @@ class LightWaveRF
|
|
983
1025
|
p 'Executing sequence. Sequence: ' + event['state']
|
984
1026
|
result = self.sequence event['state'], debug
|
985
1027
|
else
|
986
|
-
p 'Executing device. Room: ' + event['room'] + ', Device: ' + event['device'] + ', State: ' + event['state']
|
987
|
-
result = self.send event['room'], event['device'], event['state'], debug
|
1028
|
+
p 'Executing device. Room: ' + event['room'] + ', Device: ' + event['device'].to_s + ', State: ' + event['state']
|
1029
|
+
# result = self.send event['room'], event['device']['name'], event['state'], debug
|
1030
|
+
result = self.send event['room'], event['device'].to_s, event['state'], debug # is this right?
|
988
1031
|
end
|
989
1032
|
sleep 1
|
990
|
-
triggered << [ event['room'], event['device'], event['state'] ]
|
1033
|
+
triggered << [ event['room'], event['device'].to_s, event['state'] ]
|
991
1034
|
if event['annotate']
|
992
1035
|
annotate = true
|
993
1036
|
end
|
994
|
-
self.log_timer_event event['type'], event['room'], event['device'], event['state'], result
|
1037
|
+
self.log_timer_event event['type'], event['room'], event['device'].to_s, event['state'], result
|
995
1038
|
end
|
996
1039
|
|
997
1040
|
# update energy log
|
@@ -1053,7 +1096,7 @@ class LightWaveRF
|
|
1053
1096
|
Sample page generated #{date} with <code>lightwaverf web</code>.
|
1054
1097
|
Check out <a href="https://github.com/pauly/lightwaverf">the new simplified repo</a> for details
|
1055
1098
|
or <a href="https://rubygems.org/gems/lightwaverf">gem install lightwaverf && lightwaverf web</a>...
|
1056
|
-
<br />@todo
|
1099
|
+
<br />@todo merge this with <a href="https://github.com/pauly/robot-butler">robot butler</a>...
|
1057
1100
|
end
|
1058
1101
|
help = list
|
1059
1102
|
html = <<-end
|
@@ -1105,20 +1148,29 @@ class LightWaveRF
|
|
1105
1148
|
daily = self.class.get_json file
|
1106
1149
|
start_date = 0
|
1107
1150
|
d = nil
|
1151
|
+
last = nil
|
1152
|
+
prev = nil
|
1108
1153
|
File.open( self.get_log_file, 'r' ).each_line do | line |
|
1109
|
-
|
1110
|
-
|
1154
|
+
begin
|
1155
|
+
line = JSON.parse line
|
1156
|
+
rescue
|
1157
|
+
line = nil
|
1158
|
+
end
|
1159
|
+
if line and line['timestamp'] and ( last != line['message']['usage'] )
|
1111
1160
|
new_line = []
|
1112
1161
|
d = line['timestamp'][2..3] + line['timestamp'][5..6] + line['timestamp'][8..9] # compact version of date
|
1113
1162
|
ts = Time.parse( line['timestamp'] ).strftime '%s'
|
1114
1163
|
ts = ts.to_i
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
start_date = ts
|
1164
|
+
ts = ts - start_date
|
1165
|
+
if start_date == 0
|
1166
|
+
# start_date = ts # can't get this delta working
|
1119
1167
|
end
|
1120
1168
|
new_line << ts
|
1121
|
-
|
1169
|
+
smoothedUsage = line['message']['usage'].to_i
|
1170
|
+
if last && prev
|
1171
|
+
smoothedUsage = ( smoothedUsage + last + prev ) / 3 # average of last 3 readings
|
1172
|
+
end
|
1173
|
+
new_line << smoothedUsage / 10
|
1122
1174
|
if line['message']['annotation'] and line['message']['annotation']['title'] and line['message']['annotation']['text']
|
1123
1175
|
new_line << line['message']['annotation']['title']
|
1124
1176
|
new_line << line['message']['annotation']['text']
|
@@ -1128,6 +1180,8 @@ class LightWaveRF
|
|
1128
1180
|
daily[d] = line['message']
|
1129
1181
|
daily[d].delete 'usage'
|
1130
1182
|
end
|
1183
|
+
prev = last
|
1184
|
+
last = line['message']['usage'].to_i
|
1131
1185
|
end
|
1132
1186
|
end
|
1133
1187
|
debug and ( puts 'got ' + data.length.to_s + ' lines in the log' )
|
@@ -1141,7 +1195,8 @@ class LightWaveRF
|
|
1141
1195
|
end
|
1142
1196
|
summary_file = self.get_summary_file
|
1143
1197
|
File.open( summary_file, 'w' ) do |file|
|
1144
|
-
file.write data.to_s
|
1198
|
+
# file.write data.to_s
|
1199
|
+
file.write( JSON.pretty_generate( data ))
|
1145
1200
|
end
|
1146
1201
|
# @todo fix the daily stats, every night it reverts to the minimum value because the timezones are different
|
1147
1202
|
# so 1am on the wifi-link looks midnight on the server
|
@@ -1153,4 +1208,9 @@ class LightWaveRF
|
|
1153
1208
|
end
|
1154
1209
|
end
|
1155
1210
|
|
1211
|
+
# http://lightwaverfcommunity.org.uk/forums/topic/link-no-longer-responding-to-udp-commands-any-advice/page/4/#post-16098
|
1212
|
+
def firmware debug = true
|
1213
|
+
self.raw '666,!F*p', true, debug
|
1214
|
+
end
|
1215
|
+
|
1156
1216
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lightwaverf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.7'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2014-10-24 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: htmlentities
|
@@ -47,7 +47,8 @@ dependencies:
|
|
47
47
|
version: '0'
|
48
48
|
description: ! " Interact with lightwaverf wifi-link from code or the command line.\n
|
49
49
|
\ Control your lights, heating, sockets, sprinkler etc.\n Also use a google
|
50
|
-
calendar, for timers, log energy usage, and build a website.\n
|
50
|
+
calendar, for timers, log energy usage, and build a website.\n And now optionally
|
51
|
+
logging to a google doc too.\n"
|
51
52
|
email: pauly@clarkeology.com
|
52
53
|
executables:
|
53
54
|
- lightwaverf
|