Class: LightWaveRF

Inherits:
Object
  • Object
show all
Defined in:
lib/lightwaverf.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.get_contents(file) ⇒ Object



1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
# File 'lib/lightwaverf.rb', line 1175

def self.get_contents file
  begin
    file = File.open file, 'r'
    content = file.read
    file.close
  rescue
    STDERR.puts 'cannot open ' + file
  end
  content.to_s
end

.get_json(file) ⇒ Object



1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
# File 'lib/lightwaverf.rb', line 1186

def self.get_json file
  json = { }
  content = self.get_contents file
  begin
    json = JSON.parse content
  rescue
    STDERR.puts 'cannot parse ' + file.to_s
  end
  json
end

.get_rooms(config = { 'room' => [ ] }, debug = false) ⇒ Object

Get a cleaned up version of the rooms and devices from the config file



451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/lightwaverf.rb', line 451

def self.get_rooms config = { 'room' => [ ] }, debug = false
  rooms = { }
  r = 1
  config['room'].each do | room |
    room = room.first if room.is_a? Array
    rooms[room['name']] = { 'id' => 'R' + r.to_s, 'name' => room['name'], 'device' => { }, 'mood' => { }, 'learnmood' => { }}
    d = 1
    unless room['device'].nil?
      room['device'].each do | device |
        device = device.first if device.is_a? Array
        device = { 'name' => device } if device.is_a? String
        device['id'] = 'D' + d.to_s
        rooms[room['name']]['device'][device['name']] = device
        d += 1
      end
    end
    m = 1
    unless room['mood'].nil?
      room['mood'].each do | mood |
        rooms[room['name']]['mood'][mood] = 'FmP' + m.to_s
        rooms[room['name']]['learnmood'][mood] = 'FsP' + m.to_s
        m += 1
      end
    end
    r += 1
  end
  rooms
end

.get_state(state = 'on') ⇒ Object

Translate the “state” we pass in to one the wifi link understands

Example:

>> LightWaveRF.new.state 'on' # 'F1'
>> LightWaveRF.new.state 'off' # 'F0'

Arguments:

state: (String)


488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
# File 'lib/lightwaverf.rb', line 488

def self.get_state state = 'on'
  if /^\d+%?$/.match state.to_s
    state = state.to_i
  end
  case state
    when 'off'
      state = 'F0'
    when 0
      state = 'F0'
    when 'on'
      state = 'F1'
    when 'low'
      state = 'FdP8'
    when 'mid'
      state = 'FdP16'
    when 'high'
      state = 'FdP24'
    when 'full'
      state = 'FdP32'
    when 1..100
      state = 'FdP' + ( state * 0.32 ).round.to_s
    else
      if state
        p 'did not recognise state, got ' + state
      end
  end
  state
end

.to_seconds(interval = 0) ⇒ Object

Convert a string to seconds, assume it is in minutes



1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
# File 'lib/lightwaverf.rb', line 1045

def self.to_seconds interval = 0
  match = /^(\d+)([shd])$/.match( interval.to_s )
  if match
    case match[2]
    when 's'
      return match[1].to_i
    when 'h'
      return match[1].to_i * 3600
    when 'd'
      return match[1].to_i * 86400
    end
  end
  return interval.to_i * 60
end

.variance(title = '', debug = nil) ⇒ Object

Return the randomness value that may be in the event title



1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
# File 'lib/lightwaverf.rb', line 1033

def self.variance title = '', debug = nil
  randomness = /random\w* (\d+)/.match title
  if randomness
    n = randomness[1].to_i
    debug and ( p 'randomness is ' + n.to_s )
    return rand( n ) - ( n / 2 )
  end
  debug and ( p 'no randomness return nil' )
  return nil
end

Instance Method Details

#build_web_page(debug = nil) ⇒ Object



1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
# File 'lib/lightwaverf.rb', line 1197

def build_web_page debug = nil

  rooms = self.class.get_rooms self.get_config
  list = '<dl>'
  rooms.each do | name, room |
    debug and ( puts name + ' is ' + room.to_s )
    list += '<dt><a>' + name + '</a></dt><dd><ul>'
    room['device'].each do | device |
      # link ideally relative to avoid cross domain issues
      link = '/room/' + room['name'].to_s + '/' + device.first.to_s
      list += '<li><a class="ajax off" href="' + link + '">' + room['name'].to_s + ' ' + device.first.to_s + '</a></li>'
    end
    list += '</ul></dd>'
  end
  list += '</dl>'

  summary = self.class.get_contents self.get_summary_file
  js = self.class.get_contents( File.dirname( __FILE__ ) + '/../app/views/_graphs.ejs' ).gsub( '<%- summary %>', summary )
  date = Time.new.to_s
  title = self.get_config.has_key?('title') ? self.get_config['title'] : ( 'Lightwaverf energy stats ' + date )
  intro = <<-end
    Sample page generated #{date} with <code>lightwaverf web</code>.
    Check out <a href="https://github.com/pauly/lightwaverf">the new simplified repo</a> for details
    or <a href="https://rubygems.org/gems/lightwaverf">gem install lightwaverf && lightwaverf web</a>...
    <br />@todo merge this with <a href="https://github.com/pauly/robot-butler">robot butler</a>...
  end
  help = list
  html = <<-end
    <html>
      <head>
        <title>#{title}</title>
        <style type="text/css">
          body { font-family: arial, verdana, sans-serif; }
          div#energy_chart { width: 800px; height: 600px; }
          div#gauge_div { width: 100px; height: 100px; }
          dd { display: none; }
          .off, .on:hover { padding-right: 18px; background: url(lightning_delete.png) no-repeat top right; }
          .on, .off:hover { padding-right: 18px; background: url(lightning_add.png) no-repeat top right; }
        </style>
      </head>
      <body>
        <div class="container">
          <div class="row">
            <div class="col">
              <h1>#{title}</h1>
              <p class="intro">#{intro}</p>
              <div id="energy_chart">
                Not seeing an energy chart here?
                Maybe not working in your device yet, sorry.
                Try in a web browser.
              </div>
              <h2>Rooms and devices</h2>
              <p>@todo make these links to control the devices...</p>
              <p class="help">#{help}</p>
              #{js}
            </div>
            <div class="col">
              <div class="col" id="gauge_div"></div>
            </div>
          </div>
        </div>
        <p>By <a href="http://www.clarkeology.com/blog/">Paul Clarke</a>, a work in progress.</p>
      </body>
    </html>
  end
