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