voicemeeter_api_ruby 4.3.1 → 4.4.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfec081c74f1dbdd1b315bd0c3aeaff0b80da1e520b995c584d087f2753cb56e
4
- data.tar.gz: 5e40c74ebc5d8aef7f153f278b3737387f43748604d073d35a308fa817a21627
3
+ metadata.gz: 2d5c6df9dba5f3f6bb249665cc26a6aeb8541c6e77712b4ff45f73452cfac8c4
4
+ data.tar.gz: a4c14273ab0cff0f004a68b4b9aa1816c087c775627bb2ab7872c8275b497859
5
5
  SHA512:
6
- metadata.gz: 189d225992622d19c21d7eb6a3f81e08db74b5b5556b7f8794bcc3fe4c0efe17393fbd6897bae1c869d1b4ebef9eafae1433827a1ecafecb393e0349260bcfb8
7
- data.tar.gz: 933d748eb4ca2bcae4081a35c4b3cc9258b08defa79fca143c0caced8ec3c00773c55acc086f71f36c9ede579515dca945dcd26de335ed825dedd8a2529294a3
6
+ metadata.gz: ad89a1cd012ccc74a42e7f8886c17a531e72b5af8180bba87e28da1df191dee74ac2b24d6531c11c0493e554652779a334241823d31af1e73718a76fc1bdbb11
7
+ data.tar.gz: d7f1d28cc751ed6d40d770f66691e2d012e71d9a29e72d236d2a7800fca3b19f1bbb536fa066e9b0b392a109e27df76be8658dca0605d41769afbe82670eb1a9
data/CHANGELOG.md CHANGED
@@ -9,7 +9,26 @@ Before any major/minor/patch is released all unit tests will be run to verify th
9
9
 
10
10
  ## [Unreleased] - These changes have not been added to RubyGems yet
11
11
 
12
- - [ ]
12
+ - [ ] Add midi example
13
+
14
+ ## [4.4.0] - 2022-08-06
15
+
16
+ ### Added
17
+
18
+ - midi device support added.
19
+ - pdirty, mdirty, midi, ldirty kwargs added to Remote class. Initialize which events to sub to.
20
+ - Event class added. Toggle events, get list of currently subscribed.
21
+ - Voicemeeter.remote added to README in Remote class section.
22
+ - observer example updated to reflect changes.
23
+
24
+ ### Changed
25
+
26
+ - channel out props for strip/recorder now mixed in.
27
+ - By default no longer listen for level updates (high volume). Should be enabled explicitly.
28
+
29
+ ### Fixed
30
+
31
+ - bug in strip/bus levels if making capi calls directly.
13
32
 
14
33
  ## [4.3.0] - 2022-07-29
15
34
 
data/README.md CHANGED
@@ -12,9 +12,9 @@ For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md)
12
12
 
13
13
  ## Tested against
14
14
 
15
- - Basic 1.0.8.2
16
- - Banana 2.0.6.2
17
- - Potato 3.0.2.2
15
+ - Basic 1.0.8.4
16
+ - Banana 2.0.6.4
17
+ - Potato 3.0.2.4
18
18
 
19
19
  ## Requirements
20
20
 
@@ -229,7 +229,7 @@ puts vm.bus[0].levels.all
229
229
 
230
230
  ### Strip | Bus
231
231
 
232
- The following methods are Available
232
+ The following methods are available
233
233
 
234
234
  - `fadeto(amount, time)`: float, int
235
235
  - `fadeby(amount, time)`: float, int
@@ -266,7 +266,7 @@ The following properties accept boolean values.
266
266
  - `A1 - A5`: boolean
267
267
  - `B1 - A3`: boolean
268
268
 
269
- The following methods are Available
269
+ The following methods are available
270
270
 
271
271
  - `play`
272
272
  - `stop`
@@ -356,6 +356,27 @@ example:
356
356
  vm.run { (0...vm.device.ins).each { |i| puts vm.device.input(i) } }
