lightwaverf 0.7 → 0.8.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 +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: []
|