lightwaverf 0.7 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/app/views/_graphs.ejs +2 -2
- data/bin/lightwaverf-config-json +0 -1
- data/lib/lightwaverf.rb +227 -245
- metadata +5 -6
data/app/views/_graphs.ejs
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
<script src="
|
2
|
-
<script src="
|
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' ] } );
|
data/bin/lightwaverf-config-json
CHANGED
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,
|
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'].
|
56
|
-
device = self.get_config['room'].
|
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
|
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' => '
|
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
|
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
|
-
|
667
|
-
|
668
|
-
|
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
|
-
|
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
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
event =
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
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
|
-
|
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
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
debug and ( p '
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
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
|
-
|
833
|
-
|
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
|
-
|
836
|
-
|
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
|
-
|
840
|
-
|
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
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
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
|
-
|
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
|
-
|
895
|
-
|
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
|
1191
|
-
debug and ( puts 'data
|
1192
|
-
if data[0]
|
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:
|
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:
|
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
|
50
|
-
|
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/
|
62
|
+
homepage: http://www.clarkeology.com/project/lightwaverf
|
64
63
|
licenses: []
|
65
64
|
post_install_message:
|
66
65
|
rdoc_options: []
|