357
357
  ```
358
358
 
359
+ ### Midi
360
+
361
+ The following properties are available:
362
+
363
+ - `channel`: int, returns the midi channel
364
+ - `current`: int, returns the current (or most recently pressed) key
365
+
366
+ The following methods are available:
367
+
368
+ - `get(key)`: int, returns most recent velocity value for a key
369
+
370
+
371
+ example:
372
+
373
+ ```ruby
374
+ puts vm.midi.get(12)
375
+ ```
376
+
377
+ get may return nil if no value for requested key in midi cache
378
+
379
+
359
380
  ### Multiple parameters
360
381
 
361
382
  - `apply`
@@ -411,6 +432,58 @@ will load a config file at mydir/configs/banana/example.toml for Voicemeeter Ban
411
432
 
412
433
  ### Remote class
413
434
 
435
+ #### Voicemeeter.remote
436
+
437
+ You may pass the following optional keyword arguments:
438
+
439
+ - `sync`: boolean=false, force the getters to wait for dirty parameters to clear. For most cases leave this as false.
440
+ - `ratelimit`: float=0.033, how often to check for updates in ms.
441
+ - `pdirty`: boolean=true, parameter updates
442
+ - `mdirty`: boolean=true, macrobutton updates
443
+ - `midi`: boolean=true, midi updates
444
+ - `ldirty`: boolean=false, level updates
445
+
446
+
447
+ #### Event updates
448
+
449
+ To receive event updates you should do the following:
450
+
451
+ - register your app to receive updates using the `vm.add_observer(observer)` method, where observer is your app.
452
+ - define an `update(subject)` callback function in your app. The value of subject may be checked for the type of event.
453
+
454
+ See `examples/observer` for a demonstration.
455
+
456
+ Level updates are considered high volume, by default they are NOT listened for. However, polling them with strip levels and bus levels methods will still work.
457
+
458
+ So if you don't wish to receive level updates, or you prefer to handle them yourself simply leave ldirty as default (False).
459
+
460
+ Each of the update types may be enabled/disabled separately. Don't use a midi controller? You have the option to disable midi updates.
461
+
462
+ example:
463
+
464
+ ```ruby
465
+ require 'voicemeeter'
466
+ # Set updates to occur every 50ms
467
+ # Listen for level updates but disable midi updates
468
+ vm = Voicemeeter.remote('banana', ratelimit: 0.05, ldirty: true, midi: false)
469
+ vm.run { ... }
470
+ ```
471
+
472
+ #### `vm.event`
473
+
474
+ You may also add/remove event subscriptions as necessary with the Event class.
475
+
476
+ example:
477
+
478
+ ```ruby
479
+ vm.event.add("ldirty")
480
+
481
+ vm.event.remove("pdirty")
482
+
483
+ # get a list of currently subscribed
484
+ p vm.event.get
485
+ ```
486
+
414
487
  Access to lower level Getters and Setters are provided with these functions:
415
488
 
416
489
  - `vm.get(param, string=false)`: For getting the value of any parameter. Set string to true if getting a property value expected to return a string.
@@ -429,7 +502,7 @@ vm.set('Strip[4].Label', 'stripname')
429
502
  vm.set('Strip[0].Gain', -3.6)
430
503
  ```
431
504
 
432
- #### Voicemeeter::start
505
+ #### Voicemeeter.start
433
506
 
434
507
  Use this function to start Voicemeeter of a kind independently of Remote class.
435
508
  for example:
@@ -1,223 +1,211 @@
1
- require "observer"
2
-
3
- require_relative "runvm"
4
- require_relative "configs"
5
- require_relative "errors"
6
-
7
- module Voicemeeter
8
- class Base
9
- "
10
- Base class responsible for wrapping the C Remote API
11
-
12
- Mixin required modules
13
- "
14
- include Observable
15
- include Configs
16
- include RunVM
17
-
18
- attr_accessor :strip, :bus, :button, :vban, :command, :recorder, :device
19
- attr_accessor :strip_mode
20
-
21
- attr_reader :kind, :p_in, :v_in, :p_out, :v_out, :retval, :cache
22
- attr_reader :running, :_strip_comp, :_bus_comp, :delay
23
-
24
- DELAY = 0.001
25
- SYNC = false
26
- RATELIMIT = 0.033
27
- SIZE = 1
28
-
29
- def initialize(kind, **kwargs)
30
- @kind = kind
31
- @p_in, @v_in = kind.layout[:strip].values
32
- @p_out, @v_out = kind.layout[:bus].values
33
- @cache = Hash.new
34
- @sync = kwargs[:sync] || SYNC
35
- @ratelimit = kwargs[:ratelimit] || RATELIMIT
36
- @running = false
37
- @strip_mode = 0
38
- @delay = DELAY
39
- end
40
-
41
- def init_thread
42
- @running = true
43
- @cache["strip_level"], @cache["bus_level"] = _get_levels
44
- Thread.new do
45
- loop do
46
- Thread.stop if !@running
47
- if pdirty?
48
- changed
49
- notify_observers("pdirty")
50
- end
51
- if mdirty?
52
- changed
53
- notify_observers("mdirty")
54
- end
55
- if ldirty?
56
- changed
57
- @_strip_comp =
58
- @cache["strip_level"].map.with_index do |x, i|
59
- !(x == @strip_buf[i])
60
- end
61
- @_bus_comp =
62
- @cache["bus_level"].map.with_index { |x, i| !(x == @bus_buf[i]) }
63
- @cache["strip_level"] = @strip_buf
64
- @cache["bus_level"] = @bus_buf
65
- notify_observers("ldirty")
66
- end
67
- sleep(@ratelimit)
68
- end
69
- end
70
- end
71
-
72
- def end_thread
73
- @running = false
74
- end
75
-
76
- def login
77
- @@cdll.call(:login)
78
- clear_polling
79
- rescue CAPIErrors => error
80
- case
81
- when error.value == 1
82
- self.start(@kind.name)
83
- clear_polling
84
- when error.value < 0
85
- raise
86
- end
87
- end
88
-
89
- def logout
90
- clear_polling
91
- sleep(0.1)
92
- @@cdll.call(:logout)
93
- end
94
-
95
- def type
96
- c_type = FFI::MemoryPointer.new(:long, SIZE)
97
- @@cdll.call(:vmtype, c_type)
98
- types = { 1 => "basic", 2 => "banana", 3 => "potato" }
99
- types[c_type.read_long]
100
- end
101
-
102
- def version
103
- c_ver = FFI::MemoryPointer.new(:long, SIZE)
104
- @@cdll.call(:vmversion, c_ver)
105
- v1 = (c_ver.read_long & 0xFF000000) >> 24
106
- v2 = (c_ver.read_long & 0x00FF0000) >> 16
107
- v3 = (c_ver.read_long & 0x0000FF00) >> 8
108
- v4 = c_ver.read_long & 0x000000FF
109
- "#{v1}.#{v2}.#{v3}.#{v4}"
110
- end
111
-
112
- def get_parameter(name, is_string = false)
113
- self.polling("get_parameter", name: name) do
114
- if is_string
115
- c_get = FFI::MemoryPointer.new(:string, 512, true)
116
- @@cdll.call(:get_parameter_string, name, c_get)
117
- c_get.read_string
118
- else
119
- c_get = FFI::MemoryPointer.new(:float, SIZE)
120
- @@cdll.call(:get_parameter_float, name, c_get)
121
- c_get.read_float.round(1)
122
- end
123
- end
124
- end
125
-
126
- def set_parameter(name, value)
127
- if value.is_a? String
128
- @@cdll.call(:set_parameter_string, name, value)
129
- else
130
- @@cdll.call(:set_parameter_float, name, value.to_f)
131
- end
132
- @cache.store(name, value)
133
- end
134
-
135
- def get_buttonstatus(id, mode)
136
- self.polling("get_buttonstatus", id: id, mode: mode) do
137
- c_get = FFI::MemoryPointer.new(:float, SIZE)
138
- @@cdll.call(:get_buttonstatus, id, c_get, mode)
139
- c_get.read_float.to_i
140
- end
141
- end
142
-
143
- def set_buttonstatus(id, state, mode)
144
- @@cdll.call(:set_buttonstatus, id, state, mode)
145
- @cache.store("mb_#{id}_#{mode}", state)
146
- end
147
-
148
- def set_parameter_multi(param_hash)
149
- param_hash.each do |(key, val)|
150
- prop, m2, m3, *rem = key.to_s.split("_")
151
- if m2.to_i.to_s == m2
152
- m2 = m2.to_i
153
- elsif m3.to_i.to_s == m3
154
- m3 = m3.to_i
155
- end
156
-
157
- case prop
158
- when "strip"
159
- self.strip[m2].set_multi(val)
160
- when "bus"
161
- self.bus[m2].set_multi(val)
162
- when "button", "mb"
163
- self.button[m2].set_multi(val)
164
- when "vban"
165
- if %w[instream in].include? m2
166
- self.vban.instream[m3].set_multi(val)
167
- elsif %w[outstream out].include? m2
168
- self.vban.outstream[m3].set_multi(val)
169
- end
170
- end
171
- sleep(DELAY)
172
- end
173
- end
174
-
175
- def get_level(type, index)
176
- c_get = FFI::MemoryPointer.new(:float, SIZE)
177
- @@cdll.call(:get_level, type, index, c_get)
178
- c_get.read_float
179
- end
180
-
181
- def _get_levels
182
- [
183
- (0...(2 * @p_in + 8 * @v_in)).map { |i| get_level(@strip_mode, i) },
184
- (0...(8 * (@p_out + @v_out))).map { |i| get_level(3, i) }
185
- ]
186
- end
187
-
188
- def get_num_devices(direction)
189
- unless %w[in out].include? direction
190
- raise VMRemoteErrors.new("expected in or out")
191
- end
192
- if direction == "in"
193
- val = @@cdll.call(:get_num_indevices)
194
- else
195
- val = @@cdll.call(:get_num_outdevices)
196
- end
197
- val[0]
198
- end
199
-
200
- def get_device_description(index, direction)
201
- unless %w[in out].include? direction
202
- raise VMRemoteErrors.new("expected in or out")
203
- end
204
- c_type = FFI::MemoryPointer.new(:long, SIZE)
205
- c_name = FFI::MemoryPointer.new(:string, 256, true)
206
- c_hwid = FFI::MemoryPointer.new(:string, 256, true)
207
- if direction == "in"
208
- @@cdll.call(:get_desc_indevices, index, c_type, c_name, c_hwid)
209
- else
210
- @@cdll.call(:get_desc_outdevices, index, c_type, c_name, c_hwid)
211
- end
212
- [c_name.read_string, c_type.read_long, c_hwid.read_string]
213
- end
214
-
215
- alias_method "set_multi", :set_parameter_multi
216
- alias_method "apply", :set_parameter_multi
217
- alias_method "get", :get_parameter
218
- alias_method "set", :set_parameter
219
- alias_method "pdirty", :pdirty?
220
- alias_method "mdirty", :mdirty?
221
- alias_method "ldirty", :ldirty?
222
- end
223
- end
1
+ require "observer"
2
+
3
+ require_relative "worker"
4
+ require_relative "runvm"
5
+ require_relative "configs"
6
+ require_relative "errors"
7
+ require_relative "event"
8
+ require_relative "midi"
9
+
10
+ module Voicemeeter
11
+ class Base
12
+ "
13
+ Base class responsible for wrapping the C Remote API
14
+
15
+ Mixin required modules
16
+ "
17
+ include Observable
18
+ include Configs
19
+ include RunVM
20
+ include Worker
21
+
22
+ attr_accessor :strip, :bus, :button, :vban, :command, :recorder, :device
23
+ attr_accessor :midi
24
+ attr_accessor :strip_mode, :event
25
+
26
+ attr_reader :kind, :p_in, :v_in, :p_out, :v_out, :retval, :cache
27
+ attr_reader :running, :_strip_comp, :_bus_comp, :delay
28
+
29
+ DELAY = 0.001
30
+ SYNC = false
31
+ RATELIMIT = 0.033
32
+ SIZE = 1
33
+
34
+ def initialize(kind, **kwargs)
35
+ @kind = kind
36
+ @sync = kwargs.delete(:sync) || SYNC
37
+ @ratelimit = kwargs.delete(:ratelimit) || RATELIMIT
38
+ @p_in, @v_in = kind.layout[:strip].values
39
+ @p_out, @v_out = kind.layout[:bus].values
40
+ @running = false
41
+ @strip_mode = 0
42
+ @delay = DELAY
43
+ @cache = Hash.new
44
+ @midi = Midi.new
45
+ @event = Event.new(**kwargs)
46
+ end
47
+
48
+ def login
49
+ @@cdll.call(:login)
50
+ clear_polling
51
+ rescue CAPIErrors => error
52
+ case
53
+ when error.value == 1
54
+ self.start(@kind.name)
55
+ clear_polling
56
+ when error.value < 0
57
+ raise
58
+ end
59
+ end
60
+
61
+ def logout
62
+ clear_polling
63
+ sleep(0.1)
64
+ @@cdll.call(:logout)
65
+ end
66
+
67
+ def type
68
+ c_type = FFI::MemoryPointer.new(:long, SIZE)
69
+ @@cdll.call(:vmtype, c_type)
70
+ types = { 1 => "basic", 2 => "banana", 3 => "potato" }
71
+ types[c_type.read_long]
72
+ end
73
+
74
+ def version
75
+ c_ver = FFI::MemoryPointer.new(:long, SIZE)
76
+ @@cdll.call(:vmversion, c_ver)
77
+ v1 = (c_ver.read_long & 0xFF000000) >> 24
78
+ v2 = (c_ver.read_long & 0x00FF0000) >> 16
79
+ v3 = (c_ver.read_long & 0x0000FF00) >> 8
80
+ v4 = c_ver.read_long & 0x000000FF
81
+ "#{v1}.#{v2}.#{v3}.#{v4}"
82
+ end
83
+
84
+ def get_parameter(name, is_string = false)
85
+ self.polling("get_parameter", name: name) do
86
+ if is_string
87
+ c_get = FFI::MemoryPointer.new(:string, 512, true)
88
+ @@cdll.call(:get_parameter_string, name, c_get)
89
+ c_get.read_string
90
+ else
91
+ c_get = FFI::MemoryPointer.new(:float, SIZE)
92
+ @@cdll.call(:get_parameter_float, name, c_get)
93
+ c_get.read_float.round(1)
94
+ end
95
+ end
96
+ end
97
+
98
+ def set_parameter(name, value)
99
+ if value.is_a? String
100
+ @@cdll.call(:set_parameter_string, name, value)
101
+ else
102
+ @@cdll.call(:set_parameter_float, name, value.to_f)
103
+ end
104
+ @cache.store(name, value)
105
+ end
106
+
107
+ def get_buttonstatus(id, mode)
108
+ self.polling("get_buttonstatus", id: id, mode: mode) do
109
+ c_get = FFI::MemoryPointer.new(:float, SIZE)
110
+ @@cdll.call(:get_buttonstatus, id, c_get, mode)
111
+ c_get.read_float.to_i
112
+ end
113
+ end
114
+
115
+ def set_buttonstatus(id, state, mode)
116
+ @@cdll.call(:set_buttonstatus, id, state, mode)
117
+ @cache.store("mb_#{id}_#{mode}", state)
118
+ end
119
+
120
+ def set_parameter_multi(param_hash)
121
+ param_hash.each do |(key, val)|
122
+ prop, m2, m3, *rem = key.to_s.split("_")
123
+ if m2.to_i.to_s == m2
124
+ m2 = m2.to_i
125
+ elsif m3.to_i.to_s == m3
126
+ m3 = m3.to_i
127
+ end
128
+
129
+ case prop
130
+ when "strip"
131
+ self.strip[m2].set_multi(val)
132
+ when "bus"
133
+ self.bus[m2].set_multi(val)
134
+ when "button", "mb"
135
+ self.button[m2].set_multi(val)
136
+ when "vban"
137
+ if %w[instream in].include? m2
138
+ self.vban.instream[m3].set_multi(val)
139
+ elsif %w[outstream out].include? m2
140
+ self.vban.outstream[m3].set_multi(val)
141
+ end
142
+ end
143
+ sleep(DELAY)
144
+ end
145
+ end
146
+
147
+ def get_level(type, index)
148
+ c_get = FFI::MemoryPointer.new(:float, SIZE)
149
+ @@cdll.call(:get_level, type, index, c_get)
150
+ c_get.read_float
151
+ end
152
+
153
+ def _get_levels
154
+ [
155
+ (0...(2 * @p_in + 8 * @v_in)).map { |i| get_level(@strip_mode, i) },
156
+ (0...(8 * (@p_out + @v_out))).map { |i| get_level(3, i) }
157
+ ]
158
+ end
159
+
160
+ def get_num_devices(direction)
161
+ unless %w[in out].include? direction
162
+ raise VMRemoteErrors.new("expected in or out")
163
+ end
164
+ if direction == "in"
165
+ res = @@cdll.call(:get_num_indevices)
166
+ else
167
+ res = @@cdll.call(:get_num_outdevices)
168
+ end
169
+ res[0]
170
+ end
171
+
172
+ def get_device_description(index, direction)
173
+ unless %w[in out].include? direction
174
+ raise VMRemoteErrors.new("expected in or out")
175
+ end
176
+ c_type = FFI::MemoryPointer.new(:long, SIZE)
177
+ c_name = FFI::MemoryPointer.new(:string, 256, true)
178
+ c_hwid = FFI::MemoryPointer.new(:string, 256, true)
179
+ if direction == "in"
180
+ @@cdll.call(:get_desc_indevices, index, c_type, c_name, c_hwid)
181
+ else
182
+ @@cdll.call(:get_desc_outdevices, index, c_type, c_name, c_hwid)
183
+ end
184
+ [c_name.read_string, c_type.read_long, c_hwid.read_string]
185
+ end
186
+
187
+ def get_midi_message
188
+ c_msg = FFI::MemoryPointer.new(:string, 1024, true)
189
+ res = @@cdll.call(:get_midi_message, c_msg, 1024)
190
+ if res[0] > 0
191
+ vals = c_msg.read_string.bytes
192
+ vals.each_slice(3) do |ch, key, vel|
193
+ @midi.channel = ch if @midi.channel.nil? || @midi.channel != ch
194
+ @midi.current = key.to_i
195
+ @midi.set(key.to_i, vel.to_i)
196
+ end
197
+ elsif res[0] == -1 || res[0] == -2
198
+ raise CAPIErrors.new("VBVMR_GetMidiMessage returned #{msg[0]}")
199
+ end
200
+ res[0] > 0
201
+ end
202
+
203
+ alias_method "set_multi", :set_parameter_multi
204
+ alias_method "apply", :set_parameter_multi
205
+ alias_method "get", :get_parameter
206
+ alias_method "set", :set_parameter
207
+ alias_method "pdirty", :pdirty?
208
+ alias_method "mdirty", :mdirty?
209
+ alias_method "ldirty", :ldirty?
210
+ end
211
+ end
@@ -82,10 +82,10 @@ module Voicemeeter
82
82
  end
