lightwaverf 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/app/views/_graphs.ejs +81 -0
- data/bin/lightwaverf +4 -0
- data/lib/lightwaverf.rb +132 -15
- metadata +5 -4
@@ -0,0 +1,81 @@
|
|
1
|
+
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
|
2
|
+
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
|
3
|
+
<script type="text/javascript">
|
4
|
+
var gauge, gauge_data, gauge_options;
|
5
|
+
google.load( 'visualization', '1.0', { packages: [ 'corechart', 'gauge', 'annotatedtimeline' ] } );
|
6
|
+
google.setOnLoadCallback( function ( ) {
|
7
|
+
var energy_data = new google.visualization.DataTable( );
|
8
|
+
energy_data.addColumn( 'date', 'Date' );
|
9
|
+
energy_data.addColumn( 'number', 'Electricity used' );
|
10
|
+
energy_data.addColumn( 'string', 'title1' );
|
11
|
+
energy_data.addColumn( 'string', 'text1' );
|
12
|
+
if (!Array.prototype.map) {
|
13
|
+
Array.prototype.map = function(callback, thisArg) {
|
14
|
+
var T, A, k;
|
15
|
+
if (this == null) {
|
16
|
+
throw new TypeError(" this is null or not defined");
|
17
|
+
}
|
18
|
+
var O = Object(this);
|
19
|
+
var len = O.length >>> 0;
|
20
|
+
if (typeof callback !== "function") {
|
21
|
+
throw new TypeError(callback + " is not a function");
|
22
|
+
}
|
23
|
+
if (thisArg) {
|
24
|
+
T = thisArg;
|
25
|
+
}
|
26
|
+
A = new Array(len);
|
27
|
+
k = 0;
|
28
|
+
while(k < len) {
|
29
|
+
var kValue, mappedValue;
|
30
|
+
if (k in O) {
|
31
|
+
kValue = O[ k ];
|
32
|
+
mappedValue = callback.call(T, kValue, k, O);
|
33
|
+
A[ k ] = mappedValue;
|
34
|
+
}
|
35
|
+
k++;
|
36
|
+
}
|
37
|
+
return A;
|
38
|
+
};
|
39
|
+
}
|
40
|
+
var raw_data = <%- summary %>;
|
41
|
+
var start_date = raw_data[0][0];
|
42
|
+
energy_data.addRows( raw_data.map( function ( e ) {
|
43
|
+
if ( e[0] !== start_date ) e[0] += start_date;
|
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
|
+
e[1] = e[1] * 10;
|
48
|
+
e[2] = e[2] || '';
|
49
|
+
e[3] = e[3] || '';
|
50
|
+
return e;
|
51
|
+
} ));
|
52
|
+
var chart = new google.visualization.AnnotatedTimeLine( document.getElementById( 'energy_chart' ));
|
53
|
+
chart.draw( energy_data, { displayAnnotations: true, title: '24 hours electricity usage' } );
|
54
|
+
|
55
|
+
gauge = new google.visualization.Gauge( document.getElementById( 'gauge_div' ));
|
56
|
+
gauge_data = google.visualization.arrayToDataTable( [ ["Label", "Value"], ["Electric", raw_data.pop[1] ] ] );
|
57
|
+
gauge_options = {
|
58
|
+
width: '200',
|
59
|
+
height: '200',
|
60
|
+
redFrom: 90,
|
61
|
+
redTo: 100,
|
62
|
+
yellowFrom: 75,
|
63
|
+
yellowTo: 90,
|
64
|
+
minorTicks: 5
|
65
|
+
};
|
66
|
+
gauge.draw( gauge_data, gauge_options );
|
67
|
+
} );
|
68
|
+
$( function ( ) {
|
69
|
+
$('dt a').click( function ( ) {
|
70
|
+
$(this).parent( ).next('dd').slideDown( );
|
71
|
+
return false;
|
72
|
+
} );
|
73
|
+
$('a.ajax').click( function ( ) {
|
74
|
+
$a = $(this);
|
75
|
+
$.get( $a.attr('href'), function ( js ) {
|
76
|
+
alert( js.result || $a.text( ));
|
77
|
+
} );
|
78
|
+
return false;
|
79
|
+
} );
|
80
|
+
} );
|
81
|
+
</script>
|
data/bin/lightwaverf
CHANGED
@@ -23,6 +23,10 @@ case ARGV[0]
|
|
23
23
|
puts LightWaveRF.new.run_timers ARGV[1], ARGV[2]
|
24
24
|
when 'update'
|
25
25
|
puts LightWaveRF.new.update_config ARGV[1], ARGV[2]
|
26
|
+
when 'web'
|
27
|
+
puts LightWaveRF.new.build_web_page ARGV[1]
|
28
|
+
when 'summarise'
|
29
|
+
puts LightWaveRF.new.summarise ARGV[1], ARGV[2]
|
26
30
|
else
|
27
31
|
LightWaveRF.new.send ARGV[0], ARGV[1], ARGV[2], ARGV[3]
|
28
32
|
end
|
data/lib/lightwaverf.rb
CHANGED
@@ -23,6 +23,7 @@ class LightWaveRF
|
|
23
23
|
|
24
24
|
@config_file = nil
|
25
25
|
@log_file = nil
|
26
|
+
@summary_file = nil
|
26
27
|
@log_timer_file = nil
|
27
28
|
@config = nil
|
28
29
|
@timers = nil
|
@@ -108,6 +109,11 @@ class LightWaveRF
|
|
108
109
|
@log_file || File.expand_path('~') + '/lightwaverf.log'
|
109
110
|
end
|
110
111
|
|
112
|
+
# Summary file getter
|
113
|
+
def get_summary_file
|
114
|
+
@summary_file || File.expand_path('~') + '/lightwaverf-summary.json'
|
115
|
+
end
|
116
|
+
|
111
117
|
# Timer log file getter
|
112
118
|
def get_timer_log_file
|
113
119
|
@timer_log_file || File.expand_path('~') + '/lightwaverf-timer.log'
|
@@ -143,18 +149,12 @@ class LightWaveRF
|
|
143
149
|
|
144
150
|
# Get timer cache file, create it if needed
|
145
151
|
def get_timer_cache
|
146
|
-
p 'getting timer cache...'
|
147
152
|
if ! @timers
|
148
|
-
p 'no timers!'
|
149
153
|
if ! File.exists? self.get_timer_cache_file
|
150
|
-
|
151
|
-
self.update_timers
|
152
|
-
p 'updated...'
|
154
|
+
self.update_timers
|
153
155
|
end
|
154
156
|
@timers = YAML.load_file self.get_timer_cache_file
|
155
|
-
p 'timers now ' + @timers.to_s
|
156
157
|
end
|
157
|
-
p 'returning, timers now ' + @timers.to_s
|
158
158
|
@timers
|
159
159
|
end
|
160
160
|
|
@@ -733,24 +733,24 @@ class LightWaveRF
|
|
733
733
|
debug and ( p "Timezone: " + timezone)
|
734
734
|
|
735
735
|
# convert to datetimes
|
736
|
-
start_dt = DateTime.parse(start_date.strip + ' ' + start_time.strip + ' ' + timezone.strip)
|
737
|
-
end_dt = DateTime.parse(end_date.strip + ' ' + end_time.strip + ' ' + timezone.strip)
|
736
|
+
start_dt = DateTime.parse( start_date.strip + ' ' + start_time.strip + ' ' + timezone.strip )
|
737
|
+
end_dt = DateTime.parse( end_date.strip + ' ' + end_time.strip + ' ' + timezone.strip )
|
738
738
|
|
739
739
|
# apply time modifier if it exists
|
740
740
|
if time_modifier != 0
|
741
|
-
debug and ( p "Adjusting timings by: " + time_modifier.to_s)
|
742
|
-
start_dt = ((start_dt.to_time) + time_modifier*60).to_datetime
|
743
|
-
end_dt = ((end_dt.to_time) + time_modifier*60).to_datetime
|
741
|
+
debug and ( p "Adjusting timings by: " + time_modifier.to_s )
|
742
|
+
start_dt = (( start_dt.to_time ) + time_modifier * 60 ).to_datetime
|
743
|
+
end_dt = (( end_dt.to_time ) + time_modifier * 60 ).to_datetime
|
744
744
|
end
|
745
745
|
|
746
|
-
debug and ( p "Start datetime: " + start_dt.to_s)
|
747
|
-
debug and ( p "End datetime: " + end_dt.to_s)
|
746
|
+
debug and ( p "Start datetime: " + start_dt.to_s )
|
747
|
+
debug and ( p "End datetime: " + end_dt.to_s )
|
748
748
|
|
749
749
|
# populate the dates
|
750
750
|
event['date'] = start_dt
|
751
751
|
# handle device entries without explicit on/off state
|
752
752
|
if event['type'] == 'device' and ( event['state'].nil? or ( event['state'] != 'on' and event['state'] != 'off' ))
|
753
|
-
debug and ( p "Duplicating event without explicit on/off state...")
|
753
|
+
debug and ( p "Duplicating event without explicit on/off state..." )
|
754
754
|
# if not state was given, assume we meant 'on'
|
755
755
|
if event['state'].nil?
|
756
756
|
event['state'] = 'on'
|
@@ -946,5 +946,122 @@ class LightWaveRF
|
|
946
946
|
self.log_timer_event 'run', nil, nil, nil, true
|
947
947
|
end
|
948
948
|
|
949
|
+
def self.get_contents file
|
950
|
+
file = File.open file, 'r'
|
951
|
+
content = file.read
|
952
|
+
file.close
|
953
|
+
content
|
954
|
+
end
|
955
|
+
|
956
|
+
def build_web_page debug = nil
|
957
|
+
|
958
|
+
rooms = self.class.get_rooms self.get_config
|
959
|
+
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
|
+
rooms.each do | name, room |
|
962
|
+
debug and ( puts name + ' is ' + room.to_s )
|
963
|
+
list += '<dt>' + name + '</dt><dd><ul>'
|
964
|
+
room['device'].each do | device |
|
965
|
+
debug and ( puts 'device is ' + device.to_s )
|
966
|
+
list += '<li>' + room['name'].to_s + ' ' + device.first.to_s + '</li>'
|
967
|
+
end
|
968
|
+
list += '</ul></dd>'
|
969
|
+
end
|
970
|
+
list += '</dl>'
|
971
|
+
|
972
|
+
summary = self.class.get_contents self.get_summary_file
|
973
|
+
js = self.class.get_contents( File.dirname( __FILE__ ) + '/../app/views/_graphs.ejs' ).gsub( '<%- summary %>', summary )
|
974
|
+
date = Time.new.to_s
|
975
|
+
title = self.get_config.has_key?('title') ? self.get_config['title'] : ( 'Lightwaverf energy stats ' + date )
|
976
|
+
intro = <<-end
|
977
|
+
Sample page generated #{date} with <code>lightwaverf web</code>.
|
978
|
+
Check out <a href="https://github.com/pauly/lightwaverf">the new simplified repo</a> for details
|
979
|
+
or <a href="https://rubygems.org/gems/lightwaverf">gem install lightwaverf && lightwaverf web</a>...
|
980
|
+
<br />@todo make a decent, useful, simple, configurable web page...
|
981
|
+
end
|
982
|
+
help = list
|
983
|
+
html = <<-end
|
984
|
+
<html>
|
985
|
+
<head>
|
986
|
+
<title>#{title}</title>
|
987
|
+
<style type="text/css">
|
988
|
+
body { font-family: arial, verdana, sans-serif; }
|
989
|
+
div#energy_chart { width: 800px; height: 600px; }
|
990
|
+
div#gauge_div { width: 100px; height: 100px; }
|
991
|
+
</style>
|
992
|
+
</head>
|
993
|
+
<body>
|
994
|
+
<div class="container">
|
995
|
+
<div class="row">
|
996
|
+
<div class="col">
|
997
|
+
<h1>#{title}</h1>
|
998
|
+
<p class="intro">#{intro}</p>
|
999
|
+
<div id="energy_chart"></div>
|
1000
|
+
<h2>Rooms and devices</h2>
|
1001
|
+
<p>@todo make these links to control the devices...</p>
|
1002
|
+
<p class="help">#{help}</p>
|
1003
|
+
#{js}
|
1004
|
+
</div>
|
1005
|
+
<div class="col">
|
1006
|
+
<div class="col" id="gauge_div"></div>
|
1007
|
+
</div>
|
1008
|
+
</div>
|
1009
|
+
</div>
|
1010
|
+
<p>By <a href="http://www.clarkeology.com/blog/">Paul Clarke</a>, a work in progress.</p>
|
1011
|
+
</body>
|
1012
|
+
</html>
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
# summarise the log data for ease of use
|
1017
|
+
def summarise days = 7, debug = nil
|
1018
|
+
days = days.to_i
|
1019
|
+
data = [ ]
|
1020
|
+
daily = { }
|
1021
|
+
start_date = 0
|
1022
|
+
d = nil
|
1023
|
+
File.open( self.get_log_file, 'r' ).each_line do | line |
|
1024
|
+
line = JSON.parse( line )
|
1025
|
+
if line and line['timestamp']
|
1026
|
+
new_line = []
|
1027
|
+
d = line['timestamp'][2..3] + line['timestamp'][5..6] + line['timestamp'][8..9] # compact version of date
|
1028
|
+
ts = Time.parse( line['timestamp'] ).strftime '%s'
|
1029
|
+
ts = ts.to_i
|
1030
|
+
if start_date > 0
|
1031
|
+
ts = ts - start_date
|
1032
|
+
else
|
1033
|
+
start_date = ts
|
1034
|
+
end
|
1035
|
+
new_line << ts
|
1036
|
+
new_line << line['message']['usage'].to_i / 10
|
1037
|
+
if line['message']['annotation'] and line['message']['annotation']['title'] and line['message']['annotation']['text']
|
1038
|
+
new_line << line['message']['annotation']['title']
|
1039
|
+
new_line << line['message']['annotation']['text']
|
1040
|
+
end
|
1041
|
+
data << new_line
|
1042
|
+
if line['message']['today'] > daily[d]['today']
|
1043
|
+
daily[d] = line['message']
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
end
|
1047
|
+
debug and ( puts 'got ' + data.length.to_s + ' lines in the log' )
|
1048
|
+
data = data.last( 60 * 24 * days )
|
1049
|
+
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][0] != start_date
|
1051
|
+
data[0][0] += start_date
|
1052
|
+
end
|
1053
|
+
summary_file = self.get_summary_file
|
1054
|
+
File.open( summary_file, 'w' ) do |file|
|
1055
|
+
file.write data.to_s
|
1056
|
+
end
|
1057
|
+
# @todo fix the daily stats, every night it reverts to the minimum value...
|
1058
|
+
File.open( summary_file.gsub( 'summary', 'daily' ), 'w' ) do | file |
|
1059
|
+
file.write daily.to_s
|
1060
|
+
end
|
1061
|
+
File.open( summary_file.gsub( 'summary', 'daily.' + d ), 'w' ) do | file |
|
1062
|
+
file.write daily.select { |key| key == daily.keys.last }.to_s
|
1063
|
+
end
|
1064
|
+
end
|
1065
|
+
|
949
1066
|
end
|
950
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.5.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-
|
14
|
+
date: 2013-07-04 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: htmlentities
|
@@ -46,8 +46,8 @@ dependencies:
|
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
description: ! " Interact with lightwaverf wifi-link from code or the command line.\n
|
49
|
-
\ Control your lights, heating, sockets etc.\n Also
|
50
|
-
calendar
|
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"
|
51
51
|
email: pauly@clarkeology.com
|
52
52
|
executables:
|
53
53
|
- lightwaverf
|
@@ -56,6 +56,7 @@ extensions: []
|
|
56
56
|
extra_rdoc_files: []
|
57
57
|
files:
|
58
58
|
- lib/lightwaverf.rb
|
59
|
+
- app/views/_graphs.ejs
|
59
60
|
- bin/lightwaverf
|
60
61
|
- bin/lightwaverf-config-json
|
61
62
|
homepage: http://www.clarkeology.com/wiki/lightwaverf+ruby
|