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



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

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



1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
# File 'lib/lightwaverf.rb', line 1059

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



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
# File 'lib/lightwaverf.rb', line 354

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)


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
# File 'lib/lightwaverf.rb', line 391

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



911
912
913
914
915
916
917
918
919
920
921
922
923
924
# File 'lib/lightwaverf.rb', line 911

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



899
900
901
902
903
904
905
906
907
908
# File 'lib/lightwaverf.rb', line 899

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



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
# File 'lib/lightwaverf.rb', line 1070

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 = "    Sample page generated \#{date} with <code>lightwaverf web</code>.\n    Check out <a href=\"https://github.com/pauly/lightwaverf\">the new simplified repo</a> for details\n    or <a href=\"https://rubygems.org/gems/lightwaverf\">gem install lightwaverf && lightwaverf web</a>...\n    <br />@todo merge this with <a href=\"https://github.com/pauly/robot-butler\">robot butler</a>...\n  end\n  help = list\n  html = <<-end\n    <html>\n      <head>\n        <title>\#{title}</title>\n        <style type=\"text/css\">\n          body { font-family: arial, verdana, sans-serif; }\n          div#energy_chart { width: 800px; height: 600px; }\n          div#gauge_div { width: 100px; height: 100px; }\n          dd { display: none; }\n          .off, .on:hover { padding-right: 18px; background: url(lightning_delete.png) no-repeat top right; }\n          .on, .off:hover { padding-right: 18px; background: url(lightning_add.png) no-repeat top right; }\n        </style>\n      </head>\n      <body>\n        <div class=\"container\">\n          <div class=\"row\">\n            <div class=\"col\">\n              <h1>\#{title}</h1>\n              <p class=\"intro\">\#{intro}</p>\n              <div id=\"energy_chart\">\n                Not seeing an energy chart here?\n                Maybe not working in your device yet, sorry.\n                This uses google chart api which may generate FLASH :-(\n                Try in a web browser.\n              </div>\n              <h2>Rooms and devices</h2>\n              <p>@todo make these links to control the devices...</p>\n              <p class=\"help\">\#{help}</p>\n              \#{js}\n            </div>\n            <div class=\"col\">\n              <div class=\"col\" id=\"gauge_div\"></div>\n            </div>\n          </div>\n        </div>\n        <p>By <a href=\"http://www.clarkeology.com/blog/\">Paul Clarke</a>, a work in progress.</p>\n      </body>\n    </html>\n  end\n"

#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)


429
430
431
432
433
434
435
436
437
438
# File 'lib/lightwaverf.rb', line 429

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)


62
63
64
65
66
67
68
69
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
# File 'lib/lightwaverf.rb', line 62

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 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 2 mins + summarise every 5'
    crontab << '*/2 * * * * ' + 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 + ' cache timed events 1 hour back 4 hours ahead'
    crontab << '58 * * * * ' + executable + ' update_timers 60 240 > /tmp/lightwaverf_update_timers.out 2>&1'
    crontab << '# ' + executable + ' update_timers on reboot (works for me on raspbian)'
    crontab << '@reboot ' + executable + ' update_timers 60 240 > /tmp/lightwaverf_update_timers.out 2>&1'
    crontab << '# ' + executable + ' timer every 10 mins off peak'
    crontab << '*/10 0-6,9-16,23 * * * ' + executable + ' timer 10 > /tmp/lightwaverf_timer.out 2>&1'
    crontab << '# ' + executable + ' timer every 2 minutes peak'
    crontab << '*/2 7,8,17-22 * * * ' + executable + ' timer 2 > /tmp/lightwaverf_timer.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

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



613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
# File 'lib/lightwaverf.rb', line 613

def energy title = nil, text = nil, debug = false
  debug and text and ( p 'energy: ' + text )
  data = self.raw '666,@?', true
  # /W=(?<usage>\d+),(?<max>\d+),(?<today>\d+),(?<yesterday>\d+)/.match data # ruby 1.9 only?
  match = /W=(\d+),(\d+),(\d+),(\d+)/.match data
  debug and ( p match )
  if match
    data = {
      'message' => {
        'usage' => match[1].to_i,
        'max' => match[2].to_i,
        'today' => match[3].to_i
      }
    }
    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



1207
1208
1209
# File 'lib/lightwaverf.rb', line 1207

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

#get_calendar_url(debug = false) ⇒ Object



715
716
717
718
719
720
721
722
723
# File 'lib/lightwaverf.rb', line 715

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



234
235
236
237
238
239
240
241
242
243
# File 'lib/lightwaverf.rb', line 234

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



164
165
166
# File 'lib/lightwaverf.rb', line 164

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

#get_log_fileObject



168
169
170
# File 'lib/lightwaverf.rb', line 168

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

#get_modifiers(event, debug = false) ⇒ Object



