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,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: