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,256 @@
|
|
1
|
+
module Samsung; end
|
2
|
+
module Samsung::Displays; end
|
3
|
+
|
4
|
+
|
5
|
+
class Samsung::Displays::MdSeries
|
6
|
+
include ::Orchestrator::Constants
|
7
|
+
include ::Orchestrator::Transcoder
|
8
|
+
|
9
|
+
#
|
10
|
+
# Control system events
|
11
|
+
def on_load
|
12
|
+
on_update
|
13
|
+
|
14
|
+
self[:volume_min] = 0
|
15
|
+
self[:volume_max] = 100
|
16
|
+
self[:power] = false
|
17
|
+
|
18
|
+
config({
|
19
|
+
tokenize: proc {
|
20
|
+
::UV::AbstractTokenizer.new({
|
21
|
+
indicator: "\xAA",
|
22
|
+
callback: method(:check_checksum)
|
23
|
+
})
|
24
|
+
}
|
25
|
+
})
|
26
|
+
|
27
|
+
# Meta data for inquiring interfaces
|
28
|
+
self[:type] = :lcd
|
29
|
+
self[:input_stable] = true
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_unload
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_update
|
36
|
+
@id = setting(:display_id) || 0xFF
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
#
|
41
|
+
# network events
|
42
|
+
def connected
|
43
|
+
do_poll
|
44
|
+
|
45
|
+
@polling_timer = schedule.every('30s') do
|
46
|
+
logger.debug "-- Polling Display"
|
47
|
+
do_poll
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def disconnected
|
52
|
+
#
|
53
|
+
# Disconnected may be called without calling connected
|
54
|
+
# Hence the check if timer is nil here
|
55
|
+
#
|
56
|
+
self[:power] = false # As we may need to use wake on lan
|
57
|
+
@polling_timer.cancel unless @polling_timer.nil?
|
58
|
+
@polling_timer = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
#
|
63
|
+
# Command types
|
64
|
+
COMMAND = {
|
65
|
+
:hard_off => 0x11, # Completely powers off
|
66
|
+
:power => 0xF9, # Technically the panel command
|
67
|
+
:volume => 0x12,
|
68
|
+
:input => 0x14,
|
69
|
+
:mode => 0x18,
|
70
|
+
:size => 0x19,
|
71
|
+
:pip => 0x3C, # picture in picture
|
72
|
+
:auto_adjust => 0x3D,
|
73
|
+
:wall_mode => 0x5C, # Video wall mode
|
74
|
+
:safety => 0x5D,
|
75
|
+
:wall_on => 0x84, # Video wall enabled
|
76
|
+
:wall_user => 0x89 # Video wall user control
|
77
|
+
}
|
78
|
+
COMMAND.merge!(COMMAND.invert)
|
79
|
+
|
80
|
+
# As true power off disconnects the server we only want to
|
81
|
+
# power off the panel. This doesn't work in video walls
|
82
|
+
# so if a nominal blank input is
|
83
|
+
def power(power, broadcast = nil)
|
84
|
+
if is_negatory?(power)
|
85
|
+
# Blank the screen before turning off panel
|
86
|
+
#if self[:power]
|
87
|
+
# blank = setting(:blank)
|
88
|
+
# unless blank.nil?
|
89
|
+
# switch_to blank
|
90
|
+
# end
|
91
|
+
#end
|
92
|
+
do_send(:power, 1)
|
93
|
+
elsif !self[:connected]
|
94
|
+
wake(broadcast)
|
95
|
+
else
|
96
|
+
do_send(:power, 0)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def hard_off
|
101
|
+
do_send(:hard_off, 0)
|
102
|
+
end
|
103
|
+
|
104
|
+
def power?(options = {}, &block)
|
105
|
+
options[:emit] = block unless block.nil?
|
106
|
+
do_send(:power, [], options)
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
INPUTS = {
|
111
|
+
:vga => 0x14, # pc in manual
|
112
|
+
:dvi => 0x18,
|
113
|
+
:dvi_video => 0x1F,
|
114
|
+
:hdmi => 0x21,
|
115
|
+
:hdmi_pc => 0x22,
|
116
|
+
:hdmi2 => 0x23,
|
117
|
+
:hdmi2_pc => 0x24,
|
118
|
+
:hdmi3 => 0x31,
|
119
|
+
:hdmi3_pc => 0x32,
|
120
|
+
:display_port => 0x25,
|
121
|
+
:dtv => 0x40,
|
122
|
+
:media => 0x60,
|
123
|
+
:widi => 0x61,
|
124
|
+
:magic_info => 0x20
|
125
|
+
}
|
126
|
+
INPUTS.merge!(INPUTS.invert)
|
127
|
+
|
128
|
+
def switch_to(input, options = {})
|
129
|
+
input = input.to_sym if input.class == String
|
130
|
+
self[:input_stable] = false
|
131
|
+
self[:input_target] = input
|
132
|
+
do_send(:input, INPUTS[input], options)
|
133
|
+
end
|
134
|
+
|
135
|
+
def volume(vol, options = {})
|
136
|
+
vol = vol.to_i
|
137
|
+
vol = 0 if vol < 0
|
138
|
+
vol = 100 if vol > 100
|
139
|
+
|
140
|
+
do_send(:volume, vol, options)
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
#
|
145
|
+
# Emulate mute
|
146
|
+
def mute
|
147
|
+
if not self[:audio_mute]
|
148
|
+
self[:audio_mute] = true
|
149
|
+
self[:previous_volume] = self[:volume] || 50
|
150
|
+
volume 0
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def unmute
|
155
|
+
if self[:audio_mute]
|
156
|
+
self[:audio_mute] = false
|
157
|
+
volume self[:previous_volume]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
#
|
163
|
+
# Maintain connection
|
164
|
+
def do_poll
|
165
|
+
power?({:priority => 0}) do
|
166
|
+
if self[:power] == On
|
167
|
+
do_send(:volume, [], {:priority => 0})
|
168
|
+
do_send(:input, [], {:priority => 0})
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
protected
|
175
|
+
|
176
|
+
|
177
|
+
def wake(broadcast)
|
178
|
+
mac = setting(:mac_address)
|
179
|
+
if mac
|
180
|
+
# config is the database model representing this device
|
181
|
+
wake_device(mac, broadcast || '<broadcast>')
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def received(response, resolve, command)
|
186
|
+
data = str_to_array(response)
|
187
|
+
if data[2] == 3 # Check for correct data length
|
188
|
+
status = data[3]
|
189
|
+
command = data[4]
|
190
|
+
value = data[5]
|
191
|
+
|
192
|
+
if status == 0x41 # 'A'
|
193
|
+
case COMMAND[command]
|
194
|
+
when :power
|
195
|
+
self[:power] = value == 0
|
196
|
+
when :volume
|
197
|
+
self[:volume] = value
|
198
|
+
if self[:audio_mute] && value > 0
|
199
|
+
self[:audio_mute] = false
|
200
|
+
end
|
201
|
+
when :input
|
202
|
+
self[:input] = INPUTS[value]
|
203
|
+
if not self[:input_stable]
|
204
|
+
if self[:input_target] == self[:input]
|
205
|
+
self[:input_stable] = true
|
206
|
+
else
|
207
|
+
switch_to(self[:input_target])
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
return :success
|
213
|
+
else
|
214
|
+
logger.debug "Samsung failed with: #{byte_to_hex(array_to_str(data))}"
|
215
|
+
return :failed # Failed response
|
216
|
+
end
|
217
|
+
else
|
218
|
+
logger.debug "Samsung aborted with: #{byte_to_hex(array_to_str(data))}"
|
219
|
+
return :abort # unknown result
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Called by the Abstract Tokenizer
|
224
|
+
def check_checksum(byte_str)
|
225
|
+
response = str_to_array(byte_str)
|
226
|
+
check = 0
|
227
|
+
response[0..-2].each do |byte|
|
228
|
+
check = (check + byte) & 0xFF
|
229
|
+
end
|
230
|
+
response[-1] == check
|
231
|
+
end
|
232
|
+
|
233
|
+
# Called by do_send to create a checksum
|
234
|
+
def checksum(command)
|
235
|
+
check = 0
|
236
|
+
command.each do |byte|
|
237
|
+
check = (check + byte) & 0xFF
|
238
|
+
end
|
239
|
+
command << check
|
240
|
+
end
|
241
|
+
|
242
|
+
def do_send(command, data = [], options = {})
|
243
|
+
data = [data] unless data.is_a?(Array)
|
244
|
+
|
245
|
+
if command.is_a?(Symbol)
|
246
|
+
options[:name] = command if data.length > 0 # name unless status request
|
247
|
+
command = COMMAND[command]
|
248
|
+
end
|
249
|
+
|
250
|
+
data = [command, 0xFF, data.length] + data # Build request (0xFF is screen id)
|
251
|
+
checksum(data) # Add checksum
|
252
|
+
data = [0xAA] + data # Add header
|
253
|
+
send(array_to_str(data), options)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Vaddio; end
|
2
|
+
module Vaddio::Camera; end
|
3
|
+
|
4
|
+
|
5
|
+
# TCP Port: 23
|
6
|
+
class Vaddio::Camera::ClearViewPtzTelnet
|
7
|
+
include ::Orchestrator::Constants # these provide optional helper methods
|
8
|
+
include ::Orchestrator::Transcoder # (not used in this module)
|
9
|
+
|
10
|
+
|
11
|
+
def on_load
|
12
|
+
# Setup tokenisation of connection
|
13
|
+
config({
|
14
|
+
tokenize: true,
|
15
|
+
indicator: "\e[J\r\n",
|
16
|
+
delimiter: "\r\n> \e[J", # VT100 string -ESC[J
|
17
|
+
wait_ready: "login: "
|
18
|
+
})
|
19
|
+
|
20
|
+
# Default send options
|
21
|
+
defaults({
|
22
|
+
delay: 150 # time in ms between commands giving the unit time to process
|
23
|
+
})
|
24
|
+
|
25
|
+
|
26
|
+
# Constants that are made available to interfaces
|
27
|
+
self[:pan_speed_max] = 24
|
28
|
+
self[:pan_speed_min] = 1
|
29
|
+
self[:tilt_speed_max] = 24
|
30
|
+
self[:tilt_speed_min] = 1
|
31
|
+
self[:zoom_speed_max] = 7
|
32
|
+
self[:zoom_speed_min] = 1
|
33
|
+
|
34
|
+
# Restart schedule (prevents it crashing)
|
35
|
+
# Every night at 01:00am restart the camera unless defined otherwise
|
36
|
+
schedule.cron(setting(:restart_time) || '0 1 * * *') do
|
37
|
+
reboot
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_unload
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_update
|
45
|
+
end
|
46
|
+
|
47
|
+
def connected
|
48
|
+
self[:power] = true
|
49
|
+
@polling_timer = schedule.every('60s') do
|
50
|
+
logger.debug "-- Polling Vaddio Camera"
|
51
|
+
version # Low priority sent to maintain the connection
|
52
|
+
end
|
53
|
+
|
54
|
+
# Send the login password (wait false as not expecting a response)
|
55
|
+
send "admin\r", wait: false
|
56
|
+
password = setting(:password) ? "#{setting(:password)}\r" : "password\r"
|
57
|
+
send password, wait: false
|
58
|
+
end
|
59
|
+
|
60
|
+
def disconnected
|
61
|
+
self[:power] = false
|
62
|
+
|
63
|
+
# Disconnected will be called before connect if initial connect fails
|
64
|
+
@polling_timer.cancel unless @polling_timer.nil?
|
65
|
+
@polling_timer = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def power(state)
|
70
|
+
# Here for compatibility with other camera modules
|
71
|
+
end
|
72
|
+
|
73
|
+
def power?(options = nil, &block)
|
74
|
+
block.call unless block.nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
# direction: left, right, stop
|
79
|
+
def pan(direction, speed = 16)
|
80
|
+
params = direction.to_sym == :stop ? direction : "#{direction} #{speed}"
|
81
|
+
send "camera pan #{params}\r", name: :pan
|
82
|
+
end
|
83
|
+
|
84
|
+
# direction: up, down, stop
|
85
|
+
def tilt(direction, speed = 16)
|
86
|
+
params = direction.to_sym == :stop ? direction : "#{direction} #{speed}"
|
87
|
+
send "camera tilt #{params}\r", name: :tilt
|
88
|
+
end
|
89
|
+
|
90
|
+
def home
|
91
|
+
send "camera home\r", name: :home
|
92
|
+
end
|
93
|
+
|
94
|
+
# number 1->6 inclusive
|
95
|
+
def preset(number, command = :recall)
|
96
|
+
send "camera #{command} #{number}\r", name: :preset
|
97
|
+
end
|
98
|
+
|
99
|
+
# direction: in, out, stop
|
100
|
+
def zoom(direction, speed = 3)
|
101
|
+
params = direction.to_sym == :stop ? direction : "#{direction} #{speed}"
|
102
|
+
send "camera zoom #{params}\r", name: :zoom
|
103
|
+
end
|
104
|
+
|
105
|
+
def reboot(from_now = 0)
|
106
|
+
# Not named so it won't be stored in the queue when not connected
|
107
|
+
# -> Named commands persist disconnect and will execute in order on connect
|
108
|
+
send "reboot #{from_now}\r"
|
109
|
+
end
|
110
|
+
|
111
|
+
def version
|
112
|
+
send "version\r", priority: 0, wait: false
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
protected
|
117
|
+
|
118
|
+
|
119
|
+
def received(data, resolve, command)
|
120
|
+
logger.debug "Vaddio sent #{data}"
|
121
|
+
|
122
|
+
# Deals with multi-line responses
|
123
|
+
data = data.split("\r\n")[-1]
|
124
|
+
|
125
|
+
case data.to_sym
|
126
|
+
when :OK
|
127
|
+
:success
|
128
|
+
when :ERROR, :"Syntax error: Unknown or incomplete command"
|
129
|
+
warning = "Vaddio issue: #{data}"
|
130
|
+
warning << " for command #{command[:data]}" if command
|
131
|
+
logger.warn warning
|
132
|
+
:abort
|
133
|
+
else
|
134
|
+
:ignore
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aca-device-modules
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stephen von Takach
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: orchestrator
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Building automation and IoT control modules
|
42
|
+
email:
|
43
|
+
- steve@cotag.me
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- LICENSE
|
49
|
+
- README.md
|
50
|
+
- aca-device-modules.gemspec
|
51
|
+
- lib/aca-device-modules.rb
|
52
|
+
- lib/aca-device-modules/engine.rb
|
53
|
+
- lib/aca-device-modules/version.rb
|
54
|
+
- modules/aca/pc_control.rb
|
55
|
+
- modules/axis/camera/vapix.rb
|
56
|
+
- modules/biamp/nexia.rb
|
57
|
+
- modules/bss/blu100.rb
|
58
|
+
- modules/clipsal/c_bus.rb
|
59
|
+
- modules/epson/projector/esc_vp21.rb
|
60
|
+
- modules/extron/mixer/dmp44.rb
|
61
|
+
- modules/extron/mixer/dmp64.rb
|
62
|
+
- modules/extron/switcher/dxp.rb
|
63
|
+
- modules/global_cache/gc100.rb
|
64
|
+
- modules/kramer/switcher/vs_hdmi.rb
|
65
|
+
- modules/panasonic/camera/he50.rb
|
66
|
+
- modules/panasonic/projector/pj_link.rb
|
67
|
+
- modules/samsung/displays/md_series.rb
|
68
|
+
- modules/vaddio/camera/clear_view_ptz_telnet.rb
|
69
|
+
homepage: http://cotag.me/
|
70
|
+
licenses:
|
71
|
+
- LGPL3
|
72
|
+
metadata: {}
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubyforge_project:
|
89
|
+
rubygems_version: 2.2.2
|
90
|
+
signing_key:
|
91
|
+
specification_version: 4
|
92
|
+
summary: Open Source Control Modules by ACA
|
93
|
+
test_files: []
|
94
|
+
has_rdoc:
|