lightwaverf 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/app/views/_graphs.ejs +5 -2
- data/lib/lightwaverf.rb +114 -88
- metadata +2 -2
data/app/views/_graphs.ejs
CHANGED
@@ -42,8 +42,6 @@
|
|
42
42
|
energy_data.addRows( raw_data.map( function ( e ) {
|
43
43
|
if ( e[0] !== start_date ) e[0] += start_date;
|
44
44
|
e[0] = new Date( 1000 * e[0] ); // as it is now a timestamp
|
45
|
-
// var d = '' + e[0];
|
46
|
-
// e[0] = new Date( '20' + d[0] + d[1] + '-' + d[2] + d[3] + '-' + d[4] + d[5] + ' ' + d[6] + d[7] + ':' + d[8] + d[9] );
|
47
45
|
e[1] = e[1] * 10;
|
48
46
|
e[2] = e[2] || '';
|
49
47
|
e[3] = e[3] || '';
|
@@ -66,6 +64,7 @@
|
|
66
64
|
gauge.draw( gauge_data, gauge_options );
|
67
65
|
} );
|
68
66
|
$( function ( ) {
|
67
|
+
var key = 'foo';
|
69
68
|
$('dt a').click( function ( ) {
|
70
69
|
$(this).parent( ).next('dd').slideDown( );
|
71
70
|
return false;
|
@@ -75,6 +74,10 @@
|
|
75
74
|
var status = $a.data( 'status' ) == 'on' ? 'off' : 'on';
|
76
75
|
$.ajax( {
|
77
76
|
url: $a.attr('href') + '?status=' + status,
|
77
|
+
data: {
|
78
|
+
status: status,
|
79
|
+
key: key,
|
80
|
+
},
|
78
81
|
type: 'PUT',
|
79
82
|
success: function( js ) {
|
80
83
|
$a.data( 'status', status );
|
data/lib/lightwaverf.rb
CHANGED
@@ -538,19 +538,33 @@ class LightWaveRF
|
|
538
538
|
match = /W=(\d+),(\d+),(\d+),(\d+)/.match data
|
539
539
|
debug and ( p match )
|
540
540
|
if match
|
541
|
-
data = {
|
541
|
+
data = {
|
542
|
+
'message' => {
|
543
|
+
'usage' => match[1].to_i,
|
544
|
+
'max' => match[2].to_i,
|
545
|
+
'today' => match[3].to_i
|
546
|
+
}
|
547
|
+
}
|
542
548
|
data['timestamp'] = Time.now.to_s
|
543
549
|
if note
|
544
550
|
data['message']['annotation'] = { 'title' => title.to_s, 'text' => note.to_s }
|
545
551
|
end
|
546
552
|
debug and ( p data )
|
547
553
|
begin
|
548
|
-
|
554
|
+
File.open( self.get_log_file, 'a' ) do | f |
|
549
555
|
f.write( data.to_json + "\n" )
|
550
|
-
|
551
|
-
|
556
|
+
end
|
557
|
+
file = self.get_summary_file.gsub 'summary', 'daily'
|
558
|
+
json = self.class.get_contents file
|
559
|
+
begin
|
560
|
+
data['message']['history'] = JSON.parse json
|
561
|
+
rescue => e
|
562
|
+
data['message']['error'] = 'error parsing ' + file + '; ' + e.to_s
|
563
|
+
data['message']['history_json'] = json
|
564
|
+
end
|
565
|
+
data['message']
|
552
566
|
rescue
|
553
|
-
|
567
|
+
puts 'error writing to log'
|
554
568
|
end
|
555
569
|
end
|
556
570
|
end
|
@@ -592,9 +606,15 @@ class LightWaveRF
|
|
592
606
|
query_start = now - self.class.to_seconds( past )
|
593
607
|
query_end = now + self.class.to_seconds( future )
|
594
608
|
|
595
|
-
url = LightWaveRF.new.get_config['calendar']
|
596
|
-
|
597
|
-
|
609
|
+
# url = LightWaveRF.new.get_config['calendar']
|
610
|
+
url = self.get_config['calendar']
|
611
|
+
|
612
|
+
url += '?ctz=' + Time.new.zone
|
613
|
+
# url += '?ctz=UTC'
|
614
|
+
if Time.new.zone != 'UTC'
|
615
|
+
p 'time zone is ' + Time.new.zone + ' so look out...'
|
616
|
+
end
|
617
|
+
|
598
618
|
url += '&singleevents=true'
|
599
619
|
url += '&start-min=' + query_start.strftime( '%FT%T%:z' ).sub('+', '%2B')
|
600
620
|
url += '&start-max=' + query_end.strftime( '%FT%T%:z' ).sub('+', '%2B')
|
@@ -625,20 +645,20 @@ class LightWaveRF
|
|
625
645
|
|
626
646
|
# refresh the list of entries for the caching period
|
627
647
|
doc.elements.each 'feed/entry' do | e |
|
628
|
-
debug and ( p
|
629
|
-
debug and ( p
|
648
|
+
debug and ( p '-------------------' )
|
649
|
+
debug and ( p 'Processing entry...' )
|
630
650
|
event = Hash.new
|
631
651
|
|
632
652
|
# tokenise the title
|
633
|
-
debug and ( p
|
653
|
+
debug and ( p 'Event title is: ' + e.elements['title'].text )
|
634
654
|
command = e.elements['title'].text.split
|
635
655
|
command_length = command.length
|
636
|
-
debug and ( p
|
656
|
+
debug and ( p 'Number of words is: ' + command_length.to_s )
|
637
657
|
if command and command.length >= 1
|
638
658
|
first_word = command[0].to_s
|
639
659
|
# determine the type of the entry
|
640
660
|
if first_word[0,1] == '#'
|
641
|
-
debug and ( p
|
661
|
+
debug and ( p 'Type is: state' )
|
642
662
|
event['type'] = 'state' # temporary type, will be overridden later
|
643
663
|
event['room'] = nil
|
644
664
|
event['device'] = nil
|
@@ -647,21 +667,21 @@ class LightWaveRF
|
|
647
667
|
else
|
648
668
|
case first_word
|
649
669
|
when 'mood'
|
650
|
-
debug and ( p
|
670
|
+
debug and ( p 'Type is: mood' )
|
651
671
|
event['type'] = 'mood'
|
652
672
|
event['room'] = command[1].to_s
|
653
673
|
event['device'] = nil
|
654
674
|
event['state'] = command[2].to_s
|
655
675
|
modifier_start = 3
|
656
676
|
when 'sequence'
|
657
|
-
debug and ( p
|
677
|
+
debug and ( p 'Type is: sequence' )
|
658
678
|
event['type'] = 'sequence'
|
659
679
|
event['room'] = nil
|
660
680
|
event['device'] = nil
|
661
681
|
event['state'] = command[1].to_s
|
662
682
|
modifier_start = 2
|
663
683
|
else
|
664
|
-
debug and ( p
|
684
|
+
debug and ( p 'Type is: device' )
|
665
685
|
event['type'] = 'device'
|
666
686
|
event['room'] = command[0].to_s
|
667
687
|
event['device'] = command[1].to_s
|
@@ -669,18 +689,19 @@ class LightWaveRF
|
|
669
689
|
if command_length > 2
|
670
690
|
third_word = command[2].to_s
|
671
691
|
first_char = third_word[0,1]
|
672
|
-
debug and ( p
|
692
|
+
debug and ( p 'First char is: ' + first_char )
|
673
693
|
# if the third word does not start with a modifier flag, assume it's a state
|
674
|
-
if first_char != '@' and first_char != '!' and first_char != '+' and first_char != '-'
|
675
|
-
|
694
|
+
# if first_char != '@' and first_char != '!' and first_char != '+' and first_char != '-'
|
695
|
+
if /\w/.match first_char
|
696
|
+
debug and ( p 'State has been given.')
|
676
697
|
event['state'] = command[2].to_s
|
677
698
|
modifier_start = 3
|
678
699
|
else
|
679
|
-
debug and ( p
|
700
|
+
debug and ( p 'State has not been given.' )
|
680
701
|
modifier_start = 2
|
681
702
|
end
|
682
703
|
else
|
683
|
-
debug and ( p
|
704
|
+
debug and ( p 'State has not been given.' )
|
684
705
|
event['state'] = nil
|
685
706
|
modifier_start = 2
|
686
707
|
end
|
@@ -690,24 +711,24 @@ class LightWaveRF
|
|
690
711
|
# get modifiers if they exist
|
691
712
|
time_modifier = 0
|
692
713
|
if command_length > modifier_start
|
693
|
-
debug and ( p
|
714
|
+
debug and ( p 'May have modifiers...' )
|
694
715
|
when_modifiers = [ ]
|
695
716
|
unless_modifiers = [ ]
|
696
717
|
modifier_count = command_length - modifier_start
|
697
|
-
debug and ( p
|
718
|
+
debug and ( p 'Count of modifiers is ' + modifier_count.to_s )
|
698
719
|
for i in modifier_start..(command_length-1)
|
699
720
|
modifier = command[i]
|
700
721
|
if modifier[0,1] == '@'
|
701
|
-
debug and ( p
|
722
|
+
debug and ( p 'Found when modifier: ' + modifier[1..-1] )
|
702
723
|
when_modifiers.push modifier[1..-1]
|
703
724
|
elsif modifier[0,1] == '!'
|
704
|
-
debug and ( p
|
725
|
+
debug and ( p 'Found unless modifier: ' + modifier[1..-1] )
|
705
726
|
unless_modifiers.push modifier[1..-1]
|
706
727
|
elsif modifier[0,1] == '+'
|
707
|
-
debug and ( p
|
728
|
+
debug and ( p 'Found positive time modifier: ' + modifier[1..-1] )
|
708
729
|
time_modifier = modifier[1..-1].to_i
|
709
730
|
elsif modifier[0,1] == '-'
|
710
|
-
debug and ( p
|
731
|
+
debug and ( p 'Found negative time modifier: ' + modifier[1..-1] )
|
711
732
|
time_modifier = modifier[1..-1].to_i * -1
|
712
733
|
end
|
713
734
|
end
|
@@ -718,7 +739,7 @@ class LightWaveRF
|
|
718
739
|
|
719
740
|
# parse the date string
|
720
741
|
event_time = /When: ([\w ]+) (\d\d:\d\d) to ([\w ]+)?(\d\d:\d\d) \n(.*)<br>(.*)/.match e.elements['summary'].text
|
721
|
-
debug and ( p
|
742
|
+
debug and ( p 'Event times are: ' + event_time.to_s )
|
722
743
|
start_date = event_time[1].to_s
|
723
744
|
start_time = event_time[2].to_s
|
724
745
|
end_date = event_time[3].to_s
|
@@ -729,6 +750,7 @@ class LightWaveRF
|
|
729
750
|
end
|
730
751
|
|
731
752
|
time_modifier += self.class.variance e.elements['title'].text
|
753
|
+
event['annotate'] = ! ( /do not annotate/.match e.elements['title'].text )
|
732
754
|
|
733
755
|
debug and ( p 'Start date: ' + start_date )
|
734
756
|
debug and ( p 'Start time: ' + start_time )
|
@@ -884,7 +906,7 @@ class LightWaveRF
|
|
884
906
|
|
885
907
|
# if has modifiers, check modifiers against states
|
886
908
|
unless event['when_modifiers'].nil?
|
887
|
-
debug and ( p 'Event has when modifiers. Checking they are all met...')
|
909
|
+
debug and ( p 'Event has when modifiers. Checking they are all met...' )
|
888
910
|
|
889
911
|
# determine which states apply at the time of the event
|
890
912
|
applicable_states = [ ]
|
@@ -893,12 +915,12 @@ class LightWaveRF
|
|
893
915
|
applicable_states.push state['name']
|
894
916
|
end
|
895
917
|
end
|
896
|
-
debug and ( p 'Applicable states are: ' + applicable_states.to_s)
|
918
|
+
debug and ( p 'Applicable states are: ' + applicable_states.to_s )
|
897
919
|
|
898
920
|
# check that each when modifier exists in appliable states
|
899
921
|
event['when_modifiers'].each do | modifier |
|
900
922
|
unless applicable_states.include? modifier
|
901
|
-
debug and ( p 'Event when modifier not met: ' + modifier)
|
923
|
+
debug and ( p 'Event when modifier not met: ' + modifier )
|
902
924
|
run_now = false
|
903
925
|
break
|
904
926
|
end
|
@@ -907,7 +929,7 @@ class LightWaveRF
|
|
907
929
|
# check that each unless modifier does not exist in appliable states
|
908
930
|
event['unless_modifiers'].each do | modifier |
|
909
931
|
if applicable_states.include? modifier
|
910
|
-
debug and ( p 'Event unless modifier not met: ' + modifier)
|
932
|
+
debug and ( p 'Event unless modifier not met: ' + modifier )
|
911
933
|
run_now = false
|
912
934
|
break
|
913
935
|
end
|
@@ -927,32 +949,32 @@ class LightWaveRF
|
|
927
949
|
|
928
950
|
triggered = [ ]
|
929
951
|
|
952
|
+
annotate = false
|
930
953
|
run_list.each do | event |
|
931
954
|
# execute based on type
|
932
955
|
case event['type']
|
933
956
|
when 'mood'
|
934
957
|
p 'Executing mood. Room: ' + event['room'] + ', Mood: ' + event['state']
|
935
958
|
result = self.mood event['room'], event['state'], debug
|
936
|
-
sleep 1
|
937
|
-
triggered << [ event['room'], event['device'], event['state'] ]
|
938
959
|
when 'sequence'
|
939
960
|
p 'Executing sequence. Sequence: ' + event['state']
|
940
961
|
result = self.sequence event['state'], debug
|
941
|
-
sleep 1
|
942
|
-
triggered << [ event['room'], event['device'], event['state'] ]
|
943
962
|
else
|
944
963
|
p 'Executing device. Room: ' + event['room'] + ', Device: ' + event['device'] + ', State: ' + event['state']
|
945
964
|
result = self.send event['room'], event['device'], event['state'], debug
|
946
|
-
sleep 1
|
947
|
-
triggered << [ event['room'], event['device'], event['state'] ]
|
948
965
|
end
|
949
|
-
|
966
|
+
sleep 1
|
967
|
+
triggered << [ event['room'], event['device'], event['state'] ]
|
968
|
+
if event['annotate']
|
969
|
+
annotate = true
|
970
|
+
end
|
971
|
+
self.log_timer_event event['type'], event['room'], event['device'], event['state'], result
|
950
972
|
end
|
951
973
|
|
952
974
|
# update energy log
|
953
975
|
title = nil
|
954
976
|
text = nil
|
955
|
-
if
|
977
|
+
if annotate
|
956
978
|
debug and ( p triggered.length.to_s + ' events so annotating energy log too...' )
|
957
979
|
title = 'timer'
|
958
980
|
text = triggered.map { | e | e.join ' ' }.join ', '
|
@@ -981,9 +1003,9 @@ class LightWaveRF
|
|
981
1003
|
debug and ( puts name + ' is ' + room.to_s )
|
982
1004
|
list += '<dt><a>' + name + '</a></dt><dd><ul>'
|
983
1005
|
room['device'].each do | device |
|
984
|
-
|
985
|
-
|
986
|
-
|
1006
|
+
# link ideally relative to avoid cross domain issues
|
1007
|
+
link = '/room/' + room['name'].to_s + '/' + device.first.to_s
|
1008
|
+
list += '<li><a class="ajax off" href="' + link + '">' + room['name'].to_s + ' ' + device.first.to_s + '</a></li>'
|
987
1009
|
end
|
988
1010
|
list += '</ul></dd>'
|
989
1011
|
end
|
@@ -1004,39 +1026,39 @@ class LightWaveRF
|
|
1004
1026
|
<html>
|
1005
1027
|
<head>
|
1006
1028
|
<title>#{title}</title>
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1029
|
+
<style type="text/css">
|
1030
|
+
body { font-family: arial, verdana, sans-serif; }
|
1031
|
+
div#energy_chart { width: 800px; height: 600px; }
|
1032
|
+
div#gauge_div { width: 100px; height: 100px; }
|
1033
|
+
dd { display: none; }
|
1034
|
+
.off, .on:hover { padding-right: 18px; background: url(lightning_delete.png) no-repeat top right; }
|
1035
|
+
.on, .off:hover { padding-right: 18px; background: url(lightning_add.png) no-repeat top right; }
|
1036
|
+
</style>
|
1037
|
+
</head>
|
1016
1038
|
<body>
|
1017
1039
|
<div class="container">
|
1018
1040
|
<div class="row">
|
1019
1041
|
<div class="col">
|
1020
|
-
|
1021
|
-
|
1042
|
+
<h1>#{title}</h1>
|
1043
|
+
<p class="intro">#{intro}</p>
|
1022
1044
|
<div id="energy_chart">
|
1023
1045
|
Not seeing an energy chart here?
|
1024
1046
|
Maybe not working in your device yet, sorry.
|
1025
1047
|
This uses google chart api which may generate FLASH :-(
|
1026
1048
|
Try in a web browser.
|
1027
1049
|
</div>
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1050
|
+
<h2>Rooms and devices</h2>
|
1051
|
+
<p>@todo make these links to control the devices...</p>
|
1052
|
+
<p class="help">#{help}</p>
|
1053
|
+
#{js}
|
1054
|
+
</div>
|
1033
1055
|
<div class="col">
|
1034
1056
|
<div class="col" id="gauge_div"></div>
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1057
|
+
</div>
|
1058
|
+
</div>
|
1059
|
+
</div>
|
1060
|
+
<p>By <a href="http://www.clarkeology.com/blog/">Paul Clarke</a>, a work in progress.</p>
|
1061
|
+
</body>
|
1040
1062
|
</html>
|
1041
1063
|
end
|
1042
1064
|
end
|
@@ -1045,35 +1067,38 @@ class LightWaveRF
|
|
1045
1067
|
def summarise days = 7, debug = nil
|
1046
1068
|
days = days.to_i
|
1047
1069
|
data = [ ]
|
1048
|
-
|
1070
|
+
file = self.get_summary_file.gsub 'summary', 'daily'
|
1071
|
+
json = self.class.get_contents file
|
1072
|
+
daily = JSON.parse json
|
1049
1073
|
start_date = 0
|
1050
1074
|
d = nil
|
1051
1075
|
File.open( self.get_log_file, 'r' ).each_line do | line |
|
1052
|
-
line = JSON.parse
|
1076
|
+
line = JSON.parse line
|
1053
1077
|
if line and line['timestamp']
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1078
|
+
new_line = []
|
1079
|
+
d = line['timestamp'][2..3] + line['timestamp'][5..6] + line['timestamp'][8..9] # compact version of date
|
1080
|
+
ts = Time.parse( line['timestamp'] ).strftime '%s'
|
1081
|
+
ts = ts.to_i
|
1082
|
+
if start_date > 0
|
1083
|
+
ts = ts - start_date
|
1084
|
+
else
|
1085
|
+
start_date = ts
|
1086
|
+
end
|
1087
|
+
new_line << ts
|
1088
|
+
new_line << line['message']['usage'].to_i / 10
|
1089
|
+
if line['message']['annotation'] and line['message']['annotation']['title'] and line['message']['annotation']['text']
|
1090
|
+
new_line << line['message']['annotation']['title']
|
1091
|
+
new_line << line['message']['annotation']['text']
|
1092
|
+
end
|
1093
|
+
data << new_line
|
1094
|
+
if (( ! daily[d] ) or ( line['message']['today'] > daily[d]['today'] ))
|
1071
1095
|
daily[d] = line['message']
|
1072
|
-
|
1096
|
+
daily[d].delete 'usage'
|
1097
|
+
end
|
1073
1098
|
end
|
1074
1099
|
end
|
1075
1100
|
debug and ( puts 'got ' + data.length.to_s + ' lines in the log' )
|
1076
|
-
data = data.last
|
1101
|
+
data = data.last 60 * 24 * days
|
1077
1102
|
debug and ( puts 'now got ' + data.length.to_s + ' lines in the log ( 60 * 24 * ' + days.to_s + ' = ' + ( 60 * 24 * days ).to_s + ' )' )
|
1078
1103
|
if data and data[0]
|
1079
1104
|
debug and ( puts 'data[0] is ' + data[0].to_s )
|
@@ -1085,12 +1110,13 @@ class LightWaveRF
|
|
1085
1110
|
File.open( summary_file, 'w' ) do |file|
|
1086
1111
|
file.write data.to_s
|
1087
1112
|
end
|
1088
|
-
# @todo fix the daily stats, every night it reverts to the minimum value
|
1113
|
+
# @todo fix the daily stats, every night it reverts to the minimum value because the timezones are different
|
1114
|
+
# so 1am on the wifi-link looks midnight on the server
|
1089
1115
|
File.open( summary_file.gsub( 'summary', 'daily' ), 'w' ) do | file |
|
1090
|
-
file.write daily.to_s
|
1116
|
+
file.write daily.to_json.to_s
|
1091
1117
|
end
|
1092
1118
|
File.open( summary_file.gsub( 'summary', 'daily.' + d ), 'w' ) do | file |
|
1093
|
-
file.write daily.select { |key| key == daily.keys.last }.to_s
|
1119
|
+
file.write daily.select { |key| key == daily.keys.last }.to_json.to_s
|
1094
1120
|
end
|
1095
1121
|
end
|
1096
1122
|
|
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.6.
|
4
|
+
version: 0.6.1
|
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-
|
14
|
+
date: 2013-08-04 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: htmlentities
|