end

#calendar_body(debug = false) ⇒ Object



951
952
953
954
955
956
957
958
959
# File 'lib/lightwaverf.rb', line 951

def calendar_body debug = false
  url = self.get_calendar_url debug
  debug and ( p url )
  response = self.request url, debug
  if response.code != '200'
    debug and ( p "Response code is: " + response.code)
  end
  return response.body
end

#command(room, device, state) ⇒ Object

Get the command to send to the wifi link

Example:

>> LightWaveRF.new.command 'our', 'light', 'on'

Arguments:

room: (String)
device: (String)
state: (String)


526
527
528
529
530
531
532
533
534
535
# File 'lib/lightwaverf.rb', line 526

def command room, device, state
  # @todo get the device name in here...
  device = device.to_s
  # Command structure is <transaction number>,<Command>|<Action>|<State><cr>
  if room and device and !device.empty? and state
    '666,!' + room['id'] + room['device'][device]['id'] + state + '|Turn ' + room['name'] + ' ' + device + '|' + state + ' via @pauly'
  else
    '666,!' + room['id'] + state + '|Turn ' + room['name'] + '|' + state + ' via @pauly'
  end
end

#configure(debug = false) ⇒ Object

Configure, build config file. Interactive command line stuff

Arguments:

debug: (Boolean)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/lightwaverf.rb', line 70

def configure debug = false
  config = self.get_config
  puts 'What is the ip address of your wifi link? (currently "' + self.get_config['host'].to_s + '").'
  puts 'Enter a blank line to broadcast UDP commands (ok to just hit enter here).'
  host = STDIN.gets.chomp
  config['host'] = host if ! host.to_s.empty?
  puts 'What is the address of your calendar ics file? (currently "' + self.get_config['calendar'].to_s + '")'
  puts '(ok to just hit enter here)'
  calendar = STDIN.gets.chomp
  config['calendar'] = calendar if ! calendar.to_s.empty?

  puts 'Do you have an energy monitor? [Y/n]'
  puts '(ok to just hit enter here)'
  monitor = STDIN.gets.chomp.to_s
  if ! monitor.empty?
    puts 'got "' + monitor + '"' if debug
    config['monitor'] = true if monitor.byteslice( 0 ).downcase == 'y'
    puts 'made that into "' + config['monitor'].to_s + '"' if debug
  end

  puts 'Shall we create a web page on this server? (currently "' + self.get_config['web'].to_s + '"). Optional (ok to just hit enter here)'
  web = STDIN.gets.chomp.to_s
  puts 'got "' + web + '"' if debug
  config['web'] = web if ! web.empty?
  config['web'] = '/tmp/lightwaverf_web.html' if config['web'].to_s.empty?
  puts 'going with "' + config['web'].to_s + '"' if debug

  device = 'x'
  while ! device.to_s.empty?
    puts 'Enter the name of a room and its devices, space separated. For example "lounge light socket tv". Enter a blank line to finish.'
    puts 'If you want spaces in room or device name, wrap them in quotes. For example "\'living room' 'tv' 'table lamp\'"'
    puts 'If you already have rooms and devices set up on another lightwaverf app then hit enter here, and "lightwaverf update" first.'
    if device = STDIN.gets.chomp
      parts = device.split ' '
      if !parts.first.to_s.empty? and !parts[1].to_s.empty?
        new_room = parts.shift
        config['room'] ||= [ ]
        found = false
        config['room'].each do | room |
          if room['name'] == new_room
            parts.map! do | device |
              { 'name' => device, 'type' => 'O' }
            end
            room['device'] = parts
            found = true
          end
          debug and ( p 'so now room is ' + room.to_s )
        end
        if ! found
          config['room'].push 'name' => new_room, 'device' => parts, 'mood' => nil
        end
        debug and ( p 'added ' + parts.to_s + ' to ' + new_room.to_s )
      end
    end
  end
  debug and ( p 'end of configure, config is now ' + config.to_s )
  file = self.put_config config

  executable = `which lightwaverf`.chomp
  crontab = `crontab -l`.split( /\n/ ) || [ ]
  crontab = crontab.reject do | line |
    line =~ Regexp.new( Regexp.escape executable )
  end
  crontab << '# new crontab added by `' + executable + ' configure`'

  if config['monitor']
    crontab << '# ' + executable + ' energy monitor check ever minute + summarise every 5'
    crontab << '* * * * * ' + executable + ' energy > /tmp/lightwaverf_energy.out 2>&1'
    crontab << '*/5 * * * * ' + executable + ' summarise 7 > /tmp/lightwaverf_summarise.out 2>&1'
  end

  if config['web']
    crontab << '# ' + executable + ' web page generated every hour'
    crontab << '45 * * * * ' + executable + ' web > ' + config['web'] + ' 2> /tmp/lightwaverf_web.out'
  end

  if config['calendar']
    crontab << '# ' + executable + ' update schedule ONLY ONCE A DAY'
    crontab << '56 0 * * * ' + executable + ' schedule true > /tmp/lightwaverf_schedule.out 2>&1'
  end

  config['room'].each do | room |
    next unless room['device']
    room['device'].each do | device |
      next unless device['reboot']
      out_file = '/tmp/' + room['name'] + device['name'] + '.reboot.out'
      out_file.gsub! /\s/, ''
      crontab << '@reboot ' + executable + ' ' + room['name'] + ' ' + device['name'] + ' ' + device['reboot'] + ' > ' + out_file + ' 2>&1'
    end
  end
  File.open( '/tmp/cron.tab', 'w' ) do | handle |
    handle.write crontab.join( "\n" ) + "\n"
  end
  puts `crontab /tmp/cron.tab`
  'Saved config file ' + file
end

#cron_entry(event, executable, extra_debug = false) ⇒ Object



239
240
241
242
243
244
245
246
# File 'lib/lightwaverf.rb', line 239

