lightwaverf 0.6.6 → 0.7
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/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
|