lightwaverf 0.7 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
- <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
2
- <script src="//www.google.com/jsapi"></script>
1
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
2
+ <script src="https://www.google.com/jsapi"></script>
3
3
  <script>
4
4
  var gauge, gauge_data, gauge_options;
5
5
  google.load( 'visualization', '1.0', { packages: [ 'corechart', 'gauge', 'annotatedtimeline' ] } );
@@ -3,4 +3,3 @@ require 'lightwaverf'
3
3
  config = LightWaveRF.new.get_config
4
4
  config['spreadsheet']['password'] = nil
5
5
  puts JSON.generate config
6
- require 'json'
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
- # Build / update cron job automatically
8
7
 
9
8
  require 'yaml'
10
9
  require 'socket'
@@ -12,9 +11,9 @@ require 'net/http'
12
11
  require 'uri'
13
12
  require 'net/https'
14
13
  require 'json'
15
- require 'rexml/document'
16
14
  require 'time'
17
15
  require 'date'
16
+ require 'ri_cal'
18
17
  include Socket::Constants
19
18
 
20
19
  class LightWaveRF
@@ -27,7 +26,6 @@ class LightWaveRF
27
26
  @timers = nil
28
27
  @time = nil
29
28
 
30
- # Display usage info
31
29
  def usage room = nil
32
30
  rooms = self.class.get_rooms self.get_config
33
31
  config = 'usage: lightwaverf ' + rooms.values.first['name'].to_s + ' ' + rooms.values.first['device'].keys.first.to_s + ' on'
@@ -50,10 +48,10 @@ class LightWaveRF
50
48
  # Display help
51
49
  def help
52
50
  help = self.usage + "\n"
53
- help += "your rooms, devices, and sequences, as defined in " + self.get_config_file + ":\n\n"
51
+ help += "your rooms, and devices, as defined in " + self.get_config_file + ":\n\n"
54
52
  help += YAML.dump self.get_config['room']
55
- room = self.get_config['room'].last['name'].to_s
56
- device = self.get_config['room'].last['device'].last.to_s
53
+ room = self.get_config['room'].first['name'].to_s
54
+ device = self.get_config['room'].first['device'].first['name'].to_s
57
55
  help += "\n\nso to turn on " + room + " " + device + " type \"lightwaverf " + room + " " + device + " on\"\n"
58
56
  end
59
57
 
@@ -66,12 +64,31 @@ class LightWaveRF
66
64
  puts 'What is the ip address of your wifi link? (currently "' + self.get_config['host'].to_s + '"). Enter a blank line to broadcast UDP commands (ok to just hit enter here).'
67
65
  host = STDIN.gets.chomp
68
66
  config['host'] = host if ! host.to_s.empty?
69
- puts 'What is the address of your google calendar? (currently "' + self.get_config['calendar'].to_s + '"). Optional (ok to just hit enter here).'
67
+ puts 'What is the address of your calendar ics file? (currently "' + self.get_config['calendar'].to_s + '")'
68
+ puts '(ok to just hit enter here)'
70
69
  calendar = STDIN.gets.chomp
71
70
  config['calendar'] = calendar if ! calendar.to_s.empty?
71
+
72
+ puts 'Do you have an energy monitor? [Y/n]'
73
+ puts '(ok to just hit enter here)'
74
+ monitor = STDIN.gets.chomp.to_s
75
+ if ! monitor.empty?
76
+ puts 'got "' + monitor + '"' if debug
77
+ config['monitor'] = true if monitor.byteslice( 0 ).downcase == 'y'
78
+ puts 'made that into "' + config['monitor'].to_s + '"' if debug
79
+ end
80
+
81
+ puts 'Shall we create a web page on this server? (currently "' + self.get_config['web'].to_s + '"). Optional (ok to just hit enter here)'
82
+ web = STDIN.gets.chomp.to_s
83
+ puts 'got "' + web + '"' if debug
84
+ config['web'] = web if ! web.empty?
85
+ config['web'] = '/tmp/lightwaverf_web.html' if config['web'].to_s.empty?
86
+ puts 'going with "' + config['web'].to_s + '"' if debug
87
+
72
88
  device = 'x'
73
89
  while ! device.to_s.empty?