def cron_entry event, executable, extra_debug = false
  id = 'lwrf_cron ' + event['rrule'].to_s + (extra_debug ? ' ' + event.inspect : '')
  event['state'] = 'on' if event['state'].nil?
  cmd = event['room'].to_s + ' ' + event['device'].to_s + ' ' + event['state'].to_s
  out_file = '/tmp/' + cmd + '.out'
  out_file.gsub! /\s/, '-'
  return self.cron_entry_times(event) + ' ' + executable + ' ' + cmd + ' > ' + out_file + ' 2>&1 # ' + id
end

#cron_entry_times(event) ⇒ Object



248
249
250
251
252
253
254
# File 'lib/lightwaverf.rb', line 248

def cron_entry_times event
  return event['date'].strftime('%M %H * * *') if event['rrule'] =~ /\AFREQ=DAILY/
  match = /BYDAY=([\w,]+)/.match(event['rrule'])
  return event['date'].strftime('%M %H * * ') + self.rrule_days_of_week(match[1]) if match
  return event['date'].strftime('%M %H %d %m *') if event['date']
  return '# 0 12 * * *';
end

#energy(title = nil, text = nil, debug = false) ⇒ Object



723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
# File 'lib/lightwaverf.rb', line 723

def energy title = nil, text = nil, debug = false
  debug and text and ( p 'energy: ' + text )
  data = self.raw nil, true, debug
  debug and ( p data )
  match = false
  # {"trans":17903,"mac":"03:0F:DA","time":1452531946,"prod":"pwrMtr","serial":"9EB3FE","router":"4F0500","type":"energy","cUse":1163,"todUse":4680,"yesUse":0}
  begin
    data = JSON.parse data[2, data.length]
    debug and ( p data.inspect )
  rescue
    STDERR.puts 'cannot parse ' + data.to_s
    data = nil
  end
  debug and ( p data )
  if data
    data = {
      'message' => {
        'usage' => data['cUse'],
        # 'max' => 'unused now',
        'today' => data['todUse']
      }
    }
    data['timestamp'] = Time.now.to_s
    if text
      data['message']['annotation'] = { 'title' => title.to_s, 'text' => text.to_s }
    end

    if text
      if self.get_config['spreadsheet']
        spreadsheet = self.get_config['spreadsheet']['url']
        match = /key=([\w-]+)/.match spreadsheet
        debug and ( p match )
        if match
          spreadsheet = match[1]
        end
        debug and ( p 'spreadsheet is ' + spreadsheet )
        if spreadsheet
          require 'google_drive'
          session = GoogleDrive. self.get_config['spreadsheet']['username'], self.get_config['spreadsheet']['password']
          ws = session.spreadsheet_by_key( spreadsheet ).worksheets[0]
          rows = ws.num_rows
          debug and ( p rows.to_s + ' rows in ' + spreadsheet )
          row = rows + 1
          ws[ row, 1 ] = data['timestamp']
          ws[ row, 2 ] = data['message']['usage']
          ws[ row, 3 ] = data['message']['max']
          ws[ row, 4 ] = data['message']['today']
          ws[ row, 5 ] = data['message']['annotation']['title']
          ws[ row, 6 ] = data['message']['annotation']['text']
          ws.save( )
        end
      else
        debug and ( p 'no spreadsheet in your config file...' )
      end

    end
    debug and ( p data )
    begin
      File.open( self.get_log_file, 'a' ) do | f |
        f.write( data.to_json + "\n" )
      end
      file = self.get_summary_file.gsub 'summary', 'daily'
      data['message']['history'] = self.class.get_json file
      data['message']
    rescue
      puts 'error writing to log'
    end
  end
end

#firmware(debug = true) ⇒ Object



1328
1329
1330
# File 'lib/lightwaverf.rb', line 1328

def firmware debug = true
  self.raw '666,!F*p', true, debug
end

#get_calendar_url(debug = false) ⇒ Object



835
836
837
838
839
840
841
842
843
# File 'lib/lightwaverf.rb', line 835

def get_calendar_url debug = false
  url = self.get_config['calendar']
  if ! /\.ics/.match url
    STDERR.puts 'we need ical .ics format now, so using default ' + url + ' for dev'
    STDERR.puts 'This contains my test events, not yours! Add your ical url to your config file'
    url = 'https://www.google.com/calendar/ical/aar79qh62fej54nprq6334s7ck%40group.calendar.google.com/public/basic.ics'
  end
  url
end

#get_configObject

Get the config file, create it if it does not exist



329
330
331
332
333
334
335
336
337
338
# File 'lib/lightwaverf.rb', line 329

def get_config
  if ! @config
    if ! File.exists? self.get_config_file
      puts self.get_config_file + ' does not exist - copy lightwaverf-configy.yml from https://github.com/pauly/lightwaverf to your home directory or type "lightwaverf configure"'
      self.put_config
    end
    @config = YAML.load_file self.get_config_file
  end
  @config
end

#get_config_fileObject



260
261
262
# File 'lib/lightwaverf.rb', line 260

def get_config_file
  @config_file || File.expand_path('~') + '/lightwaverf-config.yml'
end

#get_log_fileObject



264
265
266
# File 'lib/lightwaverf.rb', line 264

def get_log_file
  @log_file || File.expand_path('~') + '/lightwaverf.log'
end

#get_modifiers(event, debug = false) ⇒ Object



905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
# File 'lib/lightwaverf.rb', line 905

def get_modifiers event, debug = false
  event['time_modifier'] = 0
  if event['command'].length > event['modifier_start']
    event['when_modifiers'] = [ ]
    event['unless_modifiers'] = [ ]
    for i in event['modifier_start']..(event['command'].length-1)
      modifier = event['command'][i]
      if modifier[0,1] == '@'
        debug and ( p 'Found when modifier: ' + modifier[1..-1] )
        event['when_modifiers'].push modifier[1..-1]
      elsif modifier[0,1] == '!'
        debug and ( p 'Found unless modifier: ' + modifier[1..-1] )
        event['unless_modifiers'].push modifier[1..-1]
      elsif modifier[0,1] == '+'
        event['time_modifier'] = modifier[1..-1].to_i
      elsif modifier[0,1] == '-'
        event['time_modifier'] = modifier[1..-1].to_i * -1
      end
    end
  end
  event['time_modifier'] += self.class.variance( event['summary'] ).to_i
  if event['time_modifier'] != 0
    debug and (p 'Adjusting timings by: ' + event['time_modifier'].to_s + ' ' + event.inspect)
    event['date'] = (( event['date'].to_time ) + event['time_modifier'] * 60 ).to_datetime
    if event['end']
      event['end'] = (( event['end'].to_time ) + event['time_modifier'] * 60 ).to_datetime
    end
    debug and (p 'dates now ' + event['date'].to_s + ' ' + event['end'].to_s)
  end
  event
