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.
@@ -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
+