74
90
  puts 'Enter the name of a room and its devices, space separated. For example "lounge light socket tv". Enter a blank line to finish.'
91
+ puts 'If you already have rooms and devices set up on another lightwaverf app then hit enter here, and "lightwaverf update" first.'
75
92
  if device = STDIN.gets.chomp
76
93
  parts = device.split ' '
77
94
  if !parts[0].to_s.empty? and !parts[1].to_s.empty?
@@ -97,35 +114,69 @@ class LightWaveRF
97
114
  end
98
115
  debug and ( p 'end of configure, config is now ' + config.to_s )
99
116
  file = self.put_config config
117
+
118
+ executable = `which lightwaverf`.chomp
119
+ crontab = `crontab -l`.split( /\n/ ) || [ ]
120
+ crontab = crontab.reject do | line |
121
+ line =~ Regexp.new( Regexp.escape executable )
122
+ end
123
+ crontab << '# new crontab added by `' + executable + ' configure`'
124
+
125
+ if config['monitor']
126
+ crontab << '# ' + executable + ' energy monitor check ever 2 mins + summarise every 5'
127
+ crontab << '*/2 * * * * ' + executable + ' energy > /tmp/lightwaverf_energy.out 2>&1'
128
+ crontab << '*/5 * * * * ' + executable + ' summarise 7 > /tmp/lightwaverf_summarise.out 2>&1'
129
+ end
130
+
131
+ if config['web']
132
+ crontab << '# ' + executable + ' web page generated every hour'
133
+ crontab << '45 * * * * ' + executable + ' web > ' + config['web'] + ' 2> /tmp/lightwaverf_web.out'
134
+ end
135
+
136
+ if config['calendar']
137
+ crontab << '# ' + executable + ' cache timed events 1 hour back 4 hours ahead'
138
+ crontab << '58 * * * * ' + executable + ' update_timers 60 240 > /tmp/lightwaverf_update_timers.out 2>&1'
139
+ crontab << '# ' + executable + ' update_timers on reboot (works for me on raspbian)'
140
+ crontab << '@reboot ' + executable + ' update_timers 60 240 > /tmp/lightwaverf_update_timers.out 2>&1'
141
+ crontab << '# ' + executable + ' timer every 10 mins off peak'
142
+ crontab << '*/10 0-6,9-16,23 * * * ' + executable + ' timer 10 > /tmp/lightwaverf_timer.out 2>&1'
143
+ crontab << '# ' + executable + ' timer every 2 minutes peak'
144
+ crontab << '*/2 7,8,17-22 * * * ' + executable + ' timer 2 > /tmp/lightwaverf_timer.out 2>&1'
145
+ end
146
+
147
+ config['room'].each do | room |
148
+ next unless room['device']
149
+ room['device'].each do | device |
150
+ next unless device['reboot']
151
+ out_file = '/tmp/' + room['name'] + device['name'] + '.reboot.out'
152
+ out_file.gsub! /\s/, ''
153
+ crontab << '@reboot ' + executable + ' ' + room['name'] + ' ' + device['name'] + ' ' + device['reboot'] + ' > ' + out_file + ' 2>&1'
154
+ end
155
+ end
156
+
100
157
  'Saved config file ' + file
101
158
  end
102
159
 
103
- # Config file setter
104
160
  def set_config_file file
105
161
  @config_file = file
106
162
  end
107
163
 
108
- # Config file getter
109
164
  def get_config_file
110
165
  @config_file || File.expand_path('~') + '/lightwaverf-config.yml'
111
166
  end
112
167
 
113
- # Log file getter
114
168
  def get_log_file
115
169
  @log_file || File.expand_path('~') + '/lightwaverf.log'
116
170
  end
117
171
 
118
- # Summary file getter
119
172
  def get_summary_file
120
173
  @summary_file || File.expand_path('~') + '/lightwaverf-summary.json'
121
174
  end
122
175
 
123
- # Timer log file getter
124
176
  def get_timer_log_file
125
177
  @timer_log_file || File.expand_path('~') + '/lightwaverf-timer.log'
126
178
  end
127
179
 
128
- # Timer logger
129
180
  def log_timer_event type, room = nil, device = nil, state = nil, result = false
130
181
  # create log message
131
182
  message = nil