end

#get_rooms_from(body = '', debug = nil) ⇒ Object



389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
# File 'lib/lightwaverf.rb', line 389

def get_rooms_from body = '', debug = nil
  variables = self.get_variables_from body, debug
  rooms = [ ]
  # Rooms - gRoomNames is a collection of 8 values, or room names
  debug and ( puts variables['gRoomStatus'].inspect )
  variables['gRoomNames'].each_with_index do | roomName, roomIndex |
    # Room Status - gRoomStatus is a collection of 8 values indicating the status of the corresponding room in gRoomNames
    #   A: Active
    #   I: Inactive
    if variables['gRoomStatus'] and variables['gRoomStatus'][roomIndex] and variables['gRoomStatus'][roomIndex][0] == 'A'
      debug and ( puts variables['gRoomStatus'][roomIndex].inspect )
      # Devices - gDeviceNames is a collection of 80 values, structured in blocks of ten values for each room:
      #   Devices 1 - 6, Mood 1 - 3, All Off
      roomDevices = [ ]
      deviceNamesIndexStart = roomIndex * 10
      variables['gDeviceNames'][(deviceNamesIndexStart)..(deviceNamesIndexStart+5)].each_with_index do | deviceName, deviceIndex |
        # Device Status - gDeviceStatus is a collection of 80 values which indicate the status/type of the corresponding device in gDeviceNames
        #   O: On/Off Switch
        #   D: Dimmer
        #   R: Radiator(s)
        #   P: Open/Close
        #   I: Inactive (i.e. not configured)
        #   m: Mood (inactive)
        #   M: Mood (active)
        #   o: All Off
        deviceStatusIndex = roomIndex * 10 + deviceIndex
        if variables['gDeviceStatus'] and variables['gDeviceStatus'][deviceStatusIndex] and variables['gDeviceStatus'][deviceStatusIndex][0] != 'I'
          roomDevices << { 'name' => deviceName, 'type' => variables['gDeviceStatus'][deviceStatusIndex][0] }
        end
      end
      # Create a hash of the active room and active devices and add to rooms array
      if roomName and roomDevices and roomDevices.any?
        rooms << { 'name' => roomName, 'device' => roomDevices }
      end
    end
  end
  rooms
end

#get_summary_fileObject



268
269
270
# File 'lib/lightwaverf.rb', line 268

def get_summary_file
  @summary_file || File.expand_path('~') + '/lightwaverf-summary.json'
end

#get_timer_cacheObject

Get timer cache file, create it if needed



303
304
305
306
307
308
309
310
311
# File 'lib/lightwaverf.rb', line 303

def get_timer_cache
  if ! @timers
    if ! File.exists? self.get_timer_cache_file
      self.update_timers
    end
    @timers = YAML.load_file self.get_timer_cache_file
  end
  @timers
end

#get_timer_cache_fileObject

Timer cache file getter



298
299
300
# File 'lib/lightwaverf.rb', line 298

def get_timer_cache_file
  @log_file || File.expand_path('~') + '/lightwaverf-timer-cache.yml'
end

#get_timer_log_fileObject



272
273
274
# File 'lib/lightwaverf.rb', line 272

def get_timer_log_file
  @timer_log_file || File.expand_path('~') + '/lightwaverf-timer.log'
end

#get_variables_from(body = '', debug = nil) ⇒ Object

Get variables from the source of lightwaverfhost.co.uk Separated out so it can be tested



430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
# File 'lib/lightwaverf.rb', line 430

def get_variables_from body = '', debug = nil
  # debug and ( p '[Info - LightWaveRF Gem] body was ' + body.to_s )
  variables = { }
  # Extract JavaScript variables from the page
  #   var gDeviceNames = [""]
  #   var gDeviceStatus = [""]
  #   var gRoomNames = [""]
  #   var gRoomStatus = [""]
  # http://rubular.com/r/UH0H4b4afF
  body.scan( /var (gDeviceNames|gDeviceStatus|gRoomNames|gRoomStatus)\s*=\s*([^;]*)/ ).each do | variable |
    debug and ( p variable.to_s )
    if variable[0]
      variables[variable[0]] = variable[1].scan( /"([^"]*)\"/ ).map! do | v | v.pop end
      debug and ( p 'variables[' + variable[0] + '] = ' + variables[variable[0]].to_s )
    end
  end
  debug and ( p '[Info - LightWaveRF Gem] so variables are ' + variables.inspect )
  variables
end

#helpObject

Display help



57
58
59
60
61
62
63
64
# File 'lib/lightwaverf.rb', line 57

def help
  help = self.usage + "\n"
  help += "your rooms, and devices, as defined in " + self.get_config_file + ":\n\n"
  help += YAML.dump self.get_config['room']
  room = self.get_config['room'].first['name'].to_s
  device = self.get_config['room'].first['device'].first['name'].to_s
  help += "\n\nso to turn on " + room + " " + device + " type 'lightwaverf " + self.quote( room ) + " " + self.quote( device ) + " on'\n"
end

#learnmood(room = nil, mood = nil, debug = false) ⇒ Object

Learn a mood in one of your rooms

Example:

>> LightWaveRF.new.learnmood 'living', 'movie'

Arguments:

room: (String)
mood: (String)


711
712
713
714
715
716
717
718
719
720
721
# File 'lib/lightwaverf.rb', line 711

def learnmood room = nil, mood = nil, debug = false
  debug and ( p 'Learning mood: ' + mood )
  rooms = self.class.get_rooms self.get_config
  if rooms[room] and mood and rooms[room]['learnmood'][mood]
    command = self.command rooms[room], nil, rooms[room]['learnmood'][mood]
    debug and ( p 'command is ' + command )
    self.raw command
  else
    STDERR.puts self.usage( room )
  end
end

#log_timer_event(type, room = nil, device = nil, state = nil, result = false) ⇒ Object



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/lightwaverf.rb', line 276

