lightwaverf 0.6.0 → 0.6.1
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 +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
|