@@ -172,7 +223,7 @@ class LightWaveRF
172
223
  end
173
224
 
174
225
  # Write the config file
175
- def put_config config = { 'room' => [ { 'name' => 'our', 'device' => [ 'light' => { 'name' => 'light' }, 'lights' => { 'name' => 'lights' } ] } ] }
226
+ def put_config config = { 'room' => [ { 'name' => 'default-room', 'device' => [ 'light' => { 'name' => 'default-device' } ] } ] }
176
227
  File.open( self.get_config_file, 'w' ) do | handle |
177
228
  handle.write YAML.dump( config )
178
229
  end
@@ -187,16 +238,6 @@ class LightWaveRF
187
238
  self.put_config
188
239
  end
189
240
  @config = YAML.load_file self.get_config_file
190
- # fix where update made names and devices into arrays
191
- # if @config['room']
192
- # @config['room'].map! do | room |
193
- # room['name'] = room['name'].kind_of?( Array ) ? room['name'][0] : room['name']
194
- # room['device'].map! do | device |
195
- # device = device.kind_of?( Array ) ? device[0] : device
196
- # end
197
- # room
198
- # end
199
- # end
200
241
  end
201
242
  @config
202
243
  end
@@ -215,8 +256,14 @@ class LightWaveRF
215
256
  # wonko - http://lightwaverfcommunity.org.uk/forums/topic/querying-configuration-information-from-the-lightwaverf-website/
216
257
  def update_config email = nil, pin = nil, debug = false
217
258
 
259
+ if ! email && ! pin
260
+ STDERR.puts 'missing email and / or pin'
261
+ STDERR.puts 'usage: lightwaverf update email@email.com 1111'
262
+ return
263
+ end
264
+
218
265
  # Login to LightWaveRF Host server
219
- uri = URI.parse 'https://lightwaverfhost.co.uk/manager/index.php'
266
+ uri = URI.parse 'https://www.lightwaverfhost.co.uk/manager/index.php'
220
267
  http = Net::HTTP.new uri.host, uri.port
221
268
  http.use_ssl = true if uri.scheme == 'https'
222
269
  data = 'pin=' + pin + '&email=' + email
@@ -651,34 +698,16 @@ class LightWaveRF
651
698
  response
652
699
  end
653
700
 
654
- def update_timers past = 60, future = 1440, debug = false
655
- p '----------------'
656
- p 'Updating timers...'
657
-
658
- # determine the window to query
659
- now = Time.new
660
- query_start = now - self.class.to_seconds( past )
661
- query_end = now + self.class.to_seconds( future )
662
-
663
- # url = LightWaveRF.new.get_config['calendar']
701
+ def get_calendar_url debug = false
664
702
  url = self.get_config['calendar']
665
-
666
- ctz = 'UTC'
667
- case Time.new.zone
668
- when 'BST'
669
- ctz = 'Europe/London'
670
- else
671
- p 'time zone is ' + Time.new.zone + ' so look out...'
672
- end
673
- url += '?ctz=' + ctz
674
- if ctz != 'UTC'
675
- p 'using time zone is ' + ctz + ' so look out...'
703
+ if ! /\.ics/.match url
704
+ STDERR.puts 'we need ical .ics format now, so using default ' + url + ' for dev'
705
+ STDERR.puts 'This contains my test events, not yours! Add your ical url to your config file'
706
+ url = 'https://www.google.com/calendar/ical/aar79qh62fej54nprq6334s7ck%40group.calendar.google.com/public/basic.ics'
676
707
  end
708
+ end
677
709
 
678
- url += '&singleevents=true'
679
- url += '&start-min=' + query_start.strftime( '%FT%T%:z' ).sub( '+', '%2B' )
680
- url += '&start-max=' + query_end.strftime( '%FT%T%:z' ).sub( '+', '%2B' )
681
- debug and ( p url )
710
+ def request url
682
711
  parsed_url = URI.parse url
683
712
  http = Net::HTTP.new parsed_url.host, parsed_url.port
684
713
  begin
@@ -692,212 +721,165 @@ class LightWaveRF
692
721
  end
693
722
  request = Net::HTTP::Get.new parsed_url.request_uri
694
723
  response = http.request request
724
+ end
695
725
 