def log_timer_event type, room = nil, device = nil, state = nil, result = false
  message = nil
  case type
  when 'update'
    message = '### Updated timer cache'
  when 'run'
    # message = '*** Ran timers'
  when 'sequence'
    message = 'Ran sequence: ' + state
  when 'mood'
    message = 'Set mood: ' + mood + ' in room ' + room
  when 'device'
    message = 'Set device: ' + device + ' in room ' + room + ' to state ' + state
  end
  unless message.nil?
    File.open( self.get_timer_log_file, 'a' ) do | f |
      f.write( "\n" + Time.now.to_s + ' - ' + message + ' - ' + ( result ? 'SUCCESS!' : 'FAILED!' ))
    end
  end
end

#mood(room = nil, mood = nil, debug = false) ⇒ Object

Set a mood in one of your rooms

Example:

>> LightWaveRF.new.mood 'living', 'movie'

Arguments:

room: (String)
mood: (String)


663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
# File 'lib/lightwaverf.rb', line 663

def mood room = nil, mood = nil, debug = false
  success = false
  debug and ( p 'Executing mood: ' + mood + ' in room: ' + room )
  rooms = self.class.get_rooms self.get_config
  # support for setting a mood in all rooms (recursive)
  if room == 'all'
    debug and ( p 'Processing all rooms...' )
    rooms.each do | config, each_room |
      room = each_room['name']
      debug and ( p 'Room is: ' + room )
      success = self.mood room, mood, debug
      sleep 1
    end
    success = true
  # process single mood
  else
    if rooms[room] and mood
      if rooms[room]['mood'][mood]
        command = self.command rooms[room], nil, rooms[room]['mood'][mood]
        debug and ( p 'command is ' + command )
        self.raw command
        success = true
      # support for special "moods" via device looping
      elsif mood[0,3] == 'all'
        state = mood[3..-1]
        debug and (p 'Selected state is: ' + state)
        rooms[room]['device'].each do | device |
          p 'Processing device: ' + device[0].to_s
          self.send room, device[0]['name'], state, debug
          sleep 1
        end
        success = true
      end
    else
      STDERR.puts self.usage( room );
    end
  end
  success
end

#put_config(config = { 'room' => [ { 'name' => 'default-room', 'device' => [ 'light' => { 'name' => 'default-device' } ] } ] }) ⇒ Object

Write the config file



321
322
323
324
325
326
# File 'lib/lightwaverf.rb', line 321

def put_config config = { 'room' => [ { 'name' => 'default-room', 'device' => [ 'light' => { 'name' => 'default-device' } ] } ] }
  File.open( self.get_config_file, 'w' ) do | handle |
    handle.write YAML.dump( config )
  end
  self.get_config_file
end

#put_timer_cache(timers = { 'events' => [ ] }) ⇒ Object

Store the timer cache



314
315
316
317
318
# File 'lib/lightwaverf.rb', line 314

def put_timer_cache timers = { 'events' => [ ] }
  File.open( self.get_timer_cache_file, 'w' ) do | handle |
    handle.write YAML.dump( timers )
  end
end

#quote(name = '') ⇒ Object



30
31
32
33
# File 'lib/lightwaverf.rb', line 30

def quote name = ''
  name = '"' + name + '"' if name.include? ' '
  name
end

#raw(command, listen = false, debug = false) ⇒ Object



793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
# File 'lib/lightwaverf.rb', line 793

def raw command, listen = false, debug = false
  debug and ( p self.time + ' ' + __method__.to_s + ' ' + command.to_s )
  response = nil
  # Get host address or broadcast address
  host = self.get_config['host'] || '255.255.255.255'
  debug and ( p self.time 'got ' + host )
  # Create socket
  listener = UDPSocket.new
  debug and ( p self.time 'got listener' )
  # Add broadcast socket options if necessary
  if host == '255.255.255.255'
    listener.setsockopt Socket::SOL_SOCKET, Socket::SO_BROADCAST, true
  end
  if listener
    if listen
      # Bind socket to listen for response
      begin
        listener.bind '0.0.0.0', 9761
      rescue StandardError => e
        response = "can't bind to listen for a reply; " + e.to_s
      end
    end
    # Broadcast command to server
    if command
      debug and ( p self.time 'sending...' )
      listener.send command, 0, host, 9760
      debug and ( p self.time 'sent' )
    end
    # Receive response
    if listen and ! response
      debug and ( p self.time 'receiving...' )
      response, addr = listener.recvfrom 200
      debug and ( p self.time 'received' )
    end
    debug and ( p self.time 'closing...' )
    listener.close
    debug and ( p self.time 'closed' )
  end
  debug and ( puts '[Info - LightWaveRF] ' + __method__.to_s + ': response is ' + response.to_s )
  response
end

#request(url, debug = false) ⇒ Object



845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
# File 'lib/lightwaverf.rb', line 845

def request url, debug = false
  parsed_url = URI.parse url
  http = Net::HTTP.new parsed_url.host, parsed_url.port
  begin
    http.use_ssl = true
  rescue
    debug and ( p 'cannot use ssl, tried ' + parsed_url.host + ', ' + parsed_url.port.to_s )
    url.gsub! 'https:', 'http:'
    debug and ( p 'so fetching ' + url )
    parsed_url = URI.parse url
    http = Net::HTTP.new parsed_url.host
  end
  request = Net::HTTP::Get.new parsed_url.request_uri
  response = http.request request
end

#rrule_days_of_week(days) ⇒ Object



256
257
258
# File 'lib/lightwaverf.rb', line 256

def rrule_days_of_week days
  return days.gsub('SU', '0').gsub('MO', '1').gsub('TU', '2').gsub('WE', '3').gsub('TH', '4').gsub('FR', '5').gsub('SA', '6')
end

#run_timers(interval = 5, debug = false) ⇒ Object



1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
# File 'lib/lightwaverf.rb', line 1060

