aca-device-modules 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +165 -0
- data/README.md +3 -0
- data/aca-device-modules.gemspec +21 -0
- data/lib/aca-device-modules.rb +2 -0
- data/lib/aca-device-modules/engine.rb +8 -0
- data/lib/aca-device-modules/version.rb +3 -0
- data/modules/aca/pc_control.rb +142 -0
- data/modules/axis/camera/vapix.rb +280 -0
- data/modules/biamp/nexia.rb +125 -0
- data/modules/bss/blu100.rb +265 -0
- data/modules/clipsal/c_bus.rb +256 -0
- data/modules/epson/projector/esc_vp21.rb +248 -0
- data/modules/extron/mixer/dmp44.rb +195 -0
- data/modules/extron/mixer/dmp64.rb +212 -0
- data/modules/extron/switcher/dxp.rb +249 -0
- data/modules/global_cache/gc100.rb +167 -0
- data/modules/kramer/switcher/vs_hdmi.rb +143 -0
- data/modules/panasonic/camera/he50.rb +346 -0
- data/modules/panasonic/projector/pj_link.rb +266 -0
- data/modules/samsung/displays/md_series.rb +256 -0
- data/modules/vaddio/camera/clear_view_ptz_telnet.rb +138 -0
- metadata +94 -0
@@ -0,0 +1,248 @@
|
|
1
|
+
module Epson; end
|
2
|
+
module Epson::Projector; end
|
3
|
+
|
4
|
+
#
|
5
|
+
# Port: 3629
|
6
|
+
#
|
7
|
+
class Epson::Projector::EscVp21
|
8
|
+
include ::Orchestrator::Constants
|
9
|
+
include ::Orchestrator::Transcoder
|
10
|
+
|
11
|
+
def on_load
|
12
|
+
#config({
|
13
|
+
# tokenize: true,
|
14
|
+
# delimiter: ":"
|
15
|
+
#})
|
16
|
+
|
17
|
+
self[:power] = false
|
18
|
+
self[:stable_state] = true # Stable by default (allows manual on and off)
|
19
|
+
|
20
|
+
# Meta data for inquiring interfaces
|
21
|
+
self[:type] = :projector
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_update
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def connected
|
29
|
+
# Have to init comms
|
30
|
+
send("ESC/VP.net\x10\x03\x00\x00\x00\x00")
|
31
|
+
do_poll
|
32
|
+
@polling_timer = schedule.every('52s', method(:do_poll))
|
33
|
+
end
|
34
|
+
|
35
|
+
def disconnected
|
36
|
+
self[:power] = false
|
37
|
+
@polling_timer.cancel unless @polling_timer.nil?
|
38
|
+
@polling_timer = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
#
|
44
|
+
# Power commands
|
45
|
+
#
|
46
|
+
def power(state, opt = nil)
|
47
|
+
self[:stable_state] = false
|
48
|
+
if is_affirmative?(state)
|
49
|
+
self[:power_target] = On
|
50
|
+
do_send(:PWR, :ON, {:timeout => 40000, :name => :power})
|
51
|
+
logger.debug "-- epson Proj, requested to power on"
|
52
|
+
do_send(:PWR, :name => :power_state)
|
53
|
+
else
|
54
|
+
self[:power_target] = Off
|
55
|
+
do_send(:PWR, :OFF, {:timeout => 10000, :name => :power})
|
56
|
+
logger.debug "-- epson Proj, requested to power off"
|
57
|
+
do_send(:PWR, :name => :power_state)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def power?(options = {}, &block)
|
62
|
+
options[:emit] = block if block_given?
|
63
|
+
options[:name] = :power_state
|
64
|
+
do_send(:PWR, options)
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
#
|
70
|
+
# Input selection
|
71
|
+
#
|
72
|
+
INPUTS = {
|
73
|
+
:hdmi => 0x30 # TODO:: Might need to have a setting for configuring this
|
74
|
+
}
|
75
|
+
INPUTS.merge!(INPUTS.invert)
|
76
|
+
|
77
|
+
|
78
|
+
def switch_to(input)
|
79
|
+
input = input.to_sym
|
80
|
+
return unless INPUTS.has_key? input
|
81
|
+
|
82
|
+
do_send(:SOURCE, INPUTS[input].to_s(16), {:name => :inpt_source})
|
83
|
+
do_send(:SOURCE, {:name => :inpt_query})
|
84
|
+
|
85
|
+
logger.debug "-- epson LCD, requested to switch to: #{input}"
|
86
|
+
|
87
|
+
self[:input] = input # for a responsive UI
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
#
|
93
|
+
# Volume commands are sent using the inpt command
|
94
|
+
#
|
95
|
+
def volume(vol, options = {})
|
96
|
+
vol = vol.to_i
|
97
|
+
vol = 0 if vol < 0
|
98
|
+
vol = 255 if vol > 255
|
99
|
+
|
100
|
+
do_send(:VOL, vol, options)
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
#
|
105
|
+
# Mute Audio and Video
|
106
|
+
#
|
107
|
+
def mute
|
108
|
+
logger.debug "-- epson Proj, requested to mute"
|
109
|
+
do_send(:MUTE, :ON, {:name => :video_mute}) # Audio + Video
|
110
|
+
do_send(:MUTE) # request status
|
111
|
+
end
|
112
|
+
|
113
|
+
def unmute
|
114
|
+
logger.debug "-- epson Proj, requested to mute"
|
115
|
+
do_send(:MUTE, :OFF, {:name => :video_mute})
|
116
|
+
do_send(:MUTE)
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
ERRORS = {
|
121
|
+
0 => '00: no error'.freeze,
|
122
|
+
1 => '01: fan error'.freeze,
|
123
|
+
3 => '03: lamp failure at power on'.freeze,
|
124
|
+
4 => '04: high internal temperature'.freeze,
|
125
|
+
6 => '06: lamp error'.freeze,
|
126
|
+
7 => '07: lamp cover door open'.freeze,
|
127
|
+
8 => '08: cinema filter error'.freeze,
|
128
|
+
9 => '09: capacitor is disconnected'.freeze,
|
129
|
+
10 => '0A: auto iris error'.freeze,
|
130
|
+
11 => '0B: subsystem error'.freeze,
|
131
|
+
12 => '0C: low air flow error'.freeze,
|
132
|
+
13 => '0D: air flow sensor error'.freeze,
|
133
|
+
14 => '0E: ballast power supply error'.freeze,
|
134
|
+
15 => '0F: shutter error'.freeze,
|
135
|
+
16 => '10: peltiert cooling error'.freeze,
|
136
|
+
17 => '11: pump cooling error'.freeze,
|
137
|
+
18 => '12: static iris error'.freeze,
|
138
|
+
19 => '13: power supply unit error'.freeze,
|
139
|
+
20 => '14: exhaust shutter error'.freeze,
|
140
|
+
21 => '15: obstacle detection error'.freeze,
|
141
|
+
22 => '16: IF board discernment error'.freeze
|
142
|
+
}
|
143
|
+
|
144
|
+
#
|
145
|
+
# epson Response code
|
146
|
+
#
|
147
|
+
def received(data, resolve, command) # Data is default received as a string
|
148
|
+
logger.debug "epson Proj sent: #{data}"
|
149
|
+
|
150
|
+
if data == ':'
|
151
|
+
return :success
|
152
|
+
end
|
153
|
+
|
154
|
+
data = data.split(/=|\r:/)
|
155
|
+
case data[0].to_sym
|
156
|
+
when :ERR
|
157
|
+
# Lookup error!
|
158
|
+
if data[1].nil?
|
159
|
+
warning = "Epson PJ sent error response"
|
160
|
+
warning << " for #{command[:data].inspect}" if command
|
161
|
+
logger.warn warning
|
162
|
+
return :abort
|
163
|
+
else
|
164
|
+
code = data[1].to_i(16)
|
165
|
+
self[:last_error] = ERRORS[code] || "#{data[1]}: unknown error code #{code}"
|
166
|
+
logger.warn "Epson PJ error was #{self[:last_error]}"
|
167
|
+
return :success
|
168
|
+
end
|
169
|
+
when :PWR
|
170
|
+
state = data[1].to_i
|
171
|
+
self[:power] = state < 3
|
172
|
+
self[:warming] = state == 2
|
173
|
+
self[:cooling] = state == 3
|
174
|
+
if self[:warming] || self[:cooling]
|
175
|
+
schedule.in('5s') do
|
176
|
+
power?({:priority => 0})
|
177
|
+
end
|
178
|
+
end
|
179
|
+
if !self[:stable_state] && self[:power_target] == self[:power]
|
180
|
+
self[:stable_state] = true
|
181
|
+
end
|
182
|
+
|
183
|
+
when :MUTE
|
184
|
+
self[:mute] = data[1] == 'ON'
|
185
|
+
when :VOL
|
186
|
+
self[:volume] = data[1].to_i
|
187
|
+
when :LAMP
|
188
|
+
self[:lamp] = data[1].to_i
|
189
|
+
when :SOURCE
|
190
|
+
self[:source] = INPUTS[data[1].to_i(16)] || :unknown
|
191
|
+
end
|
192
|
+
|
193
|
+
:success
|
194
|
+
end
|
195
|
+
|
196
|
+
def inspect_error
|
197
|
+
do_send(:ERR, priority: 0)
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
protected
|
202
|
+
|
203
|
+
|
204
|
+
def do_poll(*args)
|
205
|
+
power?({:priority => 0}) do
|
206
|
+
if self[:power]
|
207
|
+
if self[:stable_state] == false && self[:power_target] == Off
|
208
|
+
power(Off)
|
209
|
+
else
|
210
|
+
self[:stable_state] = true
|
211
|
+
do_send(:SOURCE, {
|
212
|
+
:name => :inpt_query,
|
213
|
+
:priority => 0
|
214
|
+
})
|
215
|
+
do_send(:MUTE, {
|
216
|
+
:name => :mute_query,
|
217
|
+
:priority => 0
|
218
|
+
})
|
219
|
+
do_send(:VOL, {
|
220
|
+
:name => :vol_query,
|
221
|
+
:priority => 0
|
222
|
+
})
|
223
|
+
end
|
224
|
+
elsif self[:stable_state] == false
|
225
|
+
if self[:power_target] == On
|
226
|
+
power(On)
|
227
|
+
else
|
228
|
+
self[:stable_state] = true
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
do_send(:LAMP, {:priority => 0})
|
233
|
+
end
|
234
|
+
|
235
|
+
def do_send(command, param = nil, options = {})
|
236
|
+
if param.is_a? Hash
|
237
|
+
options = param
|
238
|
+
param = nil
|
239
|
+
end
|
240
|
+
|
241
|
+
if param.nil?
|
242
|
+
send("#{command}?\x0D", options)
|
243
|
+
else
|
244
|
+
send("#{command} #{param}\x0D", options)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module Extron; end
|
2
|
+
module Extron::Mixer; end
|
3
|
+
|
4
|
+
|
5
|
+
# :title:Extron DSP 44
|
6
|
+
#
|
7
|
+
# Status information avaliable:
|
8
|
+
# -----------------------------
|
9
|
+
#
|
10
|
+
# (built in)
|
11
|
+
# connected
|
12
|
+
#
|
13
|
+
# (module defined)
|
14
|
+
#
|
15
|
+
|
16
|
+
|
17
|
+
class Extron::Mixer::Dmp44
|
18
|
+
include ::Orchestrator::Constants
|
19
|
+
include ::Orchestrator::Transcoder
|
20
|
+
|
21
|
+
|
22
|
+
def on_load
|
23
|
+
#
|
24
|
+
# Setup constants
|
25
|
+
#
|
26
|
+
self[:output_volume_max] = 2168
|
27
|
+
self[:output_volume_min] = 1048
|
28
|
+
self[:mic_gain_max] = 2298
|
29
|
+
self[:mic_gain_min] = 1698
|
30
|
+
|
31
|
+
config({
|
32
|
+
:clear_queue_on_disconnect => true # Clear the queue as we may need to send login
|
33
|
+
})
|
34
|
+
end
|
35
|
+
|
36
|
+
def connected
|
37
|
+
device_ready
|
38
|
+
@polling_timer = schedule.every('2m') do
|
39
|
+
logger.debug "-- Extron Maintaining Connection"
|
40
|
+
send('Q', :priority => 0) # Low priority poll to maintain connection
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def disconnected
|
45
|
+
#
|
46
|
+
# Disconnected may be called without calling connected
|
47
|
+
# Hence the check if timer is nil here
|
48
|
+
#
|
49
|
+
@polling_timer.cancel unless @polling_timer.nil?
|
50
|
+
@polling_timer = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def call_preset(number)
|
55
|
+
if number < 0 || number > 32
|
56
|
+
number = 0 # Current configuration
|
57
|
+
end
|
58
|
+
send("#{number}.") # No Carriage return for presents
|
59
|
+
# Response: Rpr#{number}
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Input control
|
64
|
+
#
|
65
|
+
def adjust_gain(input, value) # \e == 0x1B == ESC key
|
66
|
+
input -= 1
|
67
|
+
do_send("\eG3000#{input}*#{value}AU")
|
68
|
+
# Response: DsG3000#{input}*#{value}
|
69
|
+
end
|
70
|
+
|
71
|
+
def adjust_gain_relative(input, value) # \e == 0x1B == ESC key
|
72
|
+
input -= 1
|
73
|
+
current = do_send("\eG3000#{input}AU", :emit => "mic#{input + 1}_gain")
|
74
|
+
do_send("\eG3000#{input}*#{current + (value * 10)}AU")
|
75
|
+
|
76
|
+
# Response: DsG3000#{input}*#{value}
|
77
|
+
end
|
78
|
+
|
79
|
+
def mute_input(input)
|
80
|
+
input -= 1
|
81
|
+
do_send("\eM3000#{input}*1AU")
|
82
|
+
# Response: DsM3000#{input}*1
|
83
|
+
end
|
84
|
+
|
85
|
+
def unmute_input(input)
|
86
|
+
input -= 1
|
87
|
+
do_send("\eM3000#{input}*0AU")
|
88
|
+
# Response: DsM3000#{input}*0
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
#
|
93
|
+
# Group control
|
94
|
+
#
|
95
|
+
def mute_group(group)
|
96
|
+
do_send("\eD#{group}*1GRPM")
|
97
|
+
# Response: GrpmD#{group}*+00001
|
98
|
+
end
|
99
|
+
|
100
|
+
def unmute_group(group)
|
101
|
+
do_send("\eD#{group}*0GRPM")
|
102
|
+
# Response: GrpmD#{group}*+00000
|
103
|
+
end
|
104
|
+
|
105
|
+
def volume(group, value) # \e == 0x1B == ESC key
|
106
|
+
do_send("\eD#{group}*#{value * 10}*GRPM")
|
107
|
+
# Response: GrpmD#{group}*#{value}*GRPM
|
108
|
+
end
|
109
|
+
|
110
|
+
def volume_relative(group, value) # \e == 0x1B == ESC key
|
111
|
+
|
112
|
+
if value < 0
|
113
|
+
value = -value
|
114
|
+
do_send("\eD#{group}*#{value * 10}-GRPM")
|
115
|
+
else
|
116
|
+
do_send("\eD#{group}*#{value * 10}+GRPM")
|
117
|
+
end
|
118
|
+
# Response: GrpmD#{group}*#{value}*GRPM
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
def response_delimiter
|
123
|
+
[0x0D, 0x0A] # Used to interpret the end of a message
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Sends copyright information
|
128
|
+
# Then sends password prompt
|
129
|
+
#
|
130
|
+
def received(data, resolve, command)
|
131
|
+
logger.debug "Extron DSP 44 sent #{data}"
|
132
|
+
|
133
|
+
if command.nil? && data =~ /Copyright/i
|
134
|
+
device_ready
|
135
|
+
else
|
136
|
+
case data[0..2].to_sym
|
137
|
+
when :Grp # Mute or Volume
|
138
|
+
data = data.split('*')
|
139
|
+
if data[1][0] == '+' # mute
|
140
|
+
self["ouput#{data[0][5..-1].to_i}_mute"] = data[1][-1] == '1' # 1 == true
|
141
|
+
else
|
142
|
+
self["ouput#{data[0][5..-1].to_i}_volume"] = data[1].to_i
|
143
|
+
end
|
144
|
+
when :DsG # Input gain
|
145
|
+
self["input#{data[7].to_i + 1}_gain"] = data[9..-1].to_i
|
146
|
+
when :DsM # Input Mute
|
147
|
+
self["input#{data[7].to_i + 1}_mute"] = data[-1] == '1' # 1 == true
|
148
|
+
when :Rpr # Preset called
|
149
|
+
logger.debug "Extron DSP called preset #{data[3..-1]}"
|
150
|
+
else
|
151
|
+
if data == 'E22' # Busy! We should retry this one
|
152
|
+
command[:delay_on_receive] = 1 unless command.nil?
|
153
|
+
return :failed
|
154
|
+
elsif data[0] == 'E'
|
155
|
+
logger.info "Extron Error #{ERRORS[data[1..2].to_i]}"
|
156
|
+
logger.info "- for command #{command[:data]}" unless command.nil?
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
return :success
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
|
168
|
+
ERRORS = {
|
169
|
+
10 => 'Invalid command',
|
170
|
+
11 => 'Invalid preset',
|
171
|
+
12 => 'Invalid port number',
|
172
|
+
13 => 'Invalid parameter (number is out of range)',
|
173
|
+
14 => 'Not valid for this configuration',
|
174
|
+
17 => 'System timed out',
|
175
|
+
23 => 'Checksum error (for file uploads)',
|
176
|
+
24 => 'Privilege violation',
|
177
|
+
25 => 'Device is not present',
|
178
|
+
26 => 'Maximum connections exceeded',
|
179
|
+
27 => 'Invalid event number',
|
180
|
+
28 => 'Bad filename or file not found'
|
181
|
+
}
|
182
|
+
|
183
|
+
|
184
|
+
def device_ready
|
185
|
+
do_send("\e3CV") # Verbose mode and tagged responses
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
|
190
|
+
|
191
|
+
def do_send(data, options = {})
|
192
|
+
send(data << 0x0D, options)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
@@ -0,0 +1,212 @@
|
|
1
|
+
module Extron; end
|
2
|
+
module Extron::Mixer; end
|
3
|
+
|
4
|
+
|
5
|
+
# :title:Extron DSP
|
6
|
+
#
|
7
|
+
# Status information avaliable:
|
8
|
+
# -----------------------------
|
9
|
+
#
|
10
|
+
# (built in)
|
11
|
+
# connected
|
12
|
+
#
|
13
|
+
# (module defined)
|
14
|
+
#
|
15
|
+
#
|
16
|
+
#
|
17
|
+
# Volume outputs
|
18
|
+
# 60000 == volume 1
|
19
|
+
# 60003 == volume 4
|
20
|
+
#
|
21
|
+
# Pre-mix gain inputs
|
22
|
+
# 40100 == Mic1
|
23
|
+
# 40105 == Mic6
|
24
|
+
#
|
25
|
+
|
26
|
+
|
27
|
+
class Extron::Mixer::Dmp64
|
28
|
+
include ::Orchestrator::Constants
|
29
|
+
include ::Orchestrator::Transcoder
|
30
|
+
|
31
|
+
|
32
|
+
def on_load
|
33
|
+
#
|
34
|
+
# Setup constants
|
35
|
+
#
|
36
|
+
self[:output_volume_max] = 2168
|
37
|
+
self[:output_volume_min] = 1048
|
38
|
+
self[:mic_gain_max] = 2298
|
39
|
+
self[:mic_gain_min] = 1698
|
40
|
+
|
41
|
+
config({
|
42
|
+
:clear_queue_on_disconnect => true # Clear the queue as we may need to send login
|
43
|
+
})
|
44
|
+
end
|
45
|
+
|
46
|
+
def connected
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def disconnected
|
51
|
+
#
|
52
|
+
# Disconnected may be called without calling connected
|
53
|
+
# Hence the check if timer is nil here
|
54
|
+
#
|
55
|
+
@polling_timer.cancel unless @polling_timer.nil?
|
56
|
+
@polling_timer = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def call_preset(number)
|
61
|
+
if number < 0 || number > 32
|
62
|
+
number = 0 # Current configuration
|
63
|
+
end
|
64
|
+
send("#{number}.") # No Carriage return for presents
|
65
|
+
# Response: Rpr#{number}
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Input control
|
70
|
+
#
|
71
|
+
def adjust_gain(mic, value) # \e == 0x1B == ESC key
|
72
|
+
do_send("\eG4010#{mic}*#{value}AU")
|
73
|
+
# Response: DsG4010#{mic}*#{value}
|
74
|
+
end
|
75
|
+
|
76
|
+
def adjust_gain_relative(mic, value) # \e == 0x1B == ESC key
|
77
|
+
current = do_send("\eG4010#{mic}AU", :emit => "mic#{mic}_gain")
|
78
|
+
do_send("\eG4010#{mic}*#{current + (value * 10)}AU")
|
79
|
+
|
80
|
+
# Response: DsG4010#{mic}*#{value}
|
81
|
+
end
|
82
|
+
|
83
|
+
def mute_mic(mic)
|
84
|
+
do_send("\eM4000#{mic}*1AU") # 4000 (input gain), 4010 (pre-mixer gain)
|
85
|
+
# Response: DsM4010#{mic}*1
|
86
|
+
end
|
87
|
+
|
88
|
+
def unmute_mic(mic)
|
89
|
+
do_send("\eM4000#{mic}*0AU")
|
90
|
+
# Response: DsM4010#{mic}*0
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
#
|
95
|
+
# Output control
|
96
|
+
#
|
97
|
+
def mute_group(group)
|
98
|
+
do_send("\eD#{group}*1GRPM", :group_type => :mute)
|
99
|
+
# Response: GrpmD#{group}*+00001
|
100
|
+
end
|
101
|
+
|
102
|
+
def unmute_group(group)
|
103
|
+
do_send("\eD#{group}*0GRPM", :group_type => :mute)
|
104
|
+
# Response: GrpmD#{group}*+00000
|
105
|
+
end
|
106
|
+
|
107
|
+
def volume(group, value) # \e == 0x1B == ESC key
|
108
|
+
do_send("\eD#{group}*#{value}GRPM", :group_type => :volume)
|
109
|
+
# Response: GrpmD#{group}*#{value}*GRPM
|
110
|
+
end
|
111
|
+
|
112
|
+
def group_status(group, type)
|
113
|
+
do_send("\eD#{group}GRPM", :group_type => type)
|
114
|
+
end
|
115
|
+
|
116
|
+
def volume_relative(group, value) # \e == 0x1B == ESC key
|
117
|
+
if value < 0
|
118
|
+
value = -value
|
119
|
+
do_send("\eD#{group}*#{value}-GRPM")
|
120
|
+
else
|
121
|
+
do_send("\eD#{group}*#{value}+GRPM")
|
122
|
+
end
|
123
|
+
# Response: GrpmD#{group}*#{value}*GRPM
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
def response_delimiter
|
128
|
+
[0x0D, 0x0A] # Used to interpret the end of a message
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
# Sends copyright information
|
133
|
+
# Then sends password prompt
|
134
|
+
#
|
135
|
+
def received(data, resolve, command)
|
136
|
+
logger.debug "Extron DSP sent #{data}"
|
137
|
+
|
138
|
+
if command.nil? && data =~ /Copyright/i
|
139
|
+
pass = setting(:password)
|
140
|
+
if pass.nil?
|
141
|
+
device_ready
|
142
|
+
else
|
143
|
+
do_send(pass) # Password set
|
144
|
+
end
|
145
|
+
elsif data =~ /Login/i
|
146
|
+
device_ready
|
147
|
+
else
|
148
|
+
case data[0..2].to_sym
|
149
|
+
when :Grp # Mute or Volume
|
150
|
+
data = data.split('*')
|
151
|
+
if command.present? && command[:group_type] == :mute
|
152
|
+
self["ouput#{data[0][5..-1].to_i}_mute"] = data[1][-1] == '1' # 1 == true
|
153
|
+
elsif command.present? && command[:group_type] == :volume
|
154
|
+
self["ouput#{data[0][5..-1].to_i}_volume"] = data[1].to_i
|
155
|
+
else
|
156
|
+
return :failed
|
157
|
+
end
|
158
|
+
when :DsG # Mic gain
|
159
|
+
self["mic#{data[7]}_gain"] = data[9..-1].to_i
|
160
|
+
when :DsM # Mic Mute
|
161
|
+
self["mic#{data[7]}_mute"] = data[-1] == '1' # 1 == true
|
162
|
+
when :Rpr # Preset called
|
163
|
+
logger.debug "Extron DSP called preset #{data[3..-1]}"
|
164
|
+
else
|
165
|
+
if data == 'E22' # Busy! We should retry this one
|
166
|
+
command[:delay_on_receive] = 1 unless command.nil?
|
167
|
+
return :failed
|
168
|
+
elsif data[0] == 'E'
|
169
|
+
logger.info "Extron Error #{ERRORS[data[1..2].to_i]}"
|
170
|
+
logger.info "- for command #{command[:data]}" unless command.nil?
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
return :success
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
|
182
|
+
ERRORS = {
|
183
|
+
1 => 'Invalid input number (number is too large)',
|
184
|
+
12 => 'Invalid port number',
|
185
|
+
13 => 'Invalid parameter (number is out of range)',
|
186
|
+
14 => 'Not valid for this configuration',
|
187
|
+
17 => 'System timed out',
|
188
|
+
23 => 'Checksum error (for file uploads)',
|
189
|
+
24 => 'Privilege violation',
|
190
|
+
25 => 'Device is not present',
|
191
|
+
26 => 'Maximum connections exceeded',
|
192
|
+
27 => 'Invalid event number',
|
193
|
+
28 => 'Bad filename or file not found'
|
194
|
+
}
|
195
|
+
|
196
|
+
|
197
|
+
def device_ready
|
198
|
+
do_send("\e3CV") # Verbose mode and tagged responses
|
199
|
+
@polling_timer = schedule.every('2m') do
|
200
|
+
logger.debug "-- Extron Maintaining Connection"
|
201
|
+
send('Q', :priority => 0) # Low priority poll to maintain connection
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
|
208
|
+
def do_send(data, options = {})
|
209
|
+
send(data << 0x0D, options)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|