lightwaverf 0.4.0 → 0.5.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.
@@ -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
- p 'no file!'
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.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-06-22 00:00:00.000000000 Z
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 set up timers using a google
50
- calendar and log energy usage.\n"
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