def run_timers interval = 5, debug = false
  p '-- Running timers...'
  get_timer_cache
  debug and ( p 'Timer list is: ' + YAML.dump( @timers ))

  # get the current time and end interval time
  now = Time.new
  start_tm = now - now.sec
  end_tm = start_tm + self.class.to_seconds( interval )

  # convert to datetimes
  start_horizon = DateTime.parse start_tm.to_s
  end_horizon = DateTime.parse end_tm.to_s
  p '-- Start horizon is: ' + start_horizon.to_s
  p 'End horizon is: ' + end_horizon.to_s

  # sort the events and states (to guarantee order if longer intervals are used)
  @timers['events'].sort! { | x, y | x['date'] <=> y['date'] }
  @timers['states'].sort! { | x, y | x['date'] <=> y['date'] }

  # array to hold events that should be executed this run
  run_list = [ ]

  # process each event
  @timers['events'].each do | event |
    debug and ( p '-- Processing event: ' + event.to_s )
    debug and ( p 'Event time is: ' + event['date'].to_s )

    # first, assume we'll not be running the event
    run_now = false

    # check that it is in the horizon time
    if event['date'] >= start_horizon and event['date'] < end_horizon
      debug and ( p 'Event is in horizon...')
      run_now = true

      # if has modifiers, check modifiers against states
      unless event['when_modifiers'].nil?
        debug and ( p 'Event has when modifiers. Checking they are all met...' )

        # determine which states apply at the time of the event
        applicable_states = [ ]
        @timers['states'].each do | state |
          if event['date'] >= state['start'] and event['date'] < state['end']
            applicable_states.push state['name']
          end
        end
        debug and ( p 'Applicable states are: ' + applicable_states.to_s )

        # check that each when modifier exists in appliable states
        event['when_modifiers'].each do | modifier |
          unless applicable_states.include? modifier
            debug and ( p 'Event when modifier not met: ' + modifier )
            run_now = false
            break
          end
        end

        # check that each unless modifier does not exist in appliable states
        event['unless_modifiers'].each do | modifier |
          if applicable_states.include? modifier
            debug and ( p 'Event unless modifier not met: ' + modifier )
            run_now = false
            break
          end
        end
      end

      # if we have determined the event should run, add to the run list
      if run_now
        run_list.push event
      end
    end
  end

  # process the run list
  p '-- Events to execute this run are: ' + run_list.to_s

  triggered = [ ]

  annotate = false
  run_list.each do | event |
    # execute based on type
    case event['type']
    when 'mood'
      p 'Executing mood. Room: ' + event['room'] + ', Mood: ' + event['state']
      result = self.mood event['room'], event['state'], debug
    when 'sequence'
      p 'Executing sequence. Sequence: ' + event['state']
      result = self.sequence event['state'], debug
    else
      p 'send ' + event['room'].to_s + ' ' + event['device'].to_s + ' ' + event['state'].to_s + ' ' + debug.to_s
      result = self.send event['room'], event['device'], event['state'], debug
    end
    sleep 1
    triggered << [ event['room'], event['device'].to_s, event['state'] ]
    if event['annotate']
      annotate = true
    end
    self.log_timer_event event['type'], event['room'], event['device'].to_s, event['state'], result
  end

  # update energy log
  title = nil
  text = nil
  if annotate
    debug and ( p triggered.length.to_s + ' events so annotating energy log too...' )
    title = 'timer'
    text = triggered.map { | e | e.join ' ' }.join ', '
  end
  self.energy title, text, debug

  self.log_timer_event 'run', nil, nil, nil, true
end

#schedule(debug = false) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/lightwaverf.rb', line 167

def schedule debug = false
  id = 'lwrf_cron'
  executable = `which lightwaverf`.chomp
  if (executable == "")
    puts 'did not get executable from `which lightwaverf` - do we have ' + File.expand_path(__FILE__) + '../bin/lightwaverf ???'
    # executable = File.expand_path(__FILE__) + '/../bin/lightwaverf'
    executable = '/usr/local/bin/lightwaverf'
  end
  if (!executable)
    puts 'still no, bah, aborting'
    return
  end
  crontab = `crontab -l`.split( /\n/ ) || [ ]
  crontab = crontab.reject do | line |
    line =~ Regexp.new( id )
  end
  crontab << '# ' + id + ' new crontab added by `' + executable + ' cron`'

  body = self.calendar_body(debug)

  cals = RiCal.parse_string(body)

  cals.first.events.each do | e |
    event = self.tokenise_event e, debug
    event = self.get_modifiers event, debug
    event.delete 'command'
    event.delete 'modifier_start'
    event.delete 'time_modifier'

      endDate = nil

      match = /UNTIL=(\d+)/.match(event['rrule'].to_s)
      if match
        endDate = DateTime.parse(match[1].to_s)
      end
   
      match = /FREQ=(\w+);COUNT=(\d+)/.match(event['rrule'])
      # FREQ=DAILY;COUNT=8 - need to check for weekly, monthly etc
      if match
        endDate = event['date'] + match[2].to_i
      end

      if !event['rrule']
        endDate = event['date']
      end

      if endDate
        if endDate < Date.today
          next
        end
      end

      if event['type'] == 'device' and event['state'] != 'on' and event['state'] != 'off'
        event['room'] = 'sequence' if event['room'].nil?
        crontab << self.cron_entry(event, executable)
        end_event = event.dup # duplicate event for start and end
        end_event['date'] = event['end']
        end_event['state'] = 'off'
        crontab << self.cron_entry(end_event, executable)
      else
        event['room'] = 'sequence' if event['room'].nil?
        crontab << self.cron_entry(event, executable, true)
      end
   

  end
  File.open( '/tmp/cron.tab', 'w' ) do | handle |
    handle.write crontab.join( "\n" ) + "\n"
  end
  puts `crontab /tmp/cron.tab`
end

#send(room = nil, device = nil, state = 'on', debug = false) ⇒ Object

Turn one of your devices on or off or all devices in a room off

Example:

>> LightWaveRF.new.send 'our', 'light', 'on'
>> LightWaveRF.new.send 'our', '', 'off'

This method was too confusing, got rid of “alloff” it can be done with “[room] all off” anyway

Arguments:

room: (String)
device: (String)
state: (String)


563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
# File 'lib/lightwaverf.rb', line 563