696
- # if we get a good response
697
- debug and ( p "Response code is: " + response.code)
698
- if response.code == '200'
699
- debug and ( p "Retrieved calendar ok")
700
- doc = REXML::Document.new response.body
701
- now = Time.now.strftime '%H:%M'
702
-
703
- events = [ ]
704
- states = [ ]
705
-
706
- # refresh the list of entries for the caching period
707
- doc.elements.each 'feed/entry' do | e |
708
- debug and ( p '-------------------' )
709
- debug and ( p 'Processing entry...' )
710
- event = Hash.new
711
-
712
- # tokenise the title
713
- debug and ( p 'Event title is: ' + e.elements['title'].text )
714
- command = e.elements['title'].text.split
715
- command_length = command.length
716
- debug and ( p 'Number of words is: ' + command_length.to_s )
717
- if command and command.length >= 1
718
- first_word = command[0].to_s
719
- # determine the type of the entry
720
- if first_word[0,1] == '#'
721
- debug and ( p 'Type is: state' )
722
- event['type'] = 'state' # temporary type, will be overridden later
723
- event['room'] = nil
724
- event['device'] = nil
725
- event['state'] = first_word[1..-1].to_s
726
- modifier_start = command_length # can't have modifiers on states
726
+ def set_event_type event, debug = false
727
+ if event['command'].first[0,1] == '#'
728
+ event['type'] = 'state' # temporary type, will be overridden later
729
+ event['room'] = nil
730
+ event['device'] = nil
731
+ event['state'] = event['command'].first[1..-1].to_s
732
+ event['modifier_start'] = event['command'].length # can't have modifiers on states
733
+ else
734
+ case event['command'].first.to_s
735
+ when 'mood'
736
+ event['type'] = 'mood'
737
+ event['room'] = event['command'][1].to_s
738
+ event['device'] = nil
739
+ event['state'] = event['command'][2].to_s
740
+ event['modifier_start'] = 3
741
+ when 'sequence'
742
+ event['type'] = 'sequence'
743
+ event['room'] = nil
744
+ event['device'] = nil
745
+ event['state'] = event['command'][1].to_s
746
+ event['modifier_start'] = 2
747
+ else
748
+ event['type'] = 'device'
749
+ event['room'] = event['command'].first.to_s
750
+ event['device'] = event['command'][1].to_s
751
+ # handle optional state
752
+ if event['command'].length > 2
753
+ first_char = event['command'][2].to_s[0,1]
754
+ debug and ( p 'First char is: ' + first_char )
755
+ # if the third word does not start with a modifier flag, assume it's a state
756
+ if /\w/.match first_char
757
+ event['state'] = event['command'][2].to_s
758
+ event['modifier_start'] = 3
727
759
  else
728
- case first_word
729
- when 'mood'
730
- debug and ( p 'Type is: mood' )
731
- event['type'] = 'mood'
732
- event['room'] = command[1].to_s
733
- event['device'] = nil
734
- event['state'] = command[2].to_s
735
- modifier_start = 3
736
- when 'sequence'
737
- debug and ( p 'Type is: sequence' )
738
- event['type'] = 'sequence'
739
- event['room'] = nil
740
- event['device'] = nil
741
- event['state'] = command[1].to_s
742
- modifier_start = 2
743
- else
744
- debug and ( p 'Type is: device' )
745
- event['type'] = 'device'
746
- event['room'] = command[0].to_s
747
- event['device'] = command[1].to_s
748
- # handle optional state
749
- if command_length > 2
750
- third_word = command[2].to_s
751
- first_char = third_word[0,1]
752
- debug and ( p 'First char is: ' + first_char )
753
- # if the third word does not start with a modifier flag, assume it's a state
754
- # if first_char != '@' and first_char != '!' and first_char != '+' and first_char != '-'
755
- if /\w/.match first_char
756
- debug and ( p 'State has been given.')
757
- event['state'] = command[2].to_s
758
- modifier_start = 3
759
- else
760
- debug and ( p 'State has not been given.' )
761
- modifier_start = 2
762
- end
763
- else
764
- debug and ( p 'State has not been given.' )
765
- event['state'] = nil
766
- modifier_start = 2
767
- end
768
- end
769
- end
770
-
771
- # get modifiers if they exist
772
- time_modifier = 0
773
- if command_length > modifier_start
774
- debug and ( p 'May have modifiers...' )
775
- when_modifiers = [ ]
776
- unless_modifiers = [ ]
777
- modifier_count = command_length - modifier_start
778
- debug and ( p 'Count of modifiers is ' + modifier_count.to_s )
779
- for i in modifier_start..(command_length-1)
780
- modifier = command[i]
781
- if modifier[0,1] == '@'
782
- debug and ( p 'Found when modifier: ' + modifier[1..-1] )
783
- when_modifiers.push modifier[1..-1]
784
- elsif modifier[0,1] == '!'
785
- debug and ( p 'Found unless modifier: ' + modifier[1..-1] )
786
- unless_modifiers.push modifier[1..-1]
787
- elsif modifier[0,1] == '+'
788
- debug and ( p 'Found positive time modifier: ' + modifier[1..-1] )
789
- time_modifier = modifier[1..-1].to_i
790
- elsif modifier[0,1] == '-'
791
- debug and ( p 'Found negative time modifier: ' + modifier[1..-1] )
792
- time_modifier = modifier[1..-1].to_i * -1
793
- end
794
- end
795
- # add when/unless modifiers to the event
796
- event['when_modifiers'] = when_modifiers
797
- event['unless_modifiers'] = unless_modifiers
760
+ event['modifier_start'] = 2
798
761
  end