786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
# File 'lib/lightwaverf.rb', line 786

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] == '+'
        debug and ( p 'Found positive time modifier: ' + modifier[1..-1] )
        event['time_modifier'] = modifier[1..-1].to_i
      elsif modifier[0,1] == '-'
        debug and ( p 'Found negative time modifier: ' + modifier[1..-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['date'] = (( event['date'].to_time ) + event['time_modifier'] * 60 ).to_datetime
    event['end'] = (( event['end'].to_time ) + event['time_modifier'] * 60 ).to_datetime
  end
  event
end

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



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/lightwaverf.rb', line 292

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



172
173
174
# File 'lib/lightwaverf.rb', line 172

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

#get_timer_cacheObject

Get timer cache file, create it if needed



208
209
210
211
212
213
214
215
216
# File 'lib/lightwaverf.rb', line 208

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



203
204
205
# File 'lib/lightwaverf.rb', line 203

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

#get_timer_log_fileObject



176
177
178
# File 'lib/lightwaverf.rb', line 176

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



333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/lightwaverf.rb', line 333

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



49
50
51
52
53
54
55
56
# File 'lib/lightwaverf.rb', line 49

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 " + room + " " + 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)


601
602
603
604
605
606
607
608
609
610
611
# File 'lib/lightwaverf.rb', line 601

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



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/lightwaverf.rb', line 180

def log_timer_event type, room = nil, device = nil, state = nil, result = false
  # create log message
  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)


553
554
555
556
557
558
559
560
561
562
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
# File 'lib/lightwaverf.rb', line 553

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



226
227
228
229
230
231
# File 'lib/lightwaverf.rb', line 226

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



219
220
221
222
223
# File 'lib/lightwaverf.rb', line 219

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

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



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
702
703
704
705
706
707
708
709
710
711
712
713
# File 'lib/lightwaverf.rb', line 675

def raw command, listen = false, debug = false
  debug and ( p self.time + ' ' + __method__.to_s + ' ' + command )
  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
        response = "can't bind to listen for a reply"
      end
    end
    # Broadcast command to server
    debug and ( p self.time 'sending...' )
    listener.send command, 0, host, 9760
    debug and ( p self.time 'sent' )
    # 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



725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
# File 'lib/lightwaverf.rb', line 725

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

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



926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
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
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
# File 'lib/lightwaverf.rb', line 926

def run_timers interval = 5, debug = false
  p '----------------'
  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 '----------------'
  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 '----------------' )
    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
    unless event['date'] >= start_horizon and event['date'] < end_horizon
      debug and ( p 'Event is NOT in horizon...ignoring')
    else
      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 '-----------------------'
  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 'Executing device. Room: ' + event['room'] + ', Device: ' + event['device'].to_s + ', State: ' + event['state']
      # result = self.send event['room'], event['device']['name'], event['state'], debug
      result = self.send event['room'], event['device'].to_s, event['state'], debug # is this right?
    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

#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)


466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/lightwaverf.rb', line 466

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' ))
  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)


525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
# File 'lib/lightwaverf.rb', line 525

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



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
# File 'lib/lightwaverf.rb', line 741

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]
        debug and ( p 'First char is: ' + first_char )
        # 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



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
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
# File 'lib/lightwaverf.rb', line 1139

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 = nil
  prev = nil
  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'] )
      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 && prev
        smoothedUsage = ( smoothedUsage + last + prev ) / 3 # average of last 3 readings
      end
      new_line << smoothedUsage / 10
      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'] > daily[d]['today'] ))
        daily[d] = line['message']
        daily[d].delete 'usage'
      end
      prev = last
      last = line['message']['usage'].to_i
    end
  end
  debug and ( puts 'got ' + data.length.to_s + ' lines in the log' )
  data = data.last 60 * 24 * days
  debug and ( puts 'now got ' + data.length.to_s + ' lines in the log ( 60 * 24 * ' + days.to_s + ' = ' + ( 60 * 24 * days ).to_s + ' )' )
  if data and data.first
    debug and ( puts 'data.first is ' + data.first.to_s )
    if data.first.first != start_date
      data.first.first += start_date
    end
  end
  summary_file = self.get_summary_file
  File.open( summary_file, 'w' ) do |file|
    # file.write data.to_s
    file.write( JSON.pretty_generate( data ))
  end
  # @todo fix the daily stats, every night it reverts to the minimum value because the timezones are different
  # so 1am on the wifi-link looks midnight on the server
  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?



43
44
45
46
# File 'lib/lightwaverf.rb', line 43

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)


447
448
449
450
451
# File 'lib/lightwaverf.rb', line 447

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



817
818
819
820
821
822
823
824
825
# File 'lib/lightwaverf.rb', line 817

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
  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 name@example.com, 1234

Arguments:

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

Credits:

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


257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/lightwaverf.rb', line 257

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://www.lightwaverfhost.co.uk/manager/index.php'
  http = Net::HTTP.new uri.host, uri.port
  http.use_ssl = true if uri.scheme == 'https'
  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
    # config['room'] is an array of hashes containing the room name and device names
    # in the format { 'name' => 'Room Name', 'device' => ['Device 1', Device 2'] }
    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



502
503
504
505
506
507
508
509
510
511
512
513
514
# File 'lib/lightwaverf.rb', line 502

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



827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
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
# File 'lib/lightwaverf.rb', line 827

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 )

  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)
    return self.log_timer_event 'update', nil, nil, nil, false
  end

  cals = RiCal.parse_string( response.body )

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

  cals.first.events.each do | e |
    occurs = e.occurrences( :overlapping => [ query_start, query_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



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/lightwaverf.rb', line 29

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