def send room = nil, device = nil, state = 'on', debug = false
  debug and ( p self.time 'send' )
  success = false
  debug and ( p 'Executing send on device: ' + device + ' in room: ' + room + ' with ' + ( state ? 'state ' + state : 'no state' ))


  # starting to optionally move some functionality out of here
  alternativeScript = self.get_config['pywaverf']
  if alternativeScript and File.exist?( alternativeScript )
    cmd = "#{alternativeScript} \"#{room}\" \"#{device}\" \"#{state}\" \"#{debug}\""
    debug and ( p cmd )
    p `#{cmd}`
    debug and ( p self.time 'done python' )
    return
  end


  rooms = self.class.get_rooms self.get_config, debug
  debug and ( p self.time 'got rooms' )

  unless rooms[room] and state
    debug and ( p 'Missing room (' + room.to_s + ') or state (' + state.to_s + ')' );
    STDERR.puts self.usage( room );
  else
    # support for setting state for all devices in the room (recursive)
    if device == 'all'
      debug and ( p 'Processing all devices...' )
      rooms[room]['device'].each do | device_name, code |
        debug and ( p "Device is: " + device_name )
        self.send room, device_name, state, debug
        sleep 1
      end
      success = true
    # process single device
    elsif device and rooms[room]['device'][device]
      state = self.class.get_state state
      command = self.command rooms[room], device, state
      debug and ( p self.time 'command is ' + command )
      data = self.raw command
      debug and ( p self.time 'response is ' + data.to_s )
      success = true
      data = self.update_state room, device, state, debug
    else
      STDERR.puts self.usage( room );
    end
  end
  success
end

#sequence(name, debug = false) ⇒ Object

A sequence of events maybe I really mean a “mood” here?

Example:

>> LightWaveRF.new.sequence 'lights'

Arguments:

name: (String)
debug: (Boolean)


635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
# File 'lib/lightwaverf.rb', line 635

def sequence name, debug = false
  success = true
  if self.get_config['sequence'][name]
    self.get_config['sequence'][name].each do | task |
      if task[0] == 'pause'
        debug and ( p 'Pausing for ' + task[1].to_s + ' seconds...' )
        sleep task[1].to_i
        debug and ( p 'Resuming...' )
      elsif task[0] == 'mood'
        self.mood task[1], task[2], debug
      else
        self.send task[0], task[1], task[2].to_s, debug
      end
      sleep 1
    end
    success = true
  end
  success
end

#set_event_type(event, debug = false) ⇒ Object



861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
# File 'lib/lightwaverf.rb', line 861

def set_event_type event, debug = false
  if event['command'].first[0,1] == '#'
    event['type'] = 'state' # temporary type, will be overridden later
    event['room'] = nil
    event['device'] = nil
    event['state'] = event['command'].first[1..-1].to_s
    event['modifier_start'] = event['command'].length # can't have modifiers on states
  else
    case event['command'].first.to_s
    when 'mood'
      event['type'] = 'mood'
      event['room'] = event['command'][1].to_s
      event['device'] = nil
      event['state'] = event['command'][2].to_s
      event['modifier_start'] = 3
    when 'sequence'
      event['type'] = 'sequence'
      event['room'] = nil
      event['device'] = nil
      event['state'] = event['command'][1].to_s
      event['modifier_start'] = 2
    else
      event['type'] = 'device'
      event['room'] = event['command'].first.to_s
      event['device'] = event['command'][1].to_s
      # handle optional state
      if event['command'].length > 2
        first_char = event['command'][2].to_s[0,1]
        # if the third word does not start with a modifier flag, assume it's a state
        if /\w/.match first_char
          event['state'] = event['command'][2].to_s
          event['modifier_start'] = 3
        else
          event['modifier_start'] = 2
        end
      else
        event['state'] = nil
        event['modifier_start'] = 2
      end
    end
  end
  event
end

#summarise(days = 7, debug = nil) ⇒ Object

summarise the log data for ease of use



1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
# File 'lib/lightwaverf.rb', line 1265

def summarise days = 7, debug = nil
  days = days.to_i
  data = [ ]
  file = self.get_summary_file.gsub 'summary', 'daily'
  daily = self.class.get_json file
  start_date = 0
  d = nil
  last = 0
  prev = 0
  cut_off_date = ( DateTime.now - days ).to_s
  File.open( self.get_log_file, 'r' ).each_line do | line |
    begin
      line = JSON.parse line
    rescue
      line = nil
    end
    if line and line['timestamp'] and ( last != line['message']['usage'] )
      next if ( cut_off_date > line['timestamp'] )
      new_line = []
      d = line['timestamp'][2..3] + line['timestamp'][5..6] + line['timestamp'][8..9] # compact version of date
      ts = Time.parse( line['timestamp'] ).strftime '%s'
      ts = ts.to_i
      ts = ts - start_date
      if start_date == 0
        # start_date = ts # can't get this delta working
      end
      new_line << ts
      smoothedUsage = line['message']['usage'].to_i
      if last != 0 and prev != 0
        smoothedUsage = ( smoothedUsage + last + prev ) / 3 # average of last 3 readings
      end
      new_line << smoothedUsage
      if line['message']['annotation'] and line['message']['annotation']['title'] and line['message']['annotation']['text']
        new_line << line['message']['annotation']['title']
        new_line << line['message']['annotation']['text']
      end
      data << new_line
      if (( ! daily[d] ) or ( line['message']['today'].to_i > daily[d]['today'].to_i ))
        daily[d] = line['message']
        daily[d].delete 'usage'
      end
      prev = last
      last = line['message']['usage'].to_i
    end
  end
  if data and data.first
    if data.first.first != start_date
      data.first[0] += start_date
    end
  end
  summary_file = self.get_summary_file
  File.open( summary_file, 'w' ) do |file|
    file.write( JSON.pretty_generate( data ))
  end
  File.open( summary_file.gsub( 'summary', 'daily' ), 'w' ) do | file |
    file.write daily.to_json.to_s
  end
  File.open( summary_file.gsub( 'summary', 'daily.' + d ), 'w' ) do | file |
    file.write daily.select { |key| key == daily.keys.last }.to_json.to_s
  end
end

#time(label = nil) ⇒ Object

For debug timing, why is this so slow?



51
52
53
54
# File 'lib/lightwaverf.rb', line 51

def time label = nil
  @time = @time || Time.now
  label.to_s + ' (' + ( Time.now - @time ).to_s + ')'
end

#timezone(debug = false) ⇒ Object

Set the Time Zone on the LightWaveRF WiFi Link

Example:

>> LightWaveRF.new.timezone

Arguments:

debug: (Boolean)


