aca-device-modules 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,6 +14,9 @@ class Epson::Projector::EscVp21
14
14
  # delimiter: ":"
15
15
  #})
16
16
 
17
+ self[:volume_min] = 0
18
+ self[:volume_max] = 255
19
+
17
20
  self[:power] = false
18
21
  self[:stable_state] = true # Stable by default (allows manual on and off)
19
22
 
@@ -85,6 +88,7 @@ class Epson::Projector::EscVp21
85
88
  logger.debug "-- epson LCD, requested to switch to: #{input}"
86
89
 
87
90
  self[:input] = input # for a responsive UI
91
+ self[:mute] = false
88
92
  end
89
93
 
90
94
 
@@ -178,6 +182,7 @@ class Epson::Projector::EscVp21
178
182
  end
179
183
  if !self[:stable_state] && self[:power_target] == self[:power]
180
184
  self[:stable_state] = true
185
+ self[:mute] = false if !self[:power]
181
186
  end
182
187
 
183
188
  when :MUTE
@@ -0,0 +1,243 @@
1
+ module Kramer; end
2
+ module Kramer::Switcher; end
3
+
4
+
5
+ # :title:Kramer video switches
6
+ #
7
+ # Status information avaliable:
8
+ # -----------------------------
9
+ #
10
+ # (built in)
11
+ # connected
12
+ #
13
+ # (module defined)
14
+ # video_inputs
15
+ # video_outputs
16
+ #
17
+ # video1 => input
18
+ # video2
19
+ # video3
20
+ #
21
+
22
+ #
23
+ # NOTE:: These devices should be marked as make and break!
24
+ #
25
+
26
+ class Kramer::Switcher::Protocol3000
27
+ include ::Orchestrator::Constants
28
+ include ::Orchestrator::Transcoder
29
+
30
+ def on_load
31
+ config({
32
+ tokenize: true,
33
+ delimiter: "\x0D\x0A",
34
+ encoding: "ASCII-8BIT"
35
+ })
36
+
37
+ on_update
38
+ end
39
+
40
+ def on_update
41
+ @device_id = setting(:kramer_id)
42
+ @destination = "#{@device_id}@" if @device_id
43
+
44
+ @login_level = setting(:kramer_login)
45
+ @password = setting(:kramer_password) if @login_level
46
+ end
47
+
48
+ def connected
49
+ #
50
+ # Get current state of the switcher
51
+ #
52
+ protocol_handshake
53
+ login
54
+ get_machine_info
55
+
56
+ @polling_timer = schedule.every('2m') do
57
+ logger.debug "-- Kramer Maintaining Connection"
58
+ do_send('MODEL?', {:priority => 0}) # Low priority poll to maintain connection
59
+ end
60
+ end
61
+
62
+ def disconnected
63
+ @polling_timer.cancel unless @polling_timer.nil?
64
+ @polling_timer = nil
65
+ end
66
+
67
+
68
+ #
69
+ # Starting at input 1, input 0 == disconnect
70
+ #
71
+ def switch(map, out = nil)
72
+ map = {map => out} if out
73
+ do_send(CMDS[:switch], build_switch_data(map))
74
+ end
75
+
76
+
77
+ def switch_video(map, out = nil)
78
+ map = {map => out} if out
79
+ do_send(CMDS[:switch_video], build_switch_data(map))
80
+ end
81
+
82
+
83
+ def switch_audio(map, out = nil)
84
+ map = {map => out} if out
85
+ do_send(CMDS[:switch_audio], build_switch_data(map))
86
+ end
87
+
88
+
89
+ def mute_video(out, state = true)
90
+ data = is_affirmative?(state) ? 1 : 0
91
+ do_send(CMDS[:video_mute], out, data)
92
+ end
93
+
94
+ def mute_audio(out, state = true)
95
+ data = is_affirmative?(state) ? 1 : 0
96
+ do_send(CMDS[:audio_mute], out, data)
97
+ end
98
+
99
+ def unmute_video(out)
100
+ mute_video out, false
101
+ end
102
+
103
+ def unmute_audio(out)
104
+ mute_audio out, false
105
+ end
106
+
107
+
108
+ def received(data, resolve, command)
109
+ logger.debug "Kramer sent #{data}"
110
+
111
+ # Extract and check the machine number if we've defined it
112
+ components = data.split('@')
113
+ if components.length > 1
114
+ machine = components[0]
115
+ if @device_id && machine != @device_id
116
+ return :ignore
117
+ end
118
+ end
119
+
120
+ data = components[-1].strip
121
+ components = data.split(/\s+|,/)
122
+
123
+ cmd = components[0]
124
+ args = components[1..-1]
125
+
126
+ if cmd == 'OK'
127
+ return :success
128
+ elsif cmd[0..2] == 'ERR' || args[0][0..2] == 'ERR'
129
+ if cmd[0..2] == 'ERR'
130
+ error = cmd[3..-1]
131
+ errfor = nil
132
+ else
133
+ error = args[0][3..-1]
134
+ errfor = " on #{cmd}"
135
+ end
136
+ logger.error "Kramer command error #{error}#{errfor}"
137
+ self[:last_error] = error
138
+ return :abort
139
+ end
140
+
141
+ case CMDS[cmd.to_sym]
142
+ when :info
143
+ self[:video_inputs] = args[1].to_i
144
+ self[:video_outputs] = args[3].to_i
145
+ when :route
146
+ inout = args[0].split(',')
147
+ layer = inout[0].to_i
148
+ dest = inout[1].to_i
149
+ src = inout[2].to_i
150
+ self[:"#{LAYERS[layer]}#{dest}"] = src
151
+ when :switch, :switch_audio, :switch_video
152
+ # return string like "in>out,in>out,in>out"
153
+
154
+ type = :av
155
+ type = :audio if CMDS[cmd] == :switch_audio
156
+ type = :video if CMDS[cmd] == :switch_video
157
+
158
+ mappings = args[0].split(',')
159
+ mappings.each do |map|
160
+ inout = map.split('>')
161
+ self[:"#{type}#{inout[1]}"] = inout[0].to_i
162
+ end
163
+ when :audio_mute
164
+ output, mute = args[0].split(',')
165
+ self[:"audio#{output}_muted"] = mute == '1'
166
+ when :video_mute
167
+ output, mute = args[0].split(',')
168
+ self[:"video#{output}_muted"] = mute == '1'
169
+ end
170
+
171
+ return :success
172
+ end
173
+
174
+
175
+ CMDS = {
176
+ info: :"INFO-IO?",
177
+ login: :"LOGIN",
178
+ route: :"ROUTE",
179
+ switch: :"AV",
180
+ switch_audio: :"AUD",
181
+ switch_video: :"VID",
182
+ audio_mute: :"MUTE",
183
+ video_mute: :"VMUTE"
184
+ }
185
+ CMDS.merge!(CMDS.invert)
186
+
187
+ LAYERS = {
188
+ 1 => :video,
189
+ 2 => :audio,
190
+ 2 => :data
191
+ }
192
+
193
+
194
+ private
195
+
196
+
197
+ def build_switch_data(map)
198
+ data = ''
199
+
200
+ map.each do |input, outputs|
201
+ outputs = [outputs] unless outputs.class == Array
202
+ input = input.to_s if input.class == Symbol
203
+ input = input.to_i if input.class == String
204
+ outputs.each do |output|
205
+ data << "#{input}>#{output},"
206
+ end
207
+ end
208
+
209
+ data.chop
210
+ end
211
+
212
+
213
+ def protocol_handshake
214
+ do_send('', {priority: 99})
215
+ end
216
+
217
+ def login
218
+ if @login_level
219
+ do_send(CMDS[:login], @password, {priority: 99})
220
+ end
221
+ end
222
+
223
+ def get_machine_info
224
+ do_send(CMDS[:info], {priority: 99})
225
+ end
226
+
227
+
228
+ def do_send(command, *args)
229
+ options = {}
230
+ if args[-1].is_a? Hash
231
+ options = args.pop
232
+ end
233
+
234
+ cmd = "##{@destination}#{command}"
235
+
236
+ if args.length > 0
237
+ cmd << " #{args.join(',')}"
238
+ end
239
+ cmd << "\r"
240
+
241
+ send(cmd, options)
242
+ end
243
+ end
@@ -0,0 +1,482 @@
1
+ module Nec; end
2
+ module Nec::Display; end
3
+
4
+ # :title:All NEC Control Module
5
+ #
6
+ # Controls all LCD displays as of 1/07/2011
7
+ # Status information avaliable:
8
+ # -----------------------------
9
+ #
10
+ # (built in)
11
+ # connected
12
+ #
13
+ # (module defined)
14
+ # power
15
+ # warming
16
+ #
17
+ # volume
18
+ # volume_min == 0
19
+ # volume_max
20
+ #
21
+ # brightness
22
+ # brightness_min == 0
23
+ # brightness_max
24
+ #
25
+ # contrast
26
+ # contrast_min = 0
27
+ # contrast_max
28
+ #
29
+ # audio_mute
30
+ #
31
+ # input (video input)
32
+ # audio (audio input)
33
+ #
34
+ #
35
+ class Nec::Display::All
36
+ include ::Orchestrator::Constants
37
+ include ::Orchestrator::Transcoder
38
+
39
+ #
40
+ # Called on module load complete
41
+ # Alternatively you can use initialize however will
42
+ # not have access to settings and this is called
43
+ # soon afterwards
44
+ #
45
+ def on_load
46
+ #
47
+ # Setup constants
48
+ #
49
+ self[:volume_min] = 0
50
+ self[:brightness_min] = 0
51
+ self[:contrast_min] = 0
52
+ #self[:error] = [] TODO!!
53
+ end
54
+
55
+ def response_delimiter # Function required if device could contacts us first
56
+ 0x0D
57
+ end
58
+
59
+ def connected
60
+ do_poll
61
+
62
+ @polling_timer = schedule.every('30s') do
63
+ logger.debug "-- Polling Display"
64
+ do_poll
65
+ end
66
+ end
67
+
68
+ def disconnected
69
+ #
70
+ # Disconnected may be called without calling connected
71
+ # Hence the check if timer is nil here
72
+ #
73
+ @polling_timer.cancel unless @polling_timer.nil?
74
+ @polling_timer = nil
75
+ end
76
+
77
+
78
+ #
79
+ # Power commands
80
+ #
81
+ def power(state)
82
+ message = "C203D6"
83
+
84
+ power? do |result|
85
+ if is_affirmative?(state)
86
+ if result == Off
87
+ message += "0001" # Power On
88
+ send_checksum(:command, message, {:name => :power})
89
+ self[:warming] = true
90
+ self[:power] = On
91
+ logger.debug "-- NEC LCD, requested to power on"
92
+
93
+ power_on_delay
94
+ mute_status(0)
95
+ volume_status(0)
96
+ end
97
+ else
98
+ if result == On
99
+ message += "0004" # Power Off
100
+ send_checksum(:command, message, {:name => :power})
101
+
102
+ self[:power] = Off
103
+ logger.debug "-- NEC LCD, requested to power off"
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ def power?(options = {}, &block)
110
+ options[:emit] = block if block_given?
111
+ type = :command
112
+ message = "01D6"
113
+ send_checksum(type, message, options)
114
+ end
115
+
116
+
117
+ #
118
+ # Input selection
119
+ #
120
+ INPUTS = {
121
+ :vga => 1,
122
+ :rgbhv => 2,
123
+ :dvi => 3,
124
+ :hdmi_set => 4, # Set only?
125
+ :video1 => 5,
126
+ :video2 => 6,
127
+ :svideo => 7,
128
+
129
+ :tv => 10,
130
+ :dvd1 => 12,
131
+ :option => 13,
132
+ :dvd2 => 14,
133
+ :display_port => 15,
134
+
135
+ :hdmi => 17
136
+ }
137
+ def switch_to(input)
138
+ input = input.to_sym if input.class == String
139
+ #self[:target_input] = input
140
+
141
+ type = :set_parameter
142
+ message = OPERATION_CODE[:video_input]
143
+ message += INPUTS[input].to_s(16).upcase.rjust(4, '0') # Value of input as a hex string
144
+
145
+ send_checksum(type, message, {:name => :input})
146
+ brightness_status(60) # higher status than polling commands - lower than input switching
147
+ contrast_status(60)
148
+
149
+ logger.debug "-- NEC LCD, requested to switch to: #{input}"
150
+ end
151
+
152
+ AUDIO = {
153
+ :audio1 => 1,
154
+ :audio2 => 2,
155
+ :audio3 => 3,
156
+ :hdmi => 4,
157
+ :tv => 6,
158
+ :display_port => 7
159
+ }
160
+ def switch_audio(input)
161
+ input = input.to_sym if input.class == String
162
+ #self[:target_audio] = input
163
+
164
+ type = :set_parameter
165
+ message = OPERATION_CODE[:audio_input]
166
+ message += AUDIO[input].to_s(16).upcase.rjust(4, '0') # Value of input as a hex string
167
+
168
+ send_checksum(type, message, :name => :audio)
169
+ mute_status(60) # higher status than polling commands - lower than input switching
170
+ volume_status(60)
171
+
172
+ logger.debug "-- NEC LCD, requested to switch audio to: #{input}"
173
+ end
174
+
175
+
176
+ #
177
+ # Auto adjust
178
+ #
179
+ def auto_adjust
180
+ message = OPERATION_CODE[:auto_setup] #"001E" # Page + OP code
181
+ message += "0001" # Value of input as a hex string
182
+
183
+ send_checksum(:set_parameter, message, :delay_on_receive => 4.0)
184
+ end
185
+
186
+
187
+ #
188
+ # Value based set parameter
189
+ #
190
+ def brightness(val)
191
+ val = val.to_i if val.is_a? String
192
+ val = 100 if val > 100
193
+ val = 0 if val < 0
194
+
195
+ message = OPERATION_CODE[:brightness_status]
196
+ message += val.to_s(16).upcase.rjust(4, '0') # Value of input as a hex string
197
+
198
+ brightness_status
199
+ send_checksum(:set_parameter, message)
200
+ send_checksum(:command, '0C') # Save the settings
201
+ end
202
+
203
+ def contrast(val)
204
+ val = val.to_i if val.is_a? String
205
+ val = 100 if val > 100
206
+ val = 0 if val < 0
207
+
208
+ message = OPERATION_CODE[:contrast_status]
209
+ message += val.to_s(16).upcase.rjust(4, '0') # Value of input as a hex string
210
+
211
+ contrast_status
212
+ send_checksum(:set_parameter, message)
213
+ send_checksum(:command, '0C') # Save the settings
214
+ end
215
+
216
+ def volume(val)
217
+ val = val.to_i if val.is_a? String
218
+ val = 100 if val > 100
219
+ val = 0 if val < 0
220
+
221
+ message = OPERATION_CODE[:volume_status]
222
+ message += val.to_s(16).upcase.rjust(4, '0') # Value of input as a hex string
223
+
224
+ volume_status
225
+ send_checksum(:set_parameter, message)
226
+ send_checksum(:command, '0C') # Save the settings
227
+
228
+ self[:audio_mute] = false # audio is unmuted when the volume is set
229
+ end
230
+
231
+
232
+ def mute_audio
233
+ message = OPERATION_CODE[:mute_status]
234
+ message += "0001" # Value of input as a hex string
235
+
236
+ send_checksum(:set_parameter, message)
237
+
238
+ logger.debug "-- NEC LCD, requested to mute audio"
239
+ end
240
+ alias_method :mute, :mute_audio
241
+
242
+ def unmute_audio
243
+ message = OPERATION_CODE[:mute_status]
244
+ message += "0000" # Value of input as a hex string
245
+
246
+ send_checksum(:set_parameter, message)
247
+
248
+ logger.debug "-- NEC LCD, requested to unmute audio"
249
+ end
250
+ alias_method :unmute, :unmute_audio
251
+
252
+ #
253
+ # LCD Response code
254
+ #
255
+ def received(data, resolve, command)
256
+ #
257
+ # Check for valid response
258
+ #
259
+ if !check_checksum(data)
260
+ logger.debug "-- NEC LCD, checksum failed for command: #{command[:data]}"
261
+ logger.debug "-- NEC LCD, response was: #{data}"
262
+ return false
263
+ end
264
+
265
+ #data = array_to_str(data) # Convert bytes to a string (received like this)
266
+
267
+ case MSG_TYPE[data[4]] # Check the MSG_TYPE (B, D or F)
268
+ when :command_reply
269
+ #
270
+ # Power on and off
271
+ # 8..9 == "00" means no error
272
+ if data[10..15] == "C203D6" # Means power comamnd
273
+ if data[8..9] == "00"
274
+ power_on_delay(0) # wait until the screen has turned on before sending commands (0 == high priority)
275
+ else
276
+ logger.info "-- NEC LCD, command failed: #{command[:data]}"
277
+ logger.info "-- NEC LCD, response was: #{data}"
278
+ return false # command failed
279
+ end
280
+ elsif data[10..13] == "00D6" # Power status response
281
+ if data[10..11] == "00"
282
+ if data[23] == '1' # On == 1, Off == 4
283
+ self[:power] = On
284
+ else
285
+ self[:power] = Off
286
+ self[:warming] = false
287
+ end
288
+ #if self[:power_target].nil?
289
+ # self[:power_target] = self[:power]
290
+ #elsif self[:power_target] != self[:power]
291
+ # power(self[:power_target])
292
+ #end
293
+ else
294
+ logger.info "-- NEC LCD, command failed: #{command[:data]}"
295
+ logger.info "-- NEC LCD, response was: #{data}"
296
+ return false # command failed
297
+ end
298
+
299
+ end
300
+
301
+ when :get_parameter_reply, :set_parameter_reply
302
+ if data[8..9] == "00"
303
+ parse_response(data, command)
304
+ elsif data[8..9] == 'BE' # Wait response
305
+ send(command[:data]) # checksum already added
306
+ logger.debug "-- NEC LCD, response was a wait command"
307
+ else
308
+ logger.info "-- NEC LCD, get or set failed: #{command[:data]}"
309
+ logger.info "-- NEC LCD, response was: #{data}"
310
+ return false
311
+ end
312
+ end
313
+
314
+ return true # Command success
315
+ end
316
+
317
+
318
+ def do_poll
319
+ power?({:priority => 99}) do |result|
320
+ if result == On
321
+ power_on_delay
322
+ mute_status
323
+ volume_status
324
+ brightness_status
325
+ contrast_status
326
+ video_input
327
+ audio_input
328
+ end
329
+ end
330
+ end
331
+
332
+
333
+ private
334
+
335
+
336
+ def parse_response(data, command)
337
+
338
+ # 14..15 == type (we don't care)
339
+ max = data[16..19].to_i(16)
340
+ value = data[20..23].to_i(16)
341
+
342
+ case OPERATION_CODE[data[10..13]]
343
+ when :video_input
344
+ self[:input] = INPUTS.invert[value]
345
+ #self[:target_input] = self[:input] if self[:target_input].nil?
346
+ #switch_to(self[:target_input]) unless self[:input] == self[:target_input]
347
+
348
+ when :audio_input
349
+ self[:audio] = AUDIO.invert[value]
350
+ #self[:target_audio] = self[:audio] if self[:target_audio].nil?
351
+ #switch_audio(self[:target_audio]) unless self[:audio] == self[:target_audio]
352
+
353
+ when :volume_status
354
+ self[:volume_max] = max
355
+ if not self[:audio_mute]
356
+ self[:volume] = value
357
+ end
358
+
359
+ when :brightness_status
360
+ self[:brightness_max] = max
361
+ self[:brightness] = value
362
+
363
+ when :contrast_status
364
+ self[:contrast_max] = max
365
+ self[:contrast] = value
366
+
367
+ when :mute_status
368
+ self[:audio_mute] = value == 1
369
+ if(value == 1)
370
+ self[:volume] = 0
371
+ else
372
+ volume_status(0) # high priority
373
+ end
374
+
375
+ when :power_on_delay
376
+ if value > 0
377
+ self[:warming] = true
378
+ schedule.in("#{value}s") do # Prevent any commands being sent until the power on delay is complete
379
+ power_on_delay
380
+ end
381
+ else
382
+ schedule.in('3s') do # Reactive the interface once the display is online
383
+ self[:warming] = false # allow access to the display
384
+ end
385
+ end
386
+ when :auto_setup
387
+ # auto_setup
388
+ # nothing needed to do here (we are delaying the next command by 4 seconds)
389
+ else
390
+ logger.info "-- NEC LCD, unknown response: #{data[10..13]}"
391
+ logger.info "-- NEC LCD, for command: #{command[:data]}"
392
+ logger.info "-- NEC LCD, full response was: #{data}"
393
+ end
394
+ end
395
+
396
+
397
+ #
398
+ # Types of messages sent to and from the LCD
399
+ #
400
+ MSG_TYPE = {
401
+ :command => 'A',
402
+ 'B' => :command_reply,
403
+ :get_parameter => 'C',
404
+ 'D' => :get_parameter_reply,
405
+ :set_parameter => 'E',
406
+ 'F' => :set_parameter_reply
407
+ }
408
+
409
+
410
+ OPERATION_CODE = {
411
+ :video_input => '0060', '0060' => :video_input,
412
+ :audio_input => '022E', '022E' => :audio_input,
413
+ :volume_status => '0062', '0062' => :volume_status,
414
+ :mute_status => '008D', '008D' => :mute_status,
415
+ :power_on_delay => '02D8', '02D8' => :power_on_delay,
416
+ :contrast_status => '0012', '0012' => :contrast_status,
417
+ :brightness_status => '0010', '0010' => :brightness_status,
418
+ :auto_setup => '001E', '001E' => :auto_setup
419
+ }
420
+ #
421
+ # Automatically creates a callable function for each command
422
+ # http://blog.jayfields.com/2007/10/ruby-defining-class-methods.html
423
+ # http://blog.jayfields.com/2008/02/ruby-dynamically-define-method.html
424
+ #
425
+ OPERATION_CODE.each_key do |command|
426
+ define_method command do |*args|
427
+ priority = 99
428
+ if args.length > 0
429
+ priority = args[0]
430
+ end
431
+ message = OPERATION_CODE[command]
432
+ send_checksum(:get_parameter, message, {:priority => priority}) # Status polling is a low priority
433
+ end
434
+ end
435
+
436
+
437
+ def check_checksum(data)
438
+ data = str_to_array(data)
439
+
440
+ check = 0
441
+ #
442
+ # Loop through the second to the second last element
443
+ # Delimiter is removed automatically
444
+ #
445
+ if data.length >= 2
446
+ data[1..-2].each do |byte|
447
+ check = check ^ byte
448
+ end
449
+ return check == data[-1] # Check the check sum equals the last element
450
+ else
451
+ return true
452
+ end
453
+ end
454
+
455
+
456
+ #
457
+ # Builds the command and creates the checksum
458
+ #
459
+ def send_checksum(type, command, options = {})
460
+ #
461
+ # build header + command and convert to a byte array
462
+ #
463
+ command = "" << 0x02 << command << 0x03
464
+ command = "0*0#{MSG_TYPE[type]}#{command.length.to_s(16).upcase.rjust(2, '0')}#{command}"
465
+ command = str_to_array(command)
466
+
467
+ #
468
+ # build checksum
469
+ #
470
+ check = 0
471
+ command.each do |byte|
472
+ check = check ^ byte
473
+ end
474
+
475
+ command << check # Add checksum
476
+ command << 0x0D # delimiter required by NEC displays
477
+ command.insert(0, 0x01) # insert SOH byte (not part of the checksum)
478
+
479
+ send(command, options)
480
+ end
481
+ end
482
+