762
+ else
763
+ event['state'] = nil
764
+ event['modifier_start'] = 2
765
+ end
766
+ end
767
+ end
768
+ event
769
+ end
799
770
 
800
- # parse the date string
801
- event_time = /When: ([\w ]+) (\d\d:\d\d) to ([\w ]+)?(\d\d:\d\d)&nbsp;\n(.*)<br>(.*)/.match e.elements['summary'].text
802
- debug and ( p 'Event times are: ' + event_time.to_s )
803
- start_date = event_time[1].to_s
804
- start_time = event_time[2].to_s
805
- end_date = event_time[3].to_s
806
- end_time = event_time[4].to_s
807
- timezone = event_time[5].to_s
808
- if end_date == '' or end_date.nil? # copy start date to end date if it wasn't given (as the same date)
809
- end_date = start_date
810
- end
811
-
812
- time_modifier += self.class.variance( e.elements['title'].text ).to_i
813
- event['annotate'] = ! ( /do not annotate/.match e.elements['title'].text )
814
-
815
- debug and ( p 'Start date: ' + start_date )
816
- debug and ( p 'Start time: ' + start_time )
817
- debug and ( p 'End date: ' + end_date )
818
- debug and ( p 'End time: ' + end_time )
819
- debug and ( p 'Timezone: ' + timezone )
820
-
821
- # convert to datetimes
822
- start_dt = DateTime.parse( start_date.strip + ' ' + start_time.strip + ' ' + timezone.strip )
823
- end_dt = DateTime.parse( end_date.strip + ' ' + end_time.strip + ' ' + timezone.strip )
824
-
825
- # apply time modifier if it exists
826
- if time_modifier != 0
827
- debug and ( p 'Adjusting timings by: ' + time_modifier.to_s )
828
- start_dt = (( start_dt.to_time ) + time_modifier * 60 ).to_datetime
829
- end_dt = (( end_dt.to_time ) + time_modifier * 60 ).to_datetime
830
- end
771
+ def get_modifiers event, debug = false
772
+ event['time_modifier'] = 0
773
+ if event['command'].length > event['modifier_start']
774
+ event['when_modifiers'] = [ ]
775
+ event['unless_modifiers'] = [ ]
776
+ for i in event['modifier_start']..(event['command'].length-1)
777
+ modifier = event['command'][i]
778
+ if modifier[0,1] == '@'
779
+ debug and ( p 'Found when modifier: ' + modifier[1..-1] )
780
+ event['when_modifiers'].push modifier[1..-1]
781
+ elsif modifier[0,1] == '!'
782
+ debug and ( p 'Found unless modifier: ' + modifier[1..-1] )
783
+ event['unless_modifiers'].push modifier[1..-1]
784
+ elsif modifier[0,1] == '+'
785
+ debug and ( p 'Found positive time modifier: ' + modifier[1..-1] )
786
+ event['time_modifier'] = modifier[1..-1].to_i
787
+ elsif modifier[0,1] == '-'
788
+ debug and ( p 'Found negative time modifier: ' + modifier[1..-1] )
789
+ event['time_modifier'] = modifier[1..-1].to_i * -1
790
+ end
791
+ end
792
+ end
793
+ event['time_modifier'] += self.class.variance( event['summary'] ).to_i
794
+ if event['time_modifier'] != 0
795
+ debug and ( p 'Adjusting timings by: ' + event['time_modifier'].to_s )
796
+ event['date'] = (( event['date'].to_time ) + event['time_modifier'] * 60 ).to_datetime
797
+ event['end'] = (( event['end'].to_time ) + event['time_modifier'] * 60 ).to_datetime
798
+ end
799
+ event
800
+ end
831
801
 