83
83
 
84
84
  def getter(mode)
85
- if @remote.running
85
+ if @remote.running && @remote.event.ldirty
86
86
  vals = @remote.cache["bus_level"][@init, @offset]
87
87
  else
88
- vals = (@init...@offset).map { |i| @remote.get_level(mode, i) }
88
+ vals = (@init...@init + @offset).map { |i| @remote.get_level(mode, i) }
89
89
  end
90
90
  vals.map { |x| x > 0 ? (20 * Math.log(x, 10)).round(1) : -200.0 }
91
91
  end
@@ -1,25 +1,25 @@
1
- require_relative "iremote"
2
- require_relative "meta"
3
-
4
- module Voicemeeter
5
- class MacroButton < IRemote
6
- include MacroButton_Meta_Functions
7
-
8
- def self.make(remote, num_buttons)
9
- (0...num_buttons).map { |i| MacroButton.new(remote, i) }
10
- end
11
-
12
- def initialize(remote, i)
13
- super
14
- self.make_accessor_macrobutton :state, :stateonly, :trigger
15
- end
16
-
17
- def getter(mode)
18
- @remote.get_buttonstatus(@index, mode)
19
- end
20
-
21
- def setter(set, mode)
22
- @remote.set_buttonstatus(@index, set, mode)
23
- end
24
- end
25
- end
1
+ require_relative "iremote"
2
+ require_relative "meta"
3
+
4
+ module Voicemeeter
5
+ class MacroButton < IRemote
6
+ include MacroButton_Meta_Functions
7
+
8
+ def self.make(remote, num_buttons)
9
+ (0...num_buttons).map { |i| MacroButton.new(remote, i) }
10
+ end
11
+
12
+ def initialize(remote, i)
13
+ super
14
+ self.make_accessor_macrobutton :state, :stateonly, :trigger
15
+ end
16
+
17
+ def getter(mode)
18
+ @remote.get_buttonstatus(@index, mode)
19
+ end
20
+
21
+ def setter(set, mode)
22
+ @remote.set_buttonstatus(@index, set, mode)
23
+ end
24
+ end
25
+ end