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,249 @@
|
|
1
|
+
module Extron; end
|
2
|
+
module Extron::Switcher; end
|
3
|
+
|
4
|
+
|
5
|
+
# :title:Extron Digital Matrix Switchers
|
6
|
+
# NOTE:: Very similar to the XTP!! Update both
|
7
|
+
#
|
8
|
+
# Status information avaliable:
|
9
|
+
# -----------------------------
|
10
|
+
#
|
11
|
+
# (built in)
|
12
|
+
# connected
|
13
|
+
#
|
14
|
+
# (module defined)
|
15
|
+
# video_inputs
|
16
|
+
# video_outputs
|
17
|
+
# audio_inputs
|
18
|
+
# audio_outputs
|
19
|
+
#
|
20
|
+
# video1 => input (video)
|
21
|
+
# video2
|
22
|
+
# video3
|
23
|
+
# video1_muted => true
|
24
|
+
#
|
25
|
+
# audio1 => input
|
26
|
+
# audio1_muted => true
|
27
|
+
#
|
28
|
+
#
|
29
|
+
# (Settings)
|
30
|
+
# password
|
31
|
+
#
|
32
|
+
|
33
|
+
|
34
|
+
class Extron::Switcher::Dxp
|
35
|
+
include ::Orchestrator::Constants
|
36
|
+
include ::Orchestrator::Transcoder
|
37
|
+
|
38
|
+
|
39
|
+
def on_load
|
40
|
+
#
|
41
|
+
# Setup constants
|
42
|
+
#
|
43
|
+
defaults({
|
44
|
+
:wait => false
|
45
|
+
})
|
46
|
+
config({
|
47
|
+
:clear_queue_on_disconnect => true # Clear the queue as we may need to send login
|
48
|
+
})
|
49
|
+
end
|
50
|
+
|
51
|
+
def connected
|
52
|
+
@polling_timer = schedule.every('2m') do
|
53
|
+
logger.debug "-- Extron Maintaining Connection"
|
54
|
+
send('Q', :priority => 0) # Low priority poll to maintain connection
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def disconnected
|
59
|
+
#
|
60
|
+
# Disconnected may be called without calling connected
|
61
|
+
# Hence the check if timer is nil here
|
62
|
+
#
|
63
|
+
@polling_timer.cancel unless @polling_timer.nil?
|
64
|
+
@polling_timer = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def direct(string)
|
69
|
+
send(string, :wait => false)
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
#
|
74
|
+
# No need to wait as commands can be chained
|
75
|
+
#
|
76
|
+
def switch(map)
|
77
|
+
map.each do |input, outputs|
|
78
|
+
input = input.to_s if input.is_a?(Symbol)
|
79
|
+
input = input.to_i if input.is_a?(String)
|
80
|
+
|
81
|
+
outputs = [outputs] unless outputs.is_a?(Array)
|
82
|
+
command = ''
|
83
|
+
outputs.each do |output|
|
84
|
+
command += "#{input}*#{output}!"
|
85
|
+
end
|
86
|
+
send(command)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def switch_video(map)
|
91
|
+
map.each do |input, outputs|
|
92
|
+
input = input.to_s if input.is_a?(Symbol)
|
93
|
+
input = input.to_i if input.is_a?(String)
|
94
|
+
|
95
|
+
|
96
|
+
outputs = [outputs] unless outputs.is_a?(Array)
|
97
|
+
command = ''
|
98
|
+
outputs.each do |output|
|
99
|
+
command += "#{input}*#{output}%"
|
100
|
+
end
|
101
|
+
send(command)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def switch_audio(map)
|
106
|
+
map.each do |input, outputs|
|
107
|
+
input = input.to_s if input.is_a?(Symbol)
|
108
|
+
input = input.to_i if input.is_a?(String)
|
109
|
+
|
110
|
+
outputs = [outputs] unless outputs.is_a?(Array)
|
111
|
+
command = ''
|
112
|
+
outputs.each do |output|
|
113
|
+
command += "#{input}*#{output}$"
|
114
|
+
end
|
115
|
+
send(command)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def mute_video(outputs)
|
120
|
+
outputs = [outputs] unless outputs.is_a?(Array)
|
121
|
+
command = ''
|
122
|
+
outputs.each do |output|
|
123
|
+
command += "#{output}*1B"
|
124
|
+
end
|
125
|
+
send(command)
|
126
|
+
end
|
127
|
+
|
128
|
+
def unmute_video(outputs)
|
129
|
+
outputs = [outputs] unless outputs.is_a?(Array)
|
130
|
+
command = ''
|
131
|
+
outputs.each do |output|
|
132
|
+
command += "#{output}*0B"
|
133
|
+
end
|
134
|
+
send(command)
|
135
|
+
end
|
136
|
+
|
137
|
+
def mute_audio(outputs)
|
138
|
+
outputs = [outputs] unless outputs.is_a?(Array)
|
139
|
+
command = ''
|
140
|
+
outputs.each do |output|
|
141
|
+
command += "#{output}*1Z"
|
142
|
+
end
|
143
|
+
send(command)
|
144
|
+
end
|
145
|
+
|
146
|
+
def unmute_audio(outputs)
|
147
|
+
outputs = [outputs] unless outputs.is_a?(Array)
|
148
|
+
command = ''
|
149
|
+
outputs.each do |output|
|
150
|
+
command += "#{output}*0Z"
|
151
|
+
end
|
152
|
+
send(command)
|
153
|
+
end
|
154
|
+
|
155
|
+
def set_preset(number)
|
156
|
+
send("#{number},")
|
157
|
+
end
|
158
|
+
|
159
|
+
def recall_preset(number)
|
160
|
+
send("#{number}.")
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
#def response_delimiter
|
165
|
+
# [0x0D, 0x0A] # Used to interpret the end of a message
|
166
|
+
#end
|
167
|
+
|
168
|
+
|
169
|
+
#
|
170
|
+
# Sends copyright information
|
171
|
+
# Then sends password prompt
|
172
|
+
#
|
173
|
+
def received(data, resolve, command)
|
174
|
+
logger.debug "Extron Matrix sent #{data}"
|
175
|
+
|
176
|
+
if command.nil? && data =~ /Copyright/i
|
177
|
+
pass = setting(:password)
|
178
|
+
if pass.nil?
|
179
|
+
device_ready
|
180
|
+
else
|
181
|
+
do_send(pass) # Password set
|
182
|
+
end
|
183
|
+
elsif data =~ /Login/i
|
184
|
+
device_ready
|
185
|
+
elsif command.present? && command[:command] == :information
|
186
|
+
data = data.split(' ')
|
187
|
+
video = data[0][1..-1].split('X')
|
188
|
+
self[:video_inputs] = video[0].to_i
|
189
|
+
self[:video_outputs] = video[1].to_i
|
190
|
+
|
191
|
+
audio = data[1][1..-1].split('X')
|
192
|
+
self[:audio_inputs] = audio[0].to_i
|
193
|
+
self[:audio_outputs] = audio[1].to_i
|
194
|
+
else
|
195
|
+
case data[0..1].to_sym
|
196
|
+
when :Am # Audio mute
|
197
|
+
data = data[3..-1].split('*')
|
198
|
+
self["audio#{data[0].to_i}_muted"] = data[1] == '1'
|
199
|
+
when :Vm # Video mute
|
200
|
+
data = data[3..-1].split('*')
|
201
|
+
self["video#{data[0].to_i}_muted"] = data[1] == '1'
|
202
|
+
when :In # Input to all outputs
|
203
|
+
data = data[2..-1].split(' ')
|
204
|
+
input = data[0].to_i
|
205
|
+
if data[1] =~ /(All|RGB|Vid)/
|
206
|
+
for i in 1..self[:video_outputs]
|
207
|
+
self["video#{i}"] = input
|
208
|
+
end
|
209
|
+
end
|
210
|
+
if data[1] =~ /(All|Aud)/
|
211
|
+
for i in 1..self[:audio_outputs]
|
212
|
+
self["audio#{i}"] = input
|
213
|
+
end
|
214
|
+
end
|
215
|
+
when :Ou # Output x to input y
|
216
|
+
data = data[3..-1].split(' ')
|
217
|
+
output = data[0].to_i
|
218
|
+
input = data[1][2..-1].to_i
|
219
|
+
if data[2] =~ /(All|RGB|Vid)/
|
220
|
+
self["video#{output}"] = input
|
221
|
+
end
|
222
|
+
if data[2] =~ /(All|Aud)/
|
223
|
+
self["audio#{output}"] = input
|
224
|
+
end
|
225
|
+
else
|
226
|
+
if data == 'E22' # Busy! We should retry this one
|
227
|
+
command[:delay_on_receive] = 1 unless command.nil?
|
228
|
+
return :failed
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
return :success
|
234
|
+
end
|
235
|
+
|
236
|
+
|
237
|
+
private
|
238
|
+
|
239
|
+
|
240
|
+
def device_ready
|
241
|
+
send("I", :wait => true, :command => :information)
|
242
|
+
do_send("\e3CV", :wait => true) # Verbose mode and tagged responses
|
243
|
+
end
|
244
|
+
|
245
|
+
def do_send(data, options = {})
|
246
|
+
send(data << 0x0D, options)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module GlobalCache; end
|
2
|
+
|
3
|
+
|
4
|
+
class GlobalCache::Gc100
|
5
|
+
include ::Orchestrator::Constants
|
6
|
+
|
7
|
+
|
8
|
+
def on_load
|
9
|
+
self[:num_relays] = 0
|
10
|
+
self[:num_ir] = 0
|
11
|
+
|
12
|
+
config({
|
13
|
+
tokenize: true,
|
14
|
+
delimiter: "\x0D"
|
15
|
+
})
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_update
|
19
|
+
self[:config_indexed] = false
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Config maps the GC100 into a linear set of ir and relays so models can be swapped in and out
|
24
|
+
# config => {:relay => {0 => '2:1',1 => '2:2',2 => '2:3',3 => '3:1'}} etc
|
25
|
+
#
|
26
|
+
def connected
|
27
|
+
unless self[:config_indexed]
|
28
|
+
self[:config] = {}
|
29
|
+
do_send('getdevices', :max_waits => 100)
|
30
|
+
end
|
31
|
+
|
32
|
+
@polling_timer = schedule.every('60s') do
|
33
|
+
logger.debug "-- Polling GC100"
|
34
|
+
do_send("get_NET,0:1", :priority => 0) # Low priority sent to maintain the connection
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def disconnected
|
39
|
+
@polling_timer.cancel unless @polling_timer.nil?
|
40
|
+
@polling_timer = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
def relay(index, state)
|
46
|
+
if index < self[:num_relays]
|
47
|
+
connector = self[:config][:relay][index]
|
48
|
+
if is_affirmative?(state)
|
49
|
+
state = 1
|
50
|
+
else
|
51
|
+
state = 0
|
52
|
+
end
|
53
|
+
|
54
|
+
do_send("setstate,#{connector},#{state}")
|
55
|
+
else
|
56
|
+
logger.warn "Attempted to set relay on GlobalCache that does not exist: #{index}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def ir(index, command, options = {})
|
61
|
+
do_send("sendir,1:#{index},#{command}", options)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def relay_status?(index, &block)
|
66
|
+
if index < self[:num_relays]
|
67
|
+
connector = self[:config][:relay][index]
|
68
|
+
do_send("getstate,#{connector}", {:emit => {"relay#{index}".to_sym => block}})
|
69
|
+
else
|
70
|
+
logger.warn "Attempted to check IO on GlobalCache that does not exist: #{index}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def io_status?(index, &block)
|
75
|
+
if index < self[:num_ir]
|
76
|
+
connector = self[:config][:ir][index]
|
77
|
+
do_send("getstate,#{connector}", {:emit => {"ir#{index}".to_sym => block}})
|
78
|
+
else
|
79
|
+
logger.warn "Attempted to check IO on GlobalCache that does not exist: #{index}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
def received(data, command)
|
86
|
+
logger.debug "GlobalCache sent #{data}"
|
87
|
+
data = data.split(',')
|
88
|
+
|
89
|
+
case data[0].to_sym
|
90
|
+
when :state, :statechange
|
91
|
+
type, index = self[:config][data[1]]
|
92
|
+
self["#{type}#{index}"] = data[2] == '1' # Is relay index on?
|
93
|
+
when :device
|
94
|
+
address = data[1]
|
95
|
+
number, type = data[2].split(' ') # The response was "device,2,3 RELAY"
|
96
|
+
|
97
|
+
type = type.downcase.to_sym
|
98
|
+
|
99
|
+
update_status(:config) do |value|
|
100
|
+
value ||= {}
|
101
|
+
value[type] ||= {}
|
102
|
+
current = value[type].length
|
103
|
+
|
104
|
+
dev_index = 1
|
105
|
+
(current..(current + number.to_i - 1)).each do |index|
|
106
|
+
port = "#{address}:#{dev_index}"
|
107
|
+
value[type][index] = port
|
108
|
+
value[port] = [type, index]
|
109
|
+
dev_index += 1
|
110
|
+
end
|
111
|
+
|
112
|
+
value
|
113
|
+
end
|
114
|
+
return :ignore
|
115
|
+
|
116
|
+
when :endlistdevices
|
117
|
+
self[:config_indexed] = true
|
118
|
+
config = self[:config]
|
119
|
+
self[:num_relays] = config[:relay].length unless config[:relay].nil?
|
120
|
+
self[:num_ir] = config[:ir].length unless config[:ir].nil?
|
121
|
+
|
122
|
+
return :success
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
if data.length == 1
|
127
|
+
error = case data[0].split(' ')[1].to_i
|
128
|
+
when 1 then 'Command was missing the carriage return delimiter'
|
129
|
+
when 2 then 'Invalid module address when looking for version'
|
130
|
+
when 3 then 'Invalid module address'
|
131
|
+
when 4 then 'Invalid connector address'
|
132
|
+
when 5 then 'Connector address 1 is set up as "sensor in" when attempting to send an IR command'
|
133
|
+
when 6 then 'Connector address 2 is set up as "sensor in" when attempting to send an IR command'
|
134
|
+
when 7 then 'Connector address 3 is set up as "sensor in" when attempting to send an IR command'
|
135
|
+
when 8 then 'Offset is set to an even transition number, but should be set to an odd transition number in the IR command'
|
136
|
+
when 9 then 'Maximum number of transitions exceeded (256 total on/off transitions allowed)'
|
137
|
+
when 10 then 'Number of transitions in the IR command is not even (the same number of on and off transitions is required)'
|
138
|
+
when 11 then 'Contact closure command sent to a module that is not a relay'
|
139
|
+
when 12 then 'Missing carriage return. All commands must end with a carriage return'
|
140
|
+
when 13 then 'State was requested of an invalid connector address, or the connector is programmed as IR out and not sensor in.'
|
141
|
+
when 14 then 'Command sent to the unit is not supported by the GC-100'
|
142
|
+
when 15 then 'Maximum number of IR transitions exceeded'
|
143
|
+
when 16 then 'Invalid number of IR transitions (must be an even number)'
|
144
|
+
when 21 then 'Attempted to send an IR command to a non-IR module'
|
145
|
+
when 23 then 'Command sent is not supported by this type of module'
|
146
|
+
else 'Unknown error'
|
147
|
+
end
|
148
|
+
logger.warn "GlobalCache error: #{error}\nFor command: #{command[:data]}"
|
149
|
+
return :failed
|
150
|
+
end
|
151
|
+
|
152
|
+
return :success
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
protected
|
157
|
+
|
158
|
+
|
159
|
+
def do_send(command, options = {})
|
160
|
+
#logger.debug "-- GlobalCache, sending: #{command}"
|
161
|
+
|
162
|
+
command << 0x0D
|
163
|
+
|
164
|
+
send(command, options)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
@@ -0,0 +1,143 @@
|
|
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::VsHdmi
|
27
|
+
include ::Orchestrator::Constants
|
28
|
+
include ::Orchestrator::Transcoder
|
29
|
+
|
30
|
+
def on_load
|
31
|
+
|
32
|
+
#
|
33
|
+
# Setup constants
|
34
|
+
#
|
35
|
+
self[:limits_known] = false
|
36
|
+
|
37
|
+
defaults({
|
38
|
+
:wait => false
|
39
|
+
})
|
40
|
+
|
41
|
+
config = ({
|
42
|
+
:inactivity_timeout => 10000 # We wait 10 seconds before the connection is broken (make and break)
|
43
|
+
})
|
44
|
+
end
|
45
|
+
|
46
|
+
def connected
|
47
|
+
#
|
48
|
+
# Get current state of the switcher
|
49
|
+
#
|
50
|
+
get_machine_type
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
COMMANDS = {
|
55
|
+
:reset_video => 0,
|
56
|
+
:switch_video => 1,
|
57
|
+
:status_video => 5,
|
58
|
+
:define_machine => 62,
|
59
|
+
:identify_machine => 61
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
#
|
64
|
+
# Starting at input 1
|
65
|
+
#
|
66
|
+
def switch(map)
|
67
|
+
# instr, inp, outp, machine number
|
68
|
+
# Switch video
|
69
|
+
command = [1, 0x80, 0x80, 0xFF]
|
70
|
+
|
71
|
+
map.each do |input, outputs|
|
72
|
+
outputs = [outputs] unless outputs.class == Array
|
73
|
+
input = input.to_s if input.class == Symbol
|
74
|
+
input = input.to_i if input.class == String
|
75
|
+
outputs.each do |output|
|
76
|
+
command[1] = 0x80 + input
|
77
|
+
command[2] = 0x80 + output
|
78
|
+
outname = :"video#{output}"
|
79
|
+
send(command, name: outname)
|
80
|
+
self[outname] = input
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
alias :switch_video :switch
|
85
|
+
|
86
|
+
def received(data, resolve, command)
|
87
|
+
logger.debug "Kramer sent 0x#{byte_to_hex(data)}"
|
88
|
+
|
89
|
+
data = str_to_array(data)
|
90
|
+
|
91
|
+
return nil if data[0] & 0b1000000 == 0 # Check we are the destination
|
92
|
+
|
93
|
+
data[1] = data[1] & 0b1111111 # input
|
94
|
+
data[2] = data[2] & 0b1111111 # output
|
95
|
+
|
96
|
+
case data[0] & 0b111111
|
97
|
+
when COMMANDS[:define_machine]
|
98
|
+
if data[1] == 1
|
99
|
+
self[:video_inputs] = data[2]
|
100
|
+
elsif data[1] == 2
|
101
|
+
self[:video_outputs] = data[2]
|
102
|
+
end
|
103
|
+
self[:limits_known] = true # Set here in case unsupported
|
104
|
+
when COMMANDS[:status_video]
|
105
|
+
if data[2] == 0 # Then data[1] has been applied to all the outputs
|
106
|
+
logger.debug "Kramer switched #{data[1]} -> All"
|
107
|
+
|
108
|
+
(1..self[:video_outputs]).each do |i|
|
109
|
+
self["video#{i}"] = data[1]
|
110
|
+
end
|
111
|
+
else
|
112
|
+
self["video#{data[2]}"] = data[1]
|
113
|
+
|
114
|
+
logger.debug "Kramer switched #{data[1]} -> #{data[2]}"
|
115
|
+
|
116
|
+
#
|
117
|
+
# As we may not know the max number of inputs if get machine type didn't work
|
118
|
+
#
|
119
|
+
self[:video_inputs] = data[1] if data[1] > self[:video_inputs]
|
120
|
+
self[:video_outputs] = data[2] if data[2] > self[:video_outputs]
|
121
|
+
end
|
122
|
+
when COMMANDS[:identify_machine]
|
123
|
+
logger.debug "Kramer switcher protocol #{data[1]}.#{data[2]}"
|
124
|
+
end
|
125
|
+
|
126
|
+
return :success
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
|
133
|
+
#
|
134
|
+
# No all switchers implement this
|
135
|
+
#
|
136
|
+
def get_machine_type
|
137
|
+
# id com, video
|
138
|
+
command = [62, 0x81, 0x81, 0xFF]
|
139
|
+
send(command, name: :inputs) # num inputs
|
140
|
+
command[1] = 0x82
|
141
|
+
send(command, name: :outputs) # num outputs
|
142
|
+
end
|
143
|
+
end
|