aca-device-modules 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/aca-device-modules/version.rb +1 -1
- data/modules/aca/lifter_logic_auto.rb +71 -0
- data/modules/aca/lifter_logic_manual.rb +105 -0
- data/modules/aca/pc_control.rb +111 -111
- data/modules/axis/camera/vapix.rb +10 -3
- data/modules/biamp/nexia.rb +149 -128
- data/modules/bss/blu100.rb +8 -8
- data/modules/chiyu/cyt.rb +6 -1
- data/modules/clipsal/c_bus.rb +233 -235
- data/modules/epson/projector/esc_vp21.rb +232 -232
- data/modules/extron/mixer/dmp44.rb +174 -174
- data/modules/extron/mixer/dmp64.rb +192 -181
- data/modules/extron/switcher/dxp.rb +210 -210
- data/modules/global_cache/gc100.rb +6 -2
- data/modules/kramer/switcher/protocol3000.rb +209 -209
- data/modules/kramer/switcher/vs_hdmi.rb +111 -110
- data/modules/nec/display/all.rb +440 -443
- data/modules/nec/projector/np_series.rb +609 -597
- data/modules/panasonic/camera/he50.rb +1 -1
- data/modules/panasonic/projector/tcp.rb +239 -234
- data/modules/philips/dynalite.rb +196 -0
- data/modules/samsung/displays/md_series.rb +34 -16
- data/modules/screen_technics/connect.rb +53 -53
- data/modules/sony/display/id_talk.rb +275 -275
- data/modules/sony/projector/pj_talk.rb +257 -257
- data/modules/vaddio/camera/clear_view_ptz_telnet.rb +7 -3
- metadata +6 -3
@@ -45,604 +45,616 @@ module Nec::Projector; end
|
|
45
45
|
|
46
46
|
|
47
47
|
class Nec::Projector::NpSeries
|
48
|
-
|
48
|
+
include ::Orchestrator::Constants
|
49
49
|
include ::Orchestrator::Transcoder
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
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
|
-
|
291
|
-
|
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
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
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
|
-
|
388
|
-
|
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
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
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
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
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
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
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
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
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
|
-
|
51
|
+
def on_unload
|
52
|
+
end
|
53
|
+
|
54
|
+
def on_update
|
55
|
+
self[:power_stable] = true
|
56
|
+
self[:input_stable] = true
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
#
|
62
|
+
# Sets up any constants
|
63
|
+
#
|
64
|
+
def on_load
|
65
|
+
|
66
|
+
#
|
67
|
+
# Setup constants
|
68
|
+
#
|
69
|
+
self[:volume_min] = 0
|
70
|
+
self[:volume_max] = 63
|
71
|
+
self[:lamp_usage] = []
|
72
|
+
self[:filter_usage] = []
|
73
|
+
self[:error] = []
|
74
|
+
|
75
|
+
self[:power_stable] = true
|
76
|
+
self[:input_stable] = true
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Connect and request projector status
|
81
|
+
# NOTE:: Only connected and disconnected are threadsafe
|
82
|
+
# Access of other variables should be protected outside of these functions
|
83
|
+
#
|
84
|
+
def connected
|
85
|
+
#
|
86
|
+
# Get current state of the projector
|
87
|
+
#
|
88
|
+
do_poll
|
89
|
+
|
90
|
+
#
|
91
|
+
# Get the state every 50 seconds :)
|
92
|
+
#
|
93
|
+
@polling_timer = schedule.every('50s') do
|
94
|
+
do_poll
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def disconnected
|
99
|
+
#
|
100
|
+
# Perform any cleanup functions here
|
101
|
+
#
|
102
|
+
@polling_timer.cancel unless @polling_timer.nil?
|
103
|
+
@polling_timer = nil
|
104
|
+
|
105
|
+
# Disconnect often occurs on power off
|
106
|
+
# We may have not received a status response before the disconnect occurs
|
107
|
+
self[:power] = false
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
#
|
112
|
+
# Command Listing
|
113
|
+
# Second byte used to detect command type
|
114
|
+
#
|
115
|
+
COMMAND = {
|
116
|
+
# Mute controls
|
117
|
+
:mute_picture => "$02,$10,$00,$00,$00,$12",
|
118
|
+
:unmute_picture => "$02,$11,$00,$00,$00,$13",
|
119
|
+
:mute_audio => "02H 12H 00H 00H 00H 14H",
|
120
|
+
:unmute_audio => "02H 13H 00H 00H 00H 15H",
|
121
|
+
:mute_onscreen => "02H 14H 00H 00H 00H 16H",
|
122
|
+
:unmute_onscreen => "02H 15H 00H 00H 00H 17H",
|
123
|
+
|
124
|
+
:freeze_picture => "$01,$98,$00,$00,$01,$01,$9B",
|
125
|
+
:unfreeze_picture =>"$01,$98,$00,$00,$01,$02,$9C",
|
126
|
+
|
127
|
+
:status_lamp => "00H 81H 00H 00H 00H 81H", # Running sense (ret 81)
|
128
|
+
:status_input => "$00,$85,$00,$00,$01,$02,$88", # Input status (ret 85)
|
129
|
+
:status_mute => "00H 85H 00H 00H 01H 03H 89H", # MUTE STATUS REQUEST (Check 10H on byte 5)
|
130
|
+
:status_error => "00H 88H 00H 00H 00H 88H", # ERROR STATUS REQUEST (ret 88)
|
131
|
+
:status_model => "00H 85H 00H 00H 01H 04H 8A", # request model name (both of these are related)
|
132
|
+
|
133
|
+
# lamp hours / remaining information
|
134
|
+
:lamp_information => "03H 8AH 00H 00H 00H 8DH", # LAMP INFORMATION REQUEST
|
135
|
+
:filter_information => "03H 8AH 00H 00H 00H 8DH",
|
136
|
+
:projector_information => "03H 8AH 00H 00H 00H 8DH",
|
137
|
+
|
138
|
+
:background_black =>"$03,$B1,$00,$00,$02,$0B,$01,$C2", # set mute to be a black screen
|
139
|
+
:background_blue => "$03,$B1,$00,$00,$02,$0B,$00,$C1", # set mute to be a blue screen
|
140
|
+
:background_logo => "$03,$B1,$00,$00,$02,$0B,$02,$C3" # set mute to be the company logo
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
#
|
145
|
+
# Automatically creates a callable function for each command
|
146
|
+
# http://blog.jayfields.com/2007/10/ruby-defining-class-methods.html
|
147
|
+
# http://blog.jayfields.com/2008/02/ruby-dynamically-define-method.html
|
148
|
+
#
|
149
|
+
COMMAND.each_key do |command|
|
150
|
+
define_method command do
|
151
|
+
send(COMMAND[command], :hex_string => true, :name => command)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
#
|
157
|
+
# Volume Modification
|
158
|
+
#
|
159
|
+
def volume(vol)
|
160
|
+
# volume base command D1 D2 D3 D4 D5 + CKS
|
161
|
+
command = [0x03, 0x10, 0x00, 0x00, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00]
|
162
|
+
# D3 = 00 (absolute vol) or 01 (relative vol)
|
163
|
+
# D4 = value (lower bits 0 to 63)
|
164
|
+
# D5 = value (higher bits always 00h)
|
165
|
+
|
166
|
+
vol = 63 if vol > 63
|
167
|
+
vol = 0 if vol < 0
|
168
|
+
command[-2] = vol
|
169
|
+
|
170
|
+
self[:volume] = vol
|
171
|
+
|
172
|
+
send_checksum(command)
|
173
|
+
end
|
174
|
+
|
175
|
+
#
|
176
|
+
# Mutes everything
|
177
|
+
#
|
178
|
+
def mute(state = true)
|
179
|
+
if state
|
180
|
+
mute_picture
|
181
|
+
mute_onscreen
|
182
|
+
else
|
183
|
+
unmute
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
#
|
188
|
+
# unmutes everything desirable
|
189
|
+
#
|
190
|
+
def unmute
|
191
|
+
unmute_picture
|
192
|
+
end
|
193
|
+
|
194
|
+
#
|
195
|
+
# Sets the lamp power value
|
196
|
+
#
|
197
|
+
def power(power)
|
198
|
+
#:lamp_on => "$02,$00,$00,$00,$00,$02",
|
199
|
+
#:lamp_off => "$02,$01,$00,$00,$00,$03",
|
200
|
+
self[:power_stable] = false
|
201
|
+
|
202
|
+
command = [0x02, 0x00, 0x00, 0x00, 0x00, 0x02]
|
203
|
+
if is_negatory?(power)
|
204
|
+
command[1] += 1 # power off
|
205
|
+
command[-1] += 1 # checksum
|
206
|
+
self[:power_target] = Off
|
207
|
+
|
208
|
+
send(command, :name => :power, :timeout => 15000, :delay => 15000)
|
209
|
+
else
|
210
|
+
self[:power_target] = On
|
211
|
+
send(command, :name => :power, :timeout => 15000)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def power?(options = {}, &block)
|
216
|
+
options[:emit] = block if block_given?
|
217
|
+
options[:hex_string] = true
|
218
|
+
send(COMMAND[:status_lamp], options)
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
INPUTS = {
|
223
|
+
:vga1 => 0x01,
|
224
|
+
:vga => 0x01,
|
225
|
+
:rgbhv => 0x02, # \
|
226
|
+
:dvi_a => 0x02, # } - all of these are the same
|
227
|
+
:vga2 => 0x02, # /
|
228
|
+
|
229
|
+
:composite => 0x06,
|
230
|
+
:svideo => 0x0B,
|
231
|
+
|
232
|
+
:component1 => 0x10,
|
233
|
+
:component => 0x10,
|
234
|
+
:component2 => 0x11,
|
235
|
+
|
236
|
+
:hdmi => 0x1A, # \
|
237
|
+
:dvi => 0x1A, # | - These are the same
|
238
|
+
:hdmi2 => 0x1B,
|
239
|
+
:display_port => 0x1B,
|
240
|
+
|
241
|
+
:lan => 0x20,
|
242
|
+
:viewer => 0x1F
|
243
|
+
}
|
244
|
+
def switch_to(input)
|
245
|
+
input = input.to_sym if input.class == String
|
246
|
+
|
247
|
+
#
|
248
|
+
# Input status update
|
249
|
+
# As much for internal use as external
|
250
|
+
# and with the added benefit of being thread safe
|
251
|
+
#
|
252
|
+
self[:target_input] = input # should do this for power on and off (ensures correct state)
|
253
|
+
self[:input_stable] = false
|
254
|
+
|
255
|
+
command = [0x02, 0x03, 0x00, 0x00, 0x02, 0x01]
|
256
|
+
command << INPUTS[input]
|
257
|
+
send_checksum(command, :name => :input)
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
#
|
262
|
+
# Return true if command success, nil if still waiting, false if fail
|
263
|
+
#
|
264
|
+
def received(data, resolve, command)
|
265
|
+
response = data
|
266
|
+
data = str_to_array(data)
|
267
|
+
req = str_to_array(command[:data]) if command && command[:data]
|
268
|
+
|
269
|
+
logger.debug "NEC projector sent: 0x#{byte_to_hex(response)}"
|
270
|
+
|
271
|
+
#
|
272
|
+
# Command failed
|
273
|
+
#
|
274
|
+
if data[0] & 0xA0 == 0xA0
|
275
|
+
#
|
276
|
+
# We were changing power state at time of failure we should keep trying
|
277
|
+
#
|
278
|
+
if req && [0x00, 0x01].include?(req[1])
|
279
|
+
command[:delay_on_receive] = 6000
|
280
|
+
power?
|
281
|
+
return true
|
282
|
+
end
|
283
|
+
logger.warn "-- NEC projector, sent fail code for command: 0x#{byte_to_hex(req)}" if req
|
284
|
+
logger.warn "-- NEC projector, response was: 0x#{byte_to_hex(response)}"
|
285
|
+
return false
|
286
|
+
end
|
287
|
+
|
288
|
+
#
|
289
|
+
# Check checksum
|
290
|
+
#
|
291
|
+
if !check_checksum(data)
|
292
|
+
logger.debug "-- NEC projector, checksum failed for command: 0x#{byte_to_hex(req)}" if req
|
293
|
+
return false
|
294
|
+
end
|
295
|
+
|
296
|
+
#
|
297
|
+
# Process a successful command
|
298
|
+
# add 0x20 to the first byte of the send command
|
299
|
+
# Then match the second byte to the second byte of the send command
|
300
|
+
#
|
301
|
+
case data[0]
|
302
|
+
when 0x20
|
303
|
+
case data[1]
|
304
|
+
when 0x81
|
305
|
+
process_power_status(data, command)
|
306
|
+
return true
|
307
|
+
when 0x88
|
308
|
+
process_error_status(data, command)
|
309
|
+
return true
|
310
|
+
when 0x85
|
311
|
+
# Return if we can't work out what was requested initially
|
312
|
+
return true unless req
|
313
|
+
|
314
|
+
case req[-2]
|
315
|
+
when 0x02
|
316
|
+
process_input_state(data, command)
|
317
|
+
return true
|
318
|
+
when 0x03
|
319
|
+
process_mute_state(data, req)
|
320
|
+
return true
|
321
|
+
end
|
322
|
+
end
|
323
|
+
when 0x22
|
324
|
+
case data[1]
|
325
|
+
when 0x03
|
326
|
+
return process_input_switch(data, req)
|
327
|
+
when 0x00, 0x01
|
328
|
+
process_lamp_command(data, req)
|
329
|
+
return true
|
330
|
+
when 0x10, 0x11, 0x12, 0x13, 0x14, 0x15
|
331
|
+
status_mute # update mute status's (dry)
|
332
|
+
return true
|
333
|
+
end
|
334
|
+
when 0x23
|
335
|
+
case data[1]
|
336
|
+
when 0x10
|
337
|
+
#
|
338
|
+
# Picture, Volume, Keystone, Image adjust mode
|
339
|
+
# how to play this?
|
340
|
+
#
|
341
|
+
# TODO:: process volume control
|
342
|
+
#
|
343
|
+
return true
|
344
|
+
when 0x8A
|
345
|
+
process_projector_information(data, req)
|
346
|
+
return true
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
logger.info "-- NEC projector, no status updates defined for response: #{byte_to_hex(response)}"
|
351
|
+
logger.info "-- NEC projector, command was: 0x#{byte_to_hex(req)}" if req
|
352
|
+
return true # to prevent retries on commands we were not expecting
|
353
|
+
end
|
354
|
+
|
355
|
+
|
356
|
+
private # All response handling functions should be private so they cannot be called from the outside world
|
357
|
+
|
358
|
+
|
359
|
+
#
|
360
|
+
# The polling routine for the projector
|
361
|
+
#
|
362
|
+
def do_poll
|
363
|
+
power?({:priority => 0})
|
364
|
+
if self[:power]
|
365
|
+
status_input
|
366
|
+
status_mute
|
367
|
+
background_black
|
368
|
+
end
|
369
|
+
#projector_information
|
370
|
+
#status_error
|
371
|
+
end
|
372
|
+
|
373
|
+
|
374
|
+
#
|
375
|
+
# Process the lamp on/off command response
|
376
|
+
#
|
377
|
+
def process_lamp_command(data, req)
|
378
|
+
logger.debug "-- NEC projector sent a response to a power command"
|
379
|
+
|
380
|
+
#
|
381
|
+
# Ensure a change of power state was the last command sent
|
382
|
+
#
|
383
|
+
#self[:power] = data[1] == 0x00
|
384
|
+
if req.present? && [0x00, 0x01].include?(req[1])
|
385
|
+
power? # Queues the status power command
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
#
|
390
|
+
# Process the lamp status response
|
391
|
+
# Intimately entwinded with the power power command
|
392
|
+
# (as we need to control ensure we are in the correct target state)
|
393
|
+
#
|
394
|
+
def process_power_status(data, command)
|
395
|
+
logger.debug "-- NEC projector sent a response to a power status command"
|
396
|
+
|
397
|
+
self[:power] = (data[-2] & 0b10) > 0x0 # Power on?
|
398
|
+
|
399
|
+
if (data[-2] & 0b100000) > 0 || (data[-2] & 0b10000000) > 0
|
400
|
+
# Projector cooling || power on off processing
|
401
|
+
|
402
|
+
if self[:power_target] == On
|
403
|
+
self[:cooling] = false
|
404
|
+
self[:warming] = true
|
405
|
+
|
406
|
+
logger.debug "power warming..."
|
407
|
+
|
408
|
+
|
409
|
+
elsif self[:power_target] == Off
|
410
|
+
self[:warming] = false
|
411
|
+
self[:cooling] = true
|
412
|
+
|
413
|
+
logger.debug "power cooling..."
|
414
|
+
end
|
415
|
+
|
416
|
+
|
417
|
+
# recheck in 3 seconds
|
418
|
+
schedule.in(3000) do
|
419
|
+
power?
|
420
|
+
end
|
421
|
+
|
422
|
+
# Signal processing
|
423
|
+
elsif (data[-2] & 0b1000000) > 0
|
424
|
+
schedule.in(3000) do
|
425
|
+
power?
|
426
|
+
end
|
427
|
+
else
|
428
|
+
#
|
429
|
+
# We are in a stable state!
|
430
|
+
#
|
431
|
+
if (self[:power] != self[:power_target]) && !self[:power_stable]
|
432
|
+
if self[:power_target].nil?
|
433
|
+
self[:power_target] = self[:power] # setup initial state if the control system is just coming online
|
434
|
+
self[:power_stable] = true
|
435
|
+
else
|
436
|
+
#
|
437
|
+
# if we are in an undesirable state then correct it
|
438
|
+
#
|
439
|
+
logger.debug "NEC projector in an undesirable power state... (Correcting)"
|
440
|
+
power(self[:power_target])
|
441
|
+
end
|
442
|
+
else
|
443
|
+
logger.debug "NEC projector is in a good power state..."
|
444
|
+
|
445
|
+
self[:warming] = false
|
446
|
+
self[:cooling] = false
|
447
|
+
self[:power_stable] = true
|
448
|
+
|
449
|
+
#
|
450
|
+
# Ensure the input is in the correct state unless the lamp is off
|
451
|
+
#
|
452
|
+
status_input unless self[:power] == Off # calls status mute
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
|
457
|
+
logger.debug "Current state {power: #{self[:power]}, warming: #{self[:warming]}, cooling: #{self[:cooling]}}"
|
458
|
+
end
|
459
|
+
|
460
|
+
|
461
|
+
#
|
462
|
+
# NEC has different values for the input status when compared to input selection
|
463
|
+
#
|
464
|
+
INPUT_MAP = {
|
465
|
+
0x01 => {
|
466
|
+
0x01 => [:vga, :vga1],
|
467
|
+
0x02 => [:composite],
|
468
|
+
0x03 => [:svideo],
|
469
|
+
0x06 => [:hdmi, :dvi],
|
470
|
+
0x07 => [:viewer]
|
471
|
+
},
|
472
|
+
0x02 => {
|
473
|
+
0x01 => [:vga2, :dvi_a, :rgbhv],
|
474
|
+
0x04 => [:component2],
|
475
|
+
0x06 => [:display_port, :hdmi2],
|
476
|
+
0x07 => [:lan]
|
477
|
+
},
|
478
|
+
0x03 => {
|
479
|
+
0x04 => [:component, :component1]
|
480
|
+
}
|
481
|
+
}
|
482
|
+
def process_input_state(data, command)
|
483
|
+
logger.debug "-- NEC projector sent a response to an input state command"
|
484
|
+
|
485
|
+
|
486
|
+
return if self[:power] == Off # no point doing anything here if the projector is off
|
487
|
+
|
488
|
+
self[:input_selected] = INPUT_MAP[data[-15]][data[-14]]
|
489
|
+
self[:input] = self[:input_selected].nil? ? :unknown : self[:input_selected][0]
|
490
|
+
if data[-17] == 0x01
|
491
|
+
command[:delay_on_receive] = 3000 # still processing signal
|
492
|
+
status_input
|
493
|
+
else
|
494
|
+
status_mute # get mute status one signal has settled
|
495
|
+
end
|
496
|
+
|
497
|
+
logger.debug "The input selected was: #{self[:input_selected][0]}"
|
498
|
+
|
499
|
+
#
|
500
|
+
# Notify of bad input selection for debugging
|
501
|
+
# We ensure at the very least power state and input are always correct
|
502
|
+
#
|
503
|
+
if !self[:input_selected].include?(self[:target_input]) && !self[:input_stable]
|
504
|
+
if self[:target_input].nil?
|
505
|
+
self[:target_input] = self[:input_selected][0]
|
506
|
+
self[:input_stable] = true
|
507
|
+
else
|
508
|
+
switch_to(self[:target_input])
|
509
|
+
logger.debug "-- NEC input state may not be correct, desired: #{self[:target_input]} current: #{self[:input_selected]}"
|
510
|
+
end
|
511
|
+
else
|
512
|
+
self[:input_stable] = true
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
|
517
|
+
#
|
518
|
+
# Check the input switching command was successful
|
519
|
+
#
|
520
|
+
def process_input_switch(data, req)
|
521
|
+
logger.debug "-- NEC projector responded to switch input command"
|
522
|
+
|
523
|
+
if data[-2] != 0xFF
|
524
|
+
status_input # Double check with a status update
|
525
|
+
return true
|
526
|
+
end
|
527
|
+
|
528
|
+
logger.debug "-- NEC projector failed to switch input with command: #{byte_to_hex(req)}"
|
529
|
+
return false # retry the command
|
530
|
+
end
|
531
|
+
|
532
|
+
|
533
|
+
#
|
534
|
+
# Process the mute state response
|
535
|
+
#
|
536
|
+
def process_mute_state(data, command)
|
537
|
+
logger.debug "-- NEC projector responded to mute state command"
|
538
|
+
|
539
|
+
self[:picture_mute] = data[-17] == 0x01
|
540
|
+
self[:audio_mute] = data[-16] == 0x01
|
541
|
+
self[:onscreen_mute] = data[-15] == 0x01
|
542
|
+
|
543
|
+
#if !self[:onscreen_mute] && self[:power]
|
544
|
+
#
|
545
|
+
# Always mute onscreen
|
546
|
+
#
|
547
|
+
# mute_onscreen
|
548
|
+
#end
|
549
|
+
|
550
|
+
self[:mute] = data[-17] == 0x01 # Same as picture mute
|
551
|
+
end
|
552
|
+
|
553
|
+
|
554
|
+
#
|
555
|
+
# Process projector information response
|
556
|
+
# lamp1 hours + filter hours
|
557
|
+
#
|
558
|
+
def process_projector_information(data, command)
|
559
|
+
logger.debug "-- NEC projector sent a response to a projector information command"
|
560
|
+
|
561
|
+
lamp = 0
|
562
|
+
filter = 0
|
563
|
+
|
564
|
+
#
|
565
|
+
# get lamp usage
|
566
|
+
#
|
567
|
+
shift = 0
|
568
|
+
data[87..90].each do |byte|
|
569
|
+
lamp += byte << shift
|
570
|
+
shift += 8
|
571
|
+
end
|
572
|
+
|
573
|
+
#
|
574
|
+
# get filter usage
|
575
|
+
#
|
576
|
+
shift = 0
|
577
|
+
data[91..94].each do |byte|
|
578
|
+
filter += byte << shift
|
579
|
+
shift += 8
|
580
|
+
end
|
581
|
+
|
582
|
+
self[:lamp_usage] = [lamp / 3600] # Lamp usage in hours
|
583
|
+
self[:filter_usage] = [filter / 3600]
|
584
|
+
end
|
585
|
+
|
586
|
+
|
587
|
+
#
|
588
|
+
# provide all the error information required
|
589
|
+
#
|
590
|
+
ERROR_CODES = [{
|
591
|
+
0b1 => "Lamp cover error",
|
592
|
+
0b10 => "Temperature error (Bimetal)",
|
593
|
+
#0b100 == not used
|
594
|
+
0b1000 => "Fan Error",
|
595
|
+
0b10000 => "Fan Error",
|
596
|
+
0b100000 => "Power Error",
|
597
|
+
0b1000000 => "Lamp Error",
|
598
|
+
0b10000000 => "Lamp has reached its end of life"
|
599
|
+
}, {
|
600
|
+
0b1 => "Lamp has been used beyond its limit",
|
601
|
+
0b10 => "Formatter error",
|
602
|
+
0b100 => "Lamp no.2 Error"
|
603
|
+
}, {
|
604
|
+
#0b1 => "not used",
|
605
|
+
0b10 => "FPGA error",
|
606
|
+
0b100 => "Temperature error (Sensor)",
|
607
|
+
0b1000 => "Lamp housing error",
|
608
|
+
0b10000 => "Lamp data error",
|
609
|
+
0b100000 => "Mirror cover error",
|
610
|
+
0b1000000 => "Lamp no.2 has reached its end of life",
|
611
|
+
0b10000000 => "Lamp no.2 has been used beyond its limit"
|
612
|
+
}, {
|
613
|
+
0b1 => "Lamp no.2 housing error",
|
614
|
+
0b10 => "Lamp no.2 data error",
|
615
|
+
0b100 => "High temperature due to dust pile-up",
|
616
|
+
0b1000 => "A foreign object sensor error"
|
617
|
+
}]
|
618
|
+
def process_error_status(data, command)
|
619
|
+
logger.debug "-- NEC projector sent a response to an error status command"
|
620
|
+
|
621
|
+
errors = []
|
622
|
+
error = data[5..8]
|
623
|
+
error.each_index do |byte_no|
|
624
|
+
if error[byte_no] > 0 # run throught each byte
|
625
|
+
ERROR_CODES[byte_no].each_key do |key| # if error indicated run though each key
|
626
|
+
if (key & error[byte_no]) > 0 # check individual bits
|
627
|
+
errors << ERROR_CODES[byte_no][key] # add errors to the error list
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
end
|
632
|
+
self[:error] = errors
|
633
|
+
end
|
634
|
+
|
635
|
+
|
636
|
+
#
|
637
|
+
# For commands that require a checksum (volume, zoom)
|
638
|
+
#
|
639
|
+
def send_checksum(command, options = {})
|
640
|
+
#
|
641
|
+
# Prepare command for sending
|
642
|
+
#
|
643
|
+
command = str_to_array(hex_to_byte(command)) unless command.is_a?(Array)
|
644
|
+
check = 0
|
645
|
+
command.each do |byte| # Loop through the first to second last element
|
646
|
+
check = (check + byte) & 0xFF
|
647
|
+
end
|
648
|
+
command << check
|
649
|
+
send(command, options)
|
650
|
+
end
|
651
|
+
|
652
|
+
def check_checksum(data)
|
653
|
+
check = 0
|
654
|
+
data[0..-2].each do |byte| # Loop through the first to second last element
|
655
|
+
check = (check + byte) & 0xFF
|
656
|
+
end
|
657
|
+
return check == data[-1] # Check the check sum equals the last element
|
658
|
+
end
|
647
659
|
end
|
648
660
|
|