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,125 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
|
3
|
+
module Biamp; end
|
4
|
+
|
5
|
+
# TELNET port 23
|
6
|
+
|
7
|
+
class Biamp::Nexia
|
8
|
+
include ::Orchestrator::Constants
|
9
|
+
include ::Orchestrator::Transcoder
|
10
|
+
|
11
|
+
def on_load
|
12
|
+
self[:fader_min] = -36 # specifically for tonsley
|
13
|
+
self[:fader_max] = 12
|
14
|
+
|
15
|
+
# max +12
|
16
|
+
# min -100
|
17
|
+
|
18
|
+
config({
|
19
|
+
tokenize: true,
|
20
|
+
delimiter: /\xFF\xFE\x01|\r\n/
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_unload
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_update
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def connected
|
32
|
+
send("\xFF\xFE\x01") # Echo off
|
33
|
+
do_send('GETD', 0, 'DEVID')
|
34
|
+
|
35
|
+
@polling_timer = schedule.every('60s') do
|
36
|
+
do_send('GETD', 0, 'DEVID')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def disconnected
|
41
|
+
@polling_timer.cancel unless @polling_timer.nil?
|
42
|
+
@polling_timer = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def preset(number)
|
47
|
+
#
|
48
|
+
# Recall Device 0 Preset number 1001
|
49
|
+
# Device Number will always be 0 for Preset strings
|
50
|
+
# 1001 == minimum preset number
|
51
|
+
#
|
52
|
+
do_send('RECALL', 0, 'PRESET', number)
|
53
|
+
end
|
54
|
+
|
55
|
+
def fader(fader_id, level)
|
56
|
+
# value range: -100 ~ 12
|
57
|
+
do_send('SETD', self[:device_id], 'FDRLVL', fader_id, 1, level)
|
58
|
+
end
|
59
|
+
|
60
|
+
def mute(fader_id)
|
61
|
+
do_send('SETD', self[:device_id], 'FDRMUTE', fader_id, 1, 1)
|
62
|
+
end
|
63
|
+
|
64
|
+
def unmute(fader_id)
|
65
|
+
do_send('SETD', self[:device_id], 'FDRMUTE', fader_id, 1, 0)
|
66
|
+
end
|
67
|
+
|
68
|
+
def query_fader(fader_id)
|
69
|
+
send("GET #{self[:device_id]} FDRLVL #{fader_id} 1 \n") do |data|
|
70
|
+
if data == "-ERR"
|
71
|
+
:abort
|
72
|
+
else
|
73
|
+
self[:"fader_#{fader_id}"] = data.to_i
|
74
|
+
:success
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def query_mute(fader_id)
|
80
|
+
send("GET #{self[:device_id]} FDRMUTE #{fader_id} 1 \n") do |data|
|
81
|
+
if data == "-ERR"
|
82
|
+
:abort
|
83
|
+
else
|
84
|
+
self[:"fader_#{fader_id}_mute"] = data.to_i == 1
|
85
|
+
:success
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def received(data, resolve, command)
|
92
|
+
data = data.split(' ')
|
93
|
+
|
94
|
+
if data.length == 1
|
95
|
+
if data[-1] == "-ERR"
|
96
|
+
logger.debug "Nexia Invalid Command sent #{command[:data]}" if !!command
|
97
|
+
return :abort
|
98
|
+
end
|
99
|
+
return :success # data[-1] == "+OK" || data == "" # Echo off
|
100
|
+
end
|
101
|
+
|
102
|
+
unless data[2].nil?
|
103
|
+
case data[2].to_sym
|
104
|
+
when :FDRLVL
|
105
|
+
self[:"fader_#{data[3]}"] = data[-2].to_i
|
106
|
+
when :FDRMUTE
|
107
|
+
self[:"fader_#{data[3]}_mute"] = data[-2] == "1"
|
108
|
+
when :DEVID
|
109
|
+
self[:device_id] = data[-2].to_i
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
return :success
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
|
121
|
+
def do_send(*args)
|
122
|
+
send("#{args.join(' ')}\n")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
@@ -0,0 +1,265 @@
|
|
1
|
+
module Bss; end
|
2
|
+
|
3
|
+
# TCP port 1023
|
4
|
+
|
5
|
+
class Bss::Blu100
|
6
|
+
include ::Orchestrator::Constants
|
7
|
+
include ::Orchestrator::Transcoder
|
8
|
+
|
9
|
+
def on_load
|
10
|
+
defaults({
|
11
|
+
:wait => false
|
12
|
+
})
|
13
|
+
config({
|
14
|
+
tokenize: true,
|
15
|
+
delimiter: "\x03",
|
16
|
+
indicator: "\x02"
|
17
|
+
})
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_unload
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_update
|
24
|
+
end
|
25
|
+
|
26
|
+
def connected
|
27
|
+
subscribe_percent(1, 60000)
|
28
|
+
@polling_timer = schedule.every('150s') do # Every 2.5 min
|
29
|
+
subscribe_percent(1, 60000) # Request the level of Hybrid I/O Card A
|
30
|
+
end # This works to maintain the connection
|
31
|
+
end
|
32
|
+
|
33
|
+
def disconnected
|
34
|
+
#
|
35
|
+
# Disconnected may be called without calling connected
|
36
|
+
# Hence the check if timer is nil here
|
37
|
+
#
|
38
|
+
@polling_timer.cancel unless @polling_timer.nil?
|
39
|
+
@polling_timer = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
OPERATION_CODE = {
|
44
|
+
:set_state => 0x88,
|
45
|
+
:subscribe_state => 0x89,
|
46
|
+
:unsubscribe_state => 0x8A,
|
47
|
+
:venue_preset => 0x8B,
|
48
|
+
:param_preset => 0x8C,
|
49
|
+
:set_percent => 0x8D,
|
50
|
+
:subscribe_percent => 0x8E,
|
51
|
+
:unsubscribe_percent => 0x8F,
|
52
|
+
:set_relative_percent => 0x90
|
53
|
+
}
|
54
|
+
OPERATION_CODE.merge!(OPERATION_CODE.invert)
|
55
|
+
|
56
|
+
|
57
|
+
def preset(number)
|
58
|
+
number = number.to_i
|
59
|
+
|
60
|
+
do_send([OPERATION_CODE[:venue_preset]] + number_to_data(number))
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
#
|
65
|
+
# Level controls
|
66
|
+
#
|
67
|
+
def fader(fader, percent)
|
68
|
+
percent = percent.to_i
|
69
|
+
percent = 6553600 if percent > 6553600
|
70
|
+
percent = 0 if percent < 0
|
71
|
+
|
72
|
+
percent = number_to_data(percent)
|
73
|
+
|
74
|
+
do_send([OPERATION_CODE[:set_percent]] + NODE + VIRTUAL + number_to_object(fader.to_i) + CONTROLS[:gain] + percent)
|
75
|
+
subscribe_percent(fader)
|
76
|
+
end
|
77
|
+
|
78
|
+
def mute(fader)
|
79
|
+
do_send([OPERATION_CODE[:set_state]] + NODE + VIRTUAL + number_to_object(fader.to_i) + CONTROLS[:mute] + number_to_data(1))
|
80
|
+
subscribe_state(fader)
|
81
|
+
end
|
82
|
+
|
83
|
+
def unmute(fader)
|
84
|
+
do_send([OPERATION_CODE[:set_state]] + NODE + VIRTUAL + number_to_object(fader.to_i) + CONTROLS[:mute] + number_to_data(0))
|
85
|
+
subscribe_state(fader)
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def query_fader(fader_id)
|
90
|
+
subscribe_percent(fader_id)
|
91
|
+
end
|
92
|
+
|
93
|
+
def query_mute(fader_id)
|
94
|
+
subscribe_state(fader_id)
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
#
|
99
|
+
# Percent controls for relative values
|
100
|
+
#
|
101
|
+
def subscribe_percent(fader, rate = 0, control = CONTROLS[:gain]) # rate must be 0 for non meter controls
|
102
|
+
fader = number_to_object(fader.to_i)
|
103
|
+
rate = number_to_data(rate.to_i)
|
104
|
+
|
105
|
+
do_send([OPERATION_CODE[:subscribe_percent]] + NODE + VIRTUAL + fader + control + rate)
|
106
|
+
end
|
107
|
+
|
108
|
+
def unsubscribe_percent(fader, control = CONTROLS[:gain]) # rate must be 0 for non meter controls
|
109
|
+
fader = number_to_object(fader.to_i)
|
110
|
+
rate = number_to_data(0)
|
111
|
+
|
112
|
+
do_send([OPERATION_CODE[:unsubscribe_percent]] + NODE + VIRTUAL + fader + control + rate)
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# State controls are for discrete values
|
117
|
+
#
|
118
|
+
def subscribe_state(fader, rate = 0, control = CONTROLS[:mute]) # 1000 == every second
|
119
|
+
fader = number_to_object(fader.to_i)
|
120
|
+
rate = number_to_data(rate.to_i)
|
121
|
+
|
122
|
+
do_send([OPERATION_CODE[:subscribe_state]] + NODE + VIRTUAL + fader + control + rate)
|
123
|
+
end
|
124
|
+
|
125
|
+
def unsubscribe_state(fader, control = CONTROLS[:mute]) # 1000 == every second
|
126
|
+
fader = number_to_object(fader.to_i)
|
127
|
+
rate = number_to_data(0)
|
128
|
+
|
129
|
+
do_send([OPERATION_CODE[:unsubscribe_state]] + NODE + VIRTUAL + fader + control + rate)
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
def received(data, resolve, command)
|
134
|
+
#
|
135
|
+
# Grab the message body
|
136
|
+
#
|
137
|
+
data = data.split("\x02")
|
138
|
+
data = str_to_array(data[-1])
|
139
|
+
|
140
|
+
#
|
141
|
+
# Unescape any control characters
|
142
|
+
#
|
143
|
+
message = []
|
144
|
+
found = false
|
145
|
+
data.each do |byte|
|
146
|
+
if found
|
147
|
+
found = false
|
148
|
+
message << (byte - 0x80)
|
149
|
+
elsif RESERVED_CHARS.include? byte
|
150
|
+
found = true
|
151
|
+
else
|
152
|
+
message << byte
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# Process the response
|
158
|
+
#
|
159
|
+
if check_checksum(message)
|
160
|
+
logger.debug "Blu100 sent 0x#{byte_to_hex(array_to_str(message))}"
|
161
|
+
|
162
|
+
data = byte_to_hex(array_to_str(message[-4..-1])).to_i(16)
|
163
|
+
type = message[0] # Always sent
|
164
|
+
|
165
|
+
node = message[1..2]
|
166
|
+
vi = message[3]
|
167
|
+
obj = message[4..6]
|
168
|
+
cntrl = message[7..8]
|
169
|
+
|
170
|
+
case OPERATION_CODE[type]
|
171
|
+
when :set_state # This is the mute response
|
172
|
+
obj = byte_to_hex(array_to_str(obj)).to_i(16)
|
173
|
+
if CONTROLS[cntrl] == :mute
|
174
|
+
self[:"fader_#{obj}_mute"] = data == 1
|
175
|
+
else
|
176
|
+
self[:"fader_#{obj}"] = data
|
177
|
+
end
|
178
|
+
when :subscribe_state
|
179
|
+
when :unsubscribe_state
|
180
|
+
when :venue_preset
|
181
|
+
when :param_preset
|
182
|
+
when :set_percent # This is the fader response
|
183
|
+
obj = byte_to_hex(array_to_str(obj)).to_i(16)
|
184
|
+
self[:"fader_#{obj}"] = data
|
185
|
+
when :subscribe_percent
|
186
|
+
when :unsubscribe_percent
|
187
|
+
when :set_relative_percent
|
188
|
+
end
|
189
|
+
|
190
|
+
return :success
|
191
|
+
else
|
192
|
+
logger.warn "Blu100 Checksum error: 0x#{byte_to_hex(array_to_str(message))}"
|
193
|
+
return :failed
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
protected
|
199
|
+
|
200
|
+
|
201
|
+
def number_to_data(num)
|
202
|
+
str_to_array(hex_to_byte(num.to_s(16).upcase.rjust(8, '0')))
|
203
|
+
end
|
204
|
+
|
205
|
+
def number_to_object(num)
|
206
|
+
str_to_array(hex_to_byte(num.to_s(16).upcase.rjust(6, '0')))
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
RESERVED_CHARS = [0x02, 0x03, 0x06, 0x15, 0x1B]
|
211
|
+
NODE = [0,0] # node we are connected to
|
212
|
+
VIRTUAL = [3] # virtual device is always 3 for audio devices
|
213
|
+
|
214
|
+
CONTROLS = {
|
215
|
+
:gain => [0,0],
|
216
|
+
:mute => [0,1]
|
217
|
+
}
|
218
|
+
CONTROLS.merge!(CONTROLS.invert)
|
219
|
+
|
220
|
+
|
221
|
+
def check_checksum(data)
|
222
|
+
#
|
223
|
+
# Loop through the second to the second last element
|
224
|
+
# Delimiter is removed automatically
|
225
|
+
#
|
226
|
+
check = 0
|
227
|
+
data[0..-2].each do |byte|
|
228
|
+
check = check ^ byte
|
229
|
+
end
|
230
|
+
return check == data.pop # Check the check sum equals the last element
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
|
235
|
+
def do_send(command, options = {})
|
236
|
+
#
|
237
|
+
# build checksum
|
238
|
+
#
|
239
|
+
check = 0
|
240
|
+
command.each do |byte|
|
241
|
+
check = check ^ byte
|
242
|
+
end
|
243
|
+
command = command + [check]
|
244
|
+
|
245
|
+
#
|
246
|
+
# Substitute reserved characters
|
247
|
+
#
|
248
|
+
substituted = []
|
249
|
+
command.each do |byte|
|
250
|
+
if RESERVED_CHARS.include? byte
|
251
|
+
substituted << 0x1B << (byte + 0x80)
|
252
|
+
else
|
253
|
+
substituted << byte
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
#
|
258
|
+
# Add the control characters
|
259
|
+
#
|
260
|
+
substituted.unshift 0x02
|
261
|
+
substituted << 0x03
|
262
|
+
|
263
|
+
send(substituted, options)
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
module Clipsal; end
|
2
|
+
|
3
|
+
|
4
|
+
#
|
5
|
+
# Common Headers
|
6
|
+
# 0x03 == point - point -multipoint, low pri
|
7
|
+
# 0x05 == point - multipoint, low pri
|
8
|
+
# 0x06 == point - point, low pri
|
9
|
+
#
|
10
|
+
# 11xxx110 (x == reserved)
|
11
|
+
# -- Priority, 11 == high, 00 == low
|
12
|
+
# --- Destination, 011 = P-P-M, 101 = P-M, 110 = P-P
|
13
|
+
#
|
14
|
+
#
|
15
|
+
# Commands are formatted as: \ + Header + 00 + Data + checksum + <cr>
|
16
|
+
#
|
17
|
+
# Turn group on \ + 05 (MP header) + 38 (lighting) + 00 + 79 (group on) + XX (group number) + checksum + <cr>
|
18
|
+
# Turn group off \ + 05 (MP header) + 38 (lighting) + 00 + 01 (group off) + XX (group number) + checksum + <cr>
|
19
|
+
# Ramp a group \ + 05 (MP header) + 38 (lighting) + 00 + 79 (group on) + XX (group number) + checksum + <cr>
|
20
|
+
#
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
class Clipsal::CBus
|
25
|
+
include ::Orchestrator::Constants
|
26
|
+
include ::Orchestrator::Transcoder
|
27
|
+
|
28
|
+
def on_load
|
29
|
+
defaults({
|
30
|
+
:wait => false
|
31
|
+
})
|
32
|
+
|
33
|
+
config({
|
34
|
+
tokenize: true,
|
35
|
+
delimiter: "\x0D"
|
36
|
+
})
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_unload
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_update
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def connected
|
48
|
+
send("|||\r", :wait => false) # Ensure we are in smart mode
|
49
|
+
@polling_timer = schedule.every('60s') do
|
50
|
+
logger.debug "-- Polling CBUS"
|
51
|
+
send("|||\r", :wait => false) # Ensure we are in smart mode
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def disconnected
|
56
|
+
@polling_timer.cancel unless @polling_timer.nil?
|
57
|
+
@polling_timer = nil
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def lighting(group, state, application = 0x38)
|
62
|
+
group = group & 0xFF
|
63
|
+
application = application & 0xFF
|
64
|
+
|
65
|
+
command = [0x05, application, 0x00]
|
66
|
+
if [On, 1, :on, 'on'].include?(state)
|
67
|
+
state = On
|
68
|
+
command << 0x79 # Group on
|
69
|
+
else
|
70
|
+
state = Off
|
71
|
+
command << 0x01 # Group off
|
72
|
+
end
|
73
|
+
command << group
|
74
|
+
|
75
|
+
self["lighting_group_#{group}"] = state
|
76
|
+
|
77
|
+
do_send(command)
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def lighting_ramp(group, level, rate = 0b0001, application = 0x38)
|
82
|
+
|
83
|
+
#
|
84
|
+
# rates:
|
85
|
+
# => 0 == instant
|
86
|
+
# => 1 == 4sec
|
87
|
+
# => 2 == 8sec etc
|
88
|
+
#
|
89
|
+
rate = ((rate & 0x0F) << 3) | 0b010 # The command is structured as: 0b0 xxxx 010 where xxxx == rate
|
90
|
+
group = group & 0xFF
|
91
|
+
level = level & 0xFF
|
92
|
+
application = application & 0xFF
|
93
|
+
|
94
|
+
lighting_term_ramp(group)
|
95
|
+
command = [0x05, application, 0x00, rate, group, level]
|
96
|
+
|
97
|
+
do_send(command)
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
def blinds(group, action, application = 0x38)
|
103
|
+
group = group & 0xFF
|
104
|
+
application = application & 0xFF
|
105
|
+
|
106
|
+
command = [0x05, application, 0x00]
|
107
|
+
if is_affirmative?(action)
|
108
|
+
action = Down
|
109
|
+
command += [0x1A, group, 0x00]
|
110
|
+
else
|
111
|
+
command += [0x02, group]
|
112
|
+
|
113
|
+
if is_negatory?(action)
|
114
|
+
action = Up
|
115
|
+
command << 0xFF
|
116
|
+
else
|
117
|
+
# Stop (need to confirm this)
|
118
|
+
command << 0x05
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
do_send(command)
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def trigger(group, action)
|
127
|
+
action = action.to_i
|
128
|
+
group = group.to_i
|
129
|
+
|
130
|
+
group = group & 0xFF
|
131
|
+
action = action & 0xFF
|
132
|
+
command = [0x05, 0xCA, 0x00, 0x02, group, action]
|
133
|
+
|
134
|
+
self["trigger_group_#{group}"] = action
|
135
|
+
|
136
|
+
do_send(command)
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
def trigger_kill(group)
|
141
|
+
group = group.to_i
|
142
|
+
|
143
|
+
group = group & 0xFF
|
144
|
+
command = [0x05, 0xCA, 0x00, 0x01, group]
|
145
|
+
do_send(command)
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
def received(data, resolve, command)
|
150
|
+
# Debug here will sometimes have the \n char
|
151
|
+
# This is removed by the hex_to_byte function
|
152
|
+
logger.debug "CBus sent #{data}"
|
153
|
+
|
154
|
+
data = str_to_array(hex_to_byte(data))
|
155
|
+
|
156
|
+
if !check_checksum(data)
|
157
|
+
logger.debug "CBus checksum failed"
|
158
|
+
return :failed
|
159
|
+
end
|
160
|
+
|
161
|
+
# We are only looking at Point -> MultiPoint commands
|
162
|
+
return if data[0] != 0x05
|
163
|
+
# 0x03 == Point -> Point -> MultiPoint
|
164
|
+
# 0x06 == Point -> Point
|
165
|
+
|
166
|
+
application = data[2] # The application being referenced
|
167
|
+
commands = data[4..-2] # Remove the header + checksum
|
168
|
+
|
169
|
+
while commands.length > 0
|
170
|
+
current = commands.shift
|
171
|
+
|
172
|
+
case application
|
173
|
+
when 0xCA # Trigger group
|
174
|
+
case current
|
175
|
+
when 0x02 # Trigger Event (ex: 0504CA00 020101 29)
|
176
|
+
self["trigger_group_#{commands.shift}"] = commands.shift # Action selector
|
177
|
+
when 0x01 # Trigger Min
|
178
|
+
self["trigger_group_#{commands.shift}"] = 0
|
179
|
+
when 0x79 # Trigger Max
|
180
|
+
self["trigger_group_#{commands.shift}"] = 0xFF
|
181
|
+
when 0x09 # Indicator Kill (ex: 0504CA00 0901 23)
|
182
|
+
commands.shift # Group (turns off indicators of all scenes triggered by this group)
|
183
|
+
else
|
184
|
+
break # We don't know what data is here
|
185
|
+
end
|
186
|
+
when 0x30..0x5F # Lighting group
|
187
|
+
case current
|
188
|
+
when 0x01 # Group off (ex: 05043800 0101 0102 0103 0104 7905 33)
|
189
|
+
self["lighting_group_#{commands.shift}"] = Off
|
190
|
+
when 0x79 # Group on (ex: 05013800 7905 44)
|
191
|
+
self["lighting_group_#{commands.shift}"] = On
|
192
|
+
when 0x02 # Blinds up or stop
|
193
|
+
group = commands.shift
|
194
|
+
value = commands.shift
|
195
|
+
if value == 0xFF
|
196
|
+
self["blinds_group_#{group}"] = Up
|
197
|
+
elsif value == 0x05 # Value needs confirmation
|
198
|
+
self["blinds_group_#{group}"] = :stopped
|
199
|
+
end
|
200
|
+
when 0x1A # Blinds down
|
201
|
+
group = commands.shift
|
202
|
+
value = commands.shift
|
203
|
+
self["blinds_group_#{group}"] = Down if value == 0x00
|
204
|
+
when 0x09 # Terminate Ramp
|
205
|
+
commands.shift # Group address
|
206
|
+
else
|
207
|
+
if (current & 0b10000101) == 0 # Ramp to level (ex: 05013800 0205FF BC)
|
208
|
+
commands.shift(2) # Group address, level
|
209
|
+
else
|
210
|
+
break # We don't know what data is here
|
211
|
+
end
|
212
|
+
end
|
213
|
+
else
|
214
|
+
break # We haven't programmed this application
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
return :success
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
protected
|
223
|
+
|
224
|
+
|
225
|
+
def lighting_term_ramp(group)
|
226
|
+
command = [0x05, 0x38, 0x00, 0x09, group]
|
227
|
+
do_send(command)
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
def checksum(data)
|
232
|
+
check = 0
|
233
|
+
data.each do |byte|
|
234
|
+
check += byte
|
235
|
+
end
|
236
|
+
check = check % 0x100
|
237
|
+
check = ((check ^ 0xFF) + 1) & 0xFF
|
238
|
+
return check
|
239
|
+
end
|
240
|
+
|
241
|
+
def check_checksum(data)
|
242
|
+
check = 0
|
243
|
+
data.each do |byte|
|
244
|
+
check += byte
|
245
|
+
end
|
246
|
+
return (check % 0x100) == 0x00
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
def do_send(command, options = {})
|
251
|
+
string = byte_to_hex(array_to_str(command << checksum(command))).upcase
|
252
|
+
send("\\#{string}\r", options)
|
253
|
+
#logger.debug "CBus module sent #{string}"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|