832
- debug and ( p 'Start datetime: ' + start_dt.to_s )
833
- debug and ( p 'End datetime: ' + end_dt.to_s )
802
+ def tokenise_event e, debug = false
803
+ event = { }
804
+ event['summary'] = e.summary
805
+ event['command'] = event['summary'].split
806
+ event['annotate'] = !( /do not annotate/.match event['summary'] )
807
+ event['date'] = e.dtstart
808
+ event['end'] = e.dtend
809
+ event = set_event_type event, debug
810
+ end
834
811
 
835
- # populate the dates
836
- event['date'] = start_dt
837
- # handle device entries without explicit on/off state
812
+ def update_timers past = 60, future = 1440, debug = false
813
+ p '-- Updating timers...'
838
814
 
839
- # has a PROBLEM with a calendar event set to turn lights to 50% say - automatically adds an off!
840
- # fix this with something like
841
- # if self.get_state event['state'] ! starts with F
815
+ query_start = Time.new - self.class.to_seconds( past )
816
+ query_end = Time.new + self.class.to_seconds( future )
842
817
 
843
- if event['type'] == 'device' and ( event['state'].nil? or ( event['state'] != 'on' and event['state'] != 'off' ))
844
- debug and ( p 'Duplicating event without explicit on/off state...' )
845
- # if not state was given, assume we meant 'on'
846
- if event['state'].nil?
847
- event['state'] = 'on'
848
- end
849
- end_event = event.dup # duplicate event for start and end
850
- end_event['date'] = end_dt
851
- end_event['state'] = 'off'
852
- events.push event
853
- events.push end_event
854
- # create state plus start and end events if a state
855
- elsif event['type'] == 'state'
856
- debug and ( p 'Processing state : ' + event['state'] )
857
- # create state
858
- state = Hash.new
859
- state['name'] = event['state']
860
- state['start'] = start_dt.dup
861
- state['end'] = end_dt.dup
862
- states.push state
863
- # convert event to start and end sequence
864
- event['type'] = 'sequence'
865
- event['state'] = state['name'] + '_start'
866
- end_event = event.dup # duplicate event for start and end
867
- end_event['date'] = end_dt
868
- end_event['state'] = state['name'] + '_end'
869
- events.push event
870
- events.push end_event
871
- # else just add the event
872
- else
873
- events.push event
874
- end
818
+ url = self.get_calendar_url debug
819
+ debug and ( p url )
820
+ response = self.request url
821
+ if response.code != '200'
822
+ debug and ( p "Response code is: " + response.code)
823
+ return self.log_timer_event 'update', nil, nil, nil, false
824
+ end
875
825
 