544
545
546
547
548
# File 'lib/lightwaverf.rb', line 544

def timezone debug = false
  command = '666,!FzP' + ( Time.now.gmt_offset/60/60 ).to_s
  data = self.raw command, true, debug
  return data == "666,OK\r\n"
end

#tokenise_event(e, debug = false) ⇒ Object



937
938
939
940
941
942
943
944
945
946
947
948
949
# File 'lib/lightwaverf.rb', line 937

def tokenise_event e, debug = false
  event = { }
  event['summary'] = e.summary
  event['command'] = event['summary'].split
  event['annotate'] = !( /do not annotate/.match event['summary'] )
  event['date'] = e.dtstart
  event['end'] = e.dtend
  if e.rrule.length > 0
    event['rrule'] = e.rrule.first
    # event['rrules'] = event['rrule'].split(';')
  end
  event = set_event_type event, debug
end

#update_config(email = nil, pin = nil, debug = false) ⇒ Object

Update the LightWaveRF Gem config file from the LightWaveRF Host server

Example:

>> LightWaveRF.new.update_config [email protected], 1234

Arguments:

email: (String)
pin: (String)
debug: (Boolean)

Credits:

wonko - http://lightwaverfcommunity.org.uk/forums/topic/querying-configuration-information-from-the-lightwaverf-website/


352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/lightwaverf.rb', line 352

def update_config email = nil, pin = nil, debug = false

  if ! email && ! pin
    STDERR.puts 'missing email and / or pin'
    STDERR.puts 'usage: lightwaverf update [email protected] 1111'
    return
  end

  # Login to LightWaveRF Host server
  uri = URI.parse 'https://lightwaverfhost.co.uk/manager/index.php'
  http = Net::HTTP.new uri.host, uri.port
  http.use_ssl = true if uri.scheme == 'https'

  # Thanks Fitz http://lightwaverfcommunity.org.uk/forums/topic/pauly-lightwaverf-command-line-not-working/
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE

  data = 'pin=' + pin + '&email=' + email
  headers = { 'Content-Type'=> 'application/x-www-form-urlencoded' }
  resp, data = http.post uri.request_uri, data, headers

  if resp and resp.body
    rooms = self.get_rooms_from resp.body, debug
    # Update 'room' element in LightWaveRF Gem config file
    if rooms.any?
      config = self.get_config
      config['room'] = rooms
      self.put_config config
      debug and ( p '[Info - LightWaveRF Gem] Updated config with ' + rooms.size.to_s + ' room(s): ' + rooms.to_s )
    else
      debug and ( p '[Info - LightWaveRF Gem] Unable to update config: No active rooms or devices found' )
    end
  else
    debug and ( p '[Info - LightWaveRF Gem] Unable to update config: No response from Host server' )
  end
  self.get_config
end

#update_state(room, device, state, debug) ⇒ Object



612
613
614
615
616
617
618
619
620
621
622
623
624
# File 'lib/lightwaverf.rb', line 612

def update_state room, device, state, debug
  update = false
  config = self.get_config
  config['room'].each do | r |
    next unless r['name'] == room
    r['device'].each do | d |
      next unless d['name'] == device
      update = d['state'] != state
      d['state'] = state
    end
  end
  self.put_config config if update
end

#update_timers(past = 60, future = 1440, debug = false) ⇒ Object



961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
# File 'lib/lightwaverf.rb', line 961

def update_timers past = 60, future = 1440, debug = false
  p '-- Updating timers...'

  query_start = Time.new - self.class.to_seconds( past )
  query_end = Time.new + self.class.to_seconds( future )

  body = self.calendar_body(debug)

  cals = RiCal.parse_string(body)

  timers = { 'events' => [ ], 'states' => [ ] }

  cals.first.events.each do | e |
    begin
      occurs = e.occurrences(:overlapping => [query_start, query_end])
    rescue StandardError => err
      p err.to_s
      p e.to_s
      occurs = []
    end
    next if occurs.length == 0
    occurs.each do | occurrence |

      event = self.tokenise_event occurrence, debug
      debug and ( p event.inspect )

      event = self.get_modifiers event, debug
      event.delete 'command'
      event.delete 'modifier_start'
      event.delete 'time_modifier'

      # handle device entries without explicit on/off state
      # has a PROBLEM with a calendar event set to turn lights to 50% say - automatically adds an off!
      # fix this with something like
      #   if self.get_state event['state'] ! starts with F

      if event['type'] == 'device' and event['state'] != 'on' and event['state'] != 'off'
        debug and ( p 'Duplicating ' + event['summary'] + ' with ' + ( event['state'] ? 'state ' + event['state'] : 'no state' ))
        event['state'] = 'on' if event['state'].nil?
        end_event = event.dup # duplicate event for start and end
        end_event['date'] = event['end']
        end_event['state'] = 'off'
        timers['events'].push event
        timers['events'].push end_event
      elsif event['type'] == 'state'
        debug and ( p 'Create state ' + event['state'] + ' plus start and end events' )
        state = { }
        state['name'] = event['state']
        state['start'] = event['start'].dup
        state['end'] = event['end'].dup
        timers['states'].push state
        event['type'] = 'sequence'
        event['state'] = state['name'] + '_start'
        end_event = event.dup # duplicate event for start and end
        end_event['date'] = event['end']
        end_event['state'] = state['name'] + '_end'
        timers['events'].push event
        timers['events'].push end_event
      else
        timers['events'].push event
      end

    end

  end

  put_timer_cache timers
  self.log_timer_event 'update', nil, nil, nil, true

end

#usage(room = nil) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/lightwaverf.rb', line 35

def usage room = nil
  rooms = self.class.get_rooms self.get_config
  roomName = self.quote( rooms.values.first['name'].to_s )
  config = 'usage: lightwaverf ' + roomName + ' ' + rooms.values.first['device'].keys.first.to_s + ' on'
  config += ' # where ' + roomName + ' is a room in ' + self.get_config_file.to_s
  if room and rooms[room]
    roomName = self.quote( rooms[room]['name'].to_s )
    config += "\ntry: lightwaverf " + roomName + ' all on'
    rooms[room]['device'].each do | device |
      config += "\ntry: lightwaverf " + roomName + ' ' + device.first.to_s + ' on'
    end
  end
  config
end