lightwaverf 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/app/views/_graphs.ejs +11 -3
- data/lib/lightwaverf.rb +111 -81
- metadata +2 -2
data/app/views/_graphs.ejs
CHANGED
@@ -71,9 +71,17 @@
|
|
71
71
|
return false;
|
72
72
|
} );
|
73
73
|
$('a.ajax').click( function ( ) {
|
74
|
-
$a = $(this);
|
75
|
-
|
76
|
-
|
74
|
+
var $a = $(this);
|
75
|
+
var status = $a.data( 'status' ) == 'on' ? 'off' : 'on';
|
76
|
+
$.ajax( {
|
77
|
+
url: $a.attr('href') + '?status=' + status,
|
78
|
+
type: 'PUT',
|
79
|
+
success: function( js ) {
|
80
|
+
$a.data( 'status', status );
|
81
|
+
$a.removeClass( status === 'on' ? 'off' : 'on' );
|
82
|
+
$a.addClass( status );
|
83
|
+
alert( js.result || $a.text( ));
|
84
|
+
}
|
77
85
|
} );
|
78
86
|
return false;
|
79
87
|
} );
|
data/lib/lightwaverf.rb
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
# Get rid of references in yaml cache file - use dup more? Or does it not matter?
|
5
5
|
# Cope with events that start and end in the same run?
|
6
6
|
# Add info about states to timer log
|
7
|
-
# Consider adding a 'random' time shift modifier to make holiday security lights more 'realistic'
|
8
7
|
# Build / update cron job automatically
|
9
8
|
|
10
9
|
|
@@ -310,8 +309,6 @@ class LightWaveRF
|
|
310
309
|
m += 1
|
311
310
|
end
|
312
311
|
end
|
313
|
-
# add 'all off' special mood
|
314
|
-
rooms[room['name']]['mood']['alloff'] = 'Fa'
|
315
312
|
r += 1
|
316
313
|
end
|
317
314
|
rooms
|
@@ -322,7 +319,6 @@ class LightWaveRF
|
|
322
319
|
# Example:
|
323
320
|
# >> LightWaveRF.new.state 'on' # 'F1'
|
324
321
|
# >> LightWaveRF.new.state 'off' # 'F0'
|
325
|
-
# >> LightWaveRF.new.state 'alloff' # 'Fa'
|
326
322
|
#
|
327
323
|
# Arguments:
|
328
324
|
# state: (String)
|
@@ -333,11 +329,10 @@ class LightWaveRF
|
|
333
329
|
case state
|
334
330
|
when 'off'
|
335
331
|
state = 'F0'
|
336
|
-
when
|
337
|
-
state = '
|
332
|
+
when 0
|
333
|
+
state = 'F0'
|
338
334
|
when 'on'
|
339
335
|
state = 'F1'
|
340
|
-
# preset dim levels
|
341
336
|
when 'low'
|
342
337
|
state = 'FdP8'
|
343
338
|
when 'mid'
|
@@ -396,6 +391,9 @@ class LightWaveRF
|
|
396
391
|
# >> LightWaveRF.new.send 'our', 'light', 'on'
|
397
392
|
# >> LightWaveRF.new.send 'our', '', 'off'
|
398
393
|
#
|
394
|
+
# This method was too confusing, got rid of "alloff"
|
395
|
+
# it can be done with "[room] all off" anyway
|
396
|
+
#
|
399
397
|
# Arguments:
|
400
398
|
# room: (String)
|
401
399
|
# device: (String)
|
@@ -404,9 +402,9 @@ class LightWaveRF
|
|
404
402
|
success = false
|
405
403
|
debug and ( p 'Executing send on device: ' + device + ' in room: ' + room + ' with state: ' + state )
|
406
404
|
rooms = self.class.get_rooms self.get_config, debug
|
407
|
-
state = 'alloff' if ( device.empty? and state == 'off' )
|
408
405
|
|
409
406
|
unless rooms[room] and state
|
407
|
+
debug and ( p 'Missing room (' + room.to_s + ') or state (' + state.to_s + ')' );
|
410
408
|
STDERR.puts self.usage( room );
|
411
409
|
else
|
412
410
|
# support for setting state for all devices in the room (recursive)
|
@@ -419,7 +417,7 @@ class LightWaveRF
|
|
419
417
|
end
|
420
418
|
success = true
|
421
419
|
# process single device
|
422
|
-
elsif
|
420
|
+
elsif device and rooms[room]['device'][device]
|
423
421
|
state = self.class.get_state state
|
424
422
|
command = self.command rooms[room], device, state
|
425
423
|
debug and ( p 'command is ' + command )
|
@@ -546,10 +544,14 @@ class LightWaveRF
|
|
546
544
|
data['message']['annotation'] = { 'title' => title.to_s, 'text' => note.to_s }
|
547
545
|
end
|
548
546
|
debug and ( p data )
|
549
|
-
|
550
|
-
|
547
|
+
begin
|
548
|
+
File.open( self.get_log_file, 'a' ) do | f |
|
549
|
+
f.write( data.to_json + "\n" )
|
550
|
+
end
|
551
|
+
data['message']
|
552
|
+
rescue
|
553
|
+
puts 'error writing to log'
|
551
554
|
end
|
552
|
-
data['message']
|
553
555
|
end
|
554
556
|
end
|
555
557
|
|
@@ -689,8 +691,8 @@ class LightWaveRF
|
|
689
691
|
time_modifier = 0
|
690
692
|
if command_length > modifier_start
|
691
693
|
debug and ( p "May have modifiers..." )
|
692
|
-
when_modifiers =
|
693
|
-
unless_modifiers =
|
694
|
+
when_modifiers = [ ]
|
695
|
+
unless_modifiers = [ ]
|
694
696
|
modifier_count = command_length - modifier_start
|
695
697
|
debug and ( p "Count of modifiers is " + modifier_count.to_s )
|
696
698
|
for i in modifier_start..(command_length-1)
|
@@ -715,7 +717,6 @@ class LightWaveRF
|
|
715
717
|
end
|
716
718
|
|
717
719
|
# parse the date string
|
718
|
-
debug and ( p "Time string is: " + e.elements['summary'].text)
|
719
720
|
event_time = /When: ([\w ]+) (\d\d:\d\d) to ([\w ]+)?(\d\d:\d\d) \n(.*)<br>(.*)/.match e.elements['summary'].text
|
720
721
|
debug and ( p "Event times are: " + event_time.to_s )
|
721
722
|
start_date = event_time[1].to_s
|
@@ -726,11 +727,14 @@ class LightWaveRF
|
|
726
727
|
if end_date == '' or end_date.nil? # copy start date to end date if it wasn't given (as the same date)
|
727
728
|
end_date = start_date
|
728
729
|
end
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
debug and ( p
|
733
|
-
debug and ( p
|
730
|
+
|
731
|
+
time_modifier += self.class.variance e.elements['title'].text
|
732
|
+
|
733
|
+
debug and ( p 'Start date: ' + start_date )
|
734
|
+
debug and ( p 'Start time: ' + start_time )
|
735
|
+
debug and ( p 'End date: ' + end_date )
|
736
|
+
debug and ( p 'End time: ' + end_time )
|
737
|
+
debug and ( p 'Timezone: ' + timezone )
|
734
738
|
|
735
739
|
# convert to datetimes
|
736
740
|
start_dt = DateTime.parse( start_date.strip + ' ' + start_time.strip + ' ' + timezone.strip )
|
@@ -738,19 +742,19 @@ class LightWaveRF
|
|
738
742
|
|
739
743
|
# apply time modifier if it exists
|
740
744
|
if time_modifier != 0
|
741
|
-
debug and ( p
|
745
|
+
debug and ( p 'Adjusting timings by: ' + time_modifier.to_s )
|
742
746
|
start_dt = (( start_dt.to_time ) + time_modifier * 60 ).to_datetime
|
743
747
|
end_dt = (( end_dt.to_time ) + time_modifier * 60 ).to_datetime
|
744
748
|
end
|
745
749
|
|
746
|
-
debug and ( p
|
747
|
-
debug and ( p
|
750
|
+
debug and ( p 'Start datetime: ' + start_dt.to_s )
|
751
|
+
debug and ( p 'End datetime: ' + end_dt.to_s )
|
748
752
|
|
749
753
|
# populate the dates
|
750
754
|
event['date'] = start_dt
|
751
755
|
# handle device entries without explicit on/off state
|
752
756
|
if event['type'] == 'device' and ( event['state'].nil? or ( event['state'] != 'on' and event['state'] != 'off' ))
|
753
|
-
debug and ( p
|
757
|
+
debug and ( p 'Duplicating event without explicit on/off state...' )
|
754
758
|
# if not state was given, assume we meant 'on'
|
755
759
|
if event['state'].nil?
|
756
760
|
event['state'] = 'on'
|
@@ -762,7 +766,7 @@ class LightWaveRF
|
|
762
766
|
events.push end_event
|
763
767
|
# create state plus start and end events if a state
|
764
768
|
elsif event['type'] == 'state'
|
765
|
-
debug and ( p
|
769
|
+
debug and ( p 'Processing state : ' + event['state'] )
|
766
770
|
# create state
|
767
771
|
state = Hash.new
|
768
772
|
state['name'] = event['state']
|
@@ -805,10 +809,22 @@ class LightWaveRF
|
|
805
809
|
self.log_timer_event 'update', nil, nil, nil, true
|
806
810
|
|
807
811
|
else
|
808
|
-
|
812
|
+
self.log_timer_event 'update', nil, nil, nil, false
|
809
813
|
end
|
810
814
|
end
|
811
815
|
|
816
|
+
# Return the randomness value that may be in the event title
|
817
|
+
def self.variance title = '', debug = nil
|
818
|
+
randomness = /random\w* (\d+)/.match title
|
819
|
+
if randomness
|
820
|
+
n = randomness[1].to_i
|
821
|
+
debug and ( p 'randomness is ' + n.to_s )
|
822
|
+
return rand( n ) - ( n / 2 )
|
823
|
+
end
|
824
|
+
debug and ( p 'no randomness return 0' )
|
825
|
+
return 0
|
826
|
+
end
|
827
|
+
|
812
828
|
# Convert a string to seconds, assume it is in minutes
|
813
829
|
def self.to_seconds interval = 0
|
814
830
|
match = /^(\d+)([shd])$/.match( interval.to_s )
|
@@ -827,7 +843,7 @@ class LightWaveRF
|
|
827
843
|
|
828
844
|
def run_timers interval = 5, debug = false
|
829
845
|
p '----------------'
|
830
|
-
p
|
846
|
+
p 'Running timers...'
|
831
847
|
get_timer_cache
|
832
848
|
debug and ( p 'Timer list is: ' + YAML.dump( @timers ))
|
833
849
|
|
@@ -871,7 +887,7 @@ class LightWaveRF
|
|
871
887
|
debug and ( p 'Event has when modifiers. Checking they are all met...')
|
872
888
|
|
873
889
|
# determine which states apply at the time of the event
|
874
|
-
applicable_states =
|
890
|
+
applicable_states = [ ]
|
875
891
|
@timers['states'].each do | state |
|
876
892
|
if event['date'] >= state['start'] and event['date'] < state['end']
|
877
893
|
applicable_states.push state['name']
|
@@ -939,7 +955,7 @@ class LightWaveRF
|
|
939
955
|
if triggered.length > 0
|
940
956
|
debug and ( p triggered.length.to_s + ' events so annotating energy log too...' )
|
941
957
|
title = 'timer'
|
942
|
-
text = triggered.map { | e | e.join
|
958
|
+
text = triggered.map { | e | e.join ' ' }.join ', '
|
943
959
|
end
|
944
960
|
self.energy title, text, debug
|
945
961
|
|
@@ -947,23 +963,27 @@ class LightWaveRF
|
|
947
963
|
end
|
948
964
|
|
949
965
|
def self.get_contents file
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
966
|
+
begin
|
967
|
+
file = File.open file, 'r'
|
968
|
+
content = file.read
|
969
|
+
file.close
|
970
|
+
rescue
|
971
|
+
STDERR.puts 'cannot open ' + file
|
972
|
+
end
|
973
|
+
content.to_s
|
954
974
|
end
|
955
975
|
|
956
976
|
def build_web_page debug = nil
|
957
977
|
|
958
978
|
rooms = self.class.get_rooms self.get_config
|
959
979
|
list = '<dl>'
|
960
|
-
config = 'usage: lightwaverf ' + rooms.values.first['name'] + ' ' + rooms.values.first['device'].keys.first.to_s + ' on # where "' + rooms.keys.first + '" is a room in ' + self.get_config_file
|
961
980
|
rooms.each do | name, room |
|
962
981
|
debug and ( puts name + ' is ' + room.to_s )
|
963
|
-
list += '<dt>' + name + '</dt><dd><ul>'
|
982
|
+
list += '<dt><a>' + name + '</a></dt><dd><ul>'
|
964
983
|
room['device'].each do | device |
|
965
|
-
|
966
|
-
|
984
|
+
# link ideally relative to avoid cross domain issues
|
985
|
+
link = '/room/' + room['name'].to_s + '/' + device.first.to_s
|
986
|
+
list += '<li><a class="ajax off" href="' + link + '">' + room['name'].to_s + ' ' + device.first.to_s + '</a></li>'
|
967
987
|
end
|
968
988
|
list += '</ul></dd>'
|
969
989
|
end
|
@@ -974,41 +994,49 @@ class LightWaveRF
|
|
974
994
|
date = Time.new.to_s
|
975
995
|
title = self.get_config.has_key?('title') ? self.get_config['title'] : ( 'Lightwaverf energy stats ' + date )
|
976
996
|
intro = <<-end
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
997
|
+
Sample page generated #{date} with <code>lightwaverf web</code>.
|
998
|
+
Check out <a href="https://github.com/pauly/lightwaverf">the new simplified repo</a> for details
|
999
|
+
or <a href="https://rubygems.org/gems/lightwaverf">gem install lightwaverf && lightwaverf web</a>...
|
1000
|
+
<br />@todo make a decent, useful, simple, configurable web page...
|
981
1001
|
end
|
982
1002
|
help = list
|
983
1003
|
html = <<-end
|
984
1004
|
<html>
|
985
1005
|
<head>
|
986
1006
|
<title>#{title}</title>
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
1007
|
+
<style type="text/css">
|
1008
|
+
body { font-family: arial, verdana, sans-serif; }
|
1009
|
+
div#energy_chart { width: 800px; height: 600px; }
|
1010
|
+
div#gauge_div { width: 100px; height: 100px; }
|
1011
|
+
dd { display: none; }
|
1012
|
+
.off, .on:hover { padding-right: 18px; background: url(lightning_delete.png) no-repeat top right; }
|
1013
|
+
.on, .off:hover { padding-right: 18px; background: url(lightning_add.png) no-repeat top right; }
|
1014
|
+
</style>
|
1015
|
+
</head>
|
993
1016
|
<body>
|
994
1017
|
<div class="container">
|
995
1018
|
<div class="row">
|
996
1019
|
<div class="col">
|
997
|
-
|
998
|
-
|
999
|
-
<div id="energy_chart"
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1020
|
+
<h1>#{title}</h1>
|
1021
|
+
<p class="intro">#{intro}</p>
|
1022
|
+
<div id="energy_chart">
|
1023
|
+
Not seeing an energy chart here?
|
1024
|
+
Maybe not working in your device yet, sorry.
|
1025
|
+
This uses google chart api which may generate FLASH :-(
|
1026
|
+
Try in a web browser.
|
1027
|
+
</div>
|
1028
|
+
<h2>Rooms and devices</h2>
|
1029
|
+
<p>@todo make these links to control the devices...</p>
|
1030
|
+
<p class="help">#{help}</p>
|
1031
|
+
#{js}
|
1032
|
+
</div>
|
1005
1033
|
<div class="col">
|
1006
1034
|
<div class="col" id="gauge_div"></div>
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1035
|
+
</div>
|
1036
|
+
</div>
|
1037
|
+
</div>
|
1038
|
+
<p>By <a href="http://www.clarkeology.com/blog/">Paul Clarke</a>, a work in progress.</p>
|
1039
|
+
</body>
|
1012
1040
|
</html>
|
1013
1041
|
end
|
1014
1042
|
end
|
@@ -1023,32 +1051,35 @@ class LightWaveRF
|
|
1023
1051
|
File.open( self.get_log_file, 'r' ).each_line do | line |
|
1024
1052
|
line = JSON.parse( line )
|
1025
1053
|
if line and line['timestamp']
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1054
|
+
new_line = []
|
1055
|
+
d = line['timestamp'][2..3] + line['timestamp'][5..6] + line['timestamp'][8..9] # compact version of date
|
1056
|
+
ts = Time.parse( line['timestamp'] ).strftime '%s'
|
1057
|
+
ts = ts.to_i
|
1058
|
+
if start_date > 0
|
1059
|
+
ts = ts - start_date
|
1060
|
+
else
|
1061
|
+
start_date = ts
|
1062
|
+
end
|
1063
|
+
new_line << ts
|
1064
|
+
new_line << line['message']['usage'].to_i / 10
|
1065
|
+
if line['message']['annotation'] and line['message']['annotation']['title'] and line['message']['annotation']['text']
|
1066
|
+
new_line << line['message']['annotation']['title']
|
1067
|
+
new_line << line['message']['annotation']['text']
|
1068
|
+
end
|
1069
|
+
data << new_line
|
1070
|
+
if ( ! daily[d] or line['message']['today'] > daily[d]['today'] )
|
1043
1071
|
daily[d] = line['message']
|
1044
|
-
|
1072
|
+
end
|
1045
1073
|
end
|
1046
1074
|
end
|
1047
1075
|
debug and ( puts 'got ' + data.length.to_s + ' lines in the log' )
|
1048
1076
|
data = data.last( 60 * 24 * days )
|
1049
1077
|
debug and ( puts 'now got ' + data.length.to_s + ' lines in the log ( 60 * 24 * ' + days.to_s + ' = ' + ( 60 * 24 * days ).to_s + ' )' )
|
1050
|
-
if data[0]
|
1051
|
-
data[0][0]
|
1078
|
+
if data and data[0]
|
1079
|
+
debug and ( puts 'data[0] is ' + data[0].to_s )
|
1080
|
+
if data[0][0] != start_date
|
1081
|
+
data[0][0] += start_date
|
1082
|
+
end
|
1052
1083
|
end
|
1053
1084
|
summary_file = self.get_summary_file
|
1054
1085
|
File.open( summary_file, 'w' ) do |file|
|
@@ -1064,4 +1095,3 @@ class LightWaveRF
|
|
1064
1095
|
end
|
1065
1096
|
|
1066
1097
|
end
|
1067
|
-
|
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.6.0
|
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: 2013-07-
|
14
|
+
date: 2013-07-30 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: htmlentities
|