826
+ cals = RiCal.parse_string( response.body )
827
+
828
+ timers = { }
829
+ timers['events'] = [ ]
830
+ timers['states'] = [ ]
831
+
832
+ cals.first.events.each do | e |
833
+ occurs = e.occurrences( :overlapping => [ query_start, query_end ] )
834
+ next if occurs.length == 0
835
+ occurs.each do | occurrence |
836
+
837
+ event = self.tokenise_event occurrence, debug
838
+ debug and ( p event.inspect )
839
+
840
+ event = self.get_modifiers event, debug
841
+ event.delete 'command'
842
+ event.delete 'modifier_start'
843
+ event.delete 'time_modifier'
844
+
845
+ # handle device entries without explicit on/off state
846
+ # has a PROBLEM with a calendar event set to turn lights to 50% say - automatically adds an off!
847
+ # fix this with something like
848
+ # if self.get_state event['state'] ! starts with F
849
+
850
+ if event['type'] == 'device' and event['state'] != 'on' and event['state'] != 'off'
851
+ debug and ( p 'Duplicating ' + event['summary'] + ' with state ' + event['state'].to_s )
852
+ event['state'] = 'on' if event['state'].nil?
853
+ end_event = event.dup # duplicate event for start and end
854
+ end_event['date'] = event['end']
855
+ end_event['state'] = 'off'
856
+ timers['events'].push event
857
+ timers['events'].push end_event
858
+ elsif event['type'] == 'state'
859
+ debug and ( p 'Create state ' + event['state'] + ' plus start and end events' )
860
+ state = { }
861
+ state['name'] = event['state']
862
+ state['start'] = event['start'].dup
863
+ state['end'] = event['end'].dup
864
+ timers['states'].push state
865
+ event['type'] = 'sequence'
866
+ event['state'] = state['name'] + '_start'
867
+ end_event = event.dup # duplicate event for start and end
868
+ end_event['date'] = event['end']
869
+ end_event['state'] = state['name'] + '_end'
870
+ timers['events'].push event
871
+ timers['events'].push end_event
872
+ else
873
+ timers['events'].push event
876
874
  end
877
-
875
+
878
876
  end
879
877
 
880
- # record some timestamps
881
- info = { }
882
- info['updated_at'] = Time.new.strftime( '%FT%T%:z' )
883
- info['start_time'] = query_start.strftime( '%FT%T%:z' )
884
- info['end_time'] = query_end.strftime( '%FT%T%:z' )
885
-
886
- # build final timer config
887
- timers = { }
888
- timers['info'] = info
889
- timers['events'] = events
890
- timers['states'] = states
891
-
892
- p 'Timer list is: ' + YAML.dump( timers )
878
+ end
893
879
 
894
- # store the list
895
- put_timer_cache timers
896
- self.log_timer_event 'update', nil, nil, nil, true
880
+ put_timer_cache timers
881
+ self.log_timer_event 'update', nil, nil, nil, true
897
882
 
898
- else
899
- self.log_timer_event 'update', nil, nil, nil, false
900
- end
901
883
  end
902
884
 
903
885
  # Return the randomness value that may be in the event title
@@ -1187,9 +1169,9 @@ class LightWaveRF
1187
1169
  debug and ( puts 'got ' + data.length.to_s + ' lines in the log' )
1188
1170
  data = data.last 60 * 24 * days
1189
1171
  debug and ( puts 'now got ' + data.length.to_s + ' lines in the log ( 60 * 24 * ' + days.to_s + ' = ' + ( 60 * 24 * days ).to_s + ' )' )
1190
- if data and data[0]
1191
- debug and ( puts 'data[0] is ' + data[0].to_s )
1192
- if data[0][0] != start_date
1172
+ if data and data.first
1173
+ debug and ( puts 'data.first is ' + data.first.to_s )
1174
+ if data.first[0] != start_date
1193
1175
  data[0][0] += start_date
1194
1176
  end
1195
1177
  end
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.7'
4
+ version: 0.8.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: 2014-10-24 00:00:00.000000000 Z
14
+ date: 2015-10-04 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: htmlentities
@@ -46,9 +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, sprinkler etc.\n Also use a google
50
- calendar, for timers, log energy usage, and build a website.\n And now optionally
51
- logging to a google doc too.\n"
49
+ \ Control your lights, heating, sockets, sprinkler etc.\n Also use ical calendar,
50
+ for timers, log energy usage, and build a website.\n"
52
51
  email: pauly@clarkeology.com
53
52
  executables:
54
53
  - lightwaverf
@@ -60,7 +59,7 @@ files:
60
59
  - app/views/_graphs.ejs
61
60
  - bin/lightwaverf
62
61
  - bin/lightwaverf-config-json
63
- homepage: http://www.clarkeology.com/wiki/lightwaverf+ruby
62
+ homepage: http://www.clarkeology.com/project/lightwaverf
64
63
  licenses: []
65
64
  post_install_message:
66
65
  rdoc_options: []