yamaha 0.0.3 → 0.0.4
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 +4 -4
- data/.gitignore +2 -0
- data/README.md +13 -12
- data/bin/yamaha +3 -111
- data/lib/yamaha/client.rb +168 -102
- data/lib/yamaha/cmd.rb +120 -0
- data/lib/yamaha/version.rb +1 -1
- data/yamaha.gemspec +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 292b70dd67f90f6bf205084ab1bd6de2248c93edc5108acfd1daf798fa9cf320
|
4
|
+
data.tar.gz: 14a7cb6c48e62aed373c79c048cc309c09741614f616eb41c43555f8256626b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5059e62f80e3cdf8e5a21932696a9d2ffa484d050d03646d1e6719cb76ebd7108e22033946cf551296d5c24a8a5ef579d02099ea8ba67752655fcc397659979
|
7
|
+
data.tar.gz: dd338e1d1fe2220356d0a2e3c0a7f02388294325385954f7b625773d2aa211d3b9f66db9fde28b48357d852ecae80ae8b76c367509cee19cc1a6ae203cd66eb5
|
data/.gitignore
ADDED
data/README.md
CHANGED
@@ -5,17 +5,18 @@
|
|
5
5
|
### RX-V1500 Power Values
|
6
6
|
|
7
7
|
You might expect the power state to be a bit field, but it isn't - each
|
8
|
-
combination
|
9
|
-
|
10
|
-
| Main zone | Zone 2 | Zone 3 | Value | Notes
|
11
|
-
|
|
12
|
-
| On | On |
|
13
|
-
| On | Off |
|
14
|
-
| On | Off |
|
15
|
-
|
|
16
|
-
| Off | On |
|
17
|
-
| Off | Off |
|
18
|
-
| Off | Off |
|
8
|
+
combination is assigned an independent value:
|
9
|
+
|
10
|
+
| Main zone | Zone 2 | Zone 3 | Value | Notes |
|
11
|
+
| --------- | ------ | ------ | ----- | ------- |
|
12
|
+
| On | On | On | 1 | All on |
|
13
|
+
| On | On | Off | 4 | |
|
14
|
+
| On | Off | On | 5 | |
|
15
|
+
| On | Off | Off | 2 | |
|
16
|
+
| Off | On | On | 3 | |
|
17
|
+
| Off | On | Off | 6 | |
|
18
|
+
| Off | Off | On | 7 | |
|
19
|
+
| Off | Off | Off | 0 | All off |
|
19
20
|
|
20
21
|
## Implementation Notes
|
21
22
|
|
@@ -62,7 +63,7 @@ Yamaha RS232/serial protocol:
|
|
62
63
|
|
63
64
|
- [YRXV1500-MQTT](https://github.com/FireFrei/yrxv1500-mqtt)
|
64
65
|
- [YamahaController](https://github.com/mrworf/yamahacontroller)
|
65
|
-
- [Homie ESP8266 Yamaha RX-Vxxxx Control]https://github.com/memphi2/homie-yamaha-rs232)
|
66
|
+
- [Homie ESP8266 Yamaha RX-Vxxxx Control](https://github.com/memphi2/homie-yamaha-rs232)
|
66
67
|
|
67
68
|
Serial port communication in Ruby:
|
68
69
|
|
data/bin/yamaha
CHANGED
@@ -3,118 +3,10 @@
|
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
5
|
begin
|
6
|
-
require 'yamaha'
|
6
|
+
require 'yamaha/cmd'
|
7
7
|
rescue LoadError
|
8
8
|
$: << File.join(File.dirname(__FILE__), '../lib')
|
9
|
-
require 'yamaha'
|
9
|
+
require 'yamaha/cmd'
|
10
10
|
end
|
11
|
-
require 'optparse'
|
12
|
-
require 'logger'
|
13
|
-
require 'pp'
|
14
11
|
|
15
|
-
|
16
|
-
OptionParser.new do |opts|
|
17
|
-
opts.banner = "Usage: yamaha [-d device] command arg..."
|
18
|
-
|
19
|
-
opts.on("-d", "--device DEVICE", "TTY to use (default autodetect)") do |v|
|
20
|
-
options[:device] = v
|
21
|
-
end
|
22
|
-
end.parse!
|
23
|
-
|
24
|
-
logger = Logger.new(STDERR)
|
25
|
-
client = Yamaha::Client.new(options[:device], logger: logger)
|
26
|
-
|
27
|
-
cmd = ARGV.shift
|
28
|
-
unless cmd
|
29
|
-
raise ArgumentError, "No command given"
|
30
|
-
end
|
31
|
-
|
32
|
-
def parse_on_off(value)
|
33
|
-
case value&.downcase
|
34
|
-
when '1', 'on', 'yes', 'true'
|
35
|
-
true
|
36
|
-
when '0', 'off', 'no', 'value'
|
37
|
-
false
|
38
|
-
else
|
39
|
-
raise ArgumentError, "Invalid on/off value: #{value}"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
case cmd
|
44
|
-
when 'detect'
|
45
|
-
device = Yamaha::Client.detect_device(*ARGV, logger: logger)
|
46
|
-
if device
|
47
|
-
puts device
|
48
|
-
exit 0
|
49
|
-
else
|
50
|
-
STDERR.puts("Yamaha receiver not found")
|
51
|
-
exit 3
|
52
|
-
end
|
53
|
-
when 'power'
|
54
|
-
which = ARGV.shift&.downcase
|
55
|
-
if %w(main zone2 zone3).include?(which)
|
56
|
-
method = "set_#{which}_power"
|
57
|
-
state = parse_on_off(ARGV.shift)
|
58
|
-
else
|
59
|
-
method = 'set_power'
|
60
|
-
state = parse_on_off(which)
|
61
|
-
end
|
62
|
-
client.public_send(method, state)
|
63
|
-
when 'volume'
|
64
|
-
which = ARGV.shift
|
65
|
-
if %w(main zone2 zone3).include?(which)
|
66
|
-
prefix = "set_#{which}"
|
67
|
-
value = ARGV.shift
|
68
|
-
else
|
69
|
-
prefix = 'set_main'
|
70
|
-
value = which
|
71
|
-
end
|
72
|
-
if %w(. -).include?(value)
|
73
|
-
method = "#{prefix}_mute"
|
74
|
-
value = true
|
75
|
-
else
|
76
|
-
method = "#{prefix}_volume_db"
|
77
|
-
if value[0] == ','
|
78
|
-
value = value[1..]
|
79
|
-
end
|
80
|
-
value = Float(value)
|
81
|
-
end
|
82
|
-
client.public_send(method, value)
|
83
|
-
p client.get_main_volume_text
|
84
|
-
p client.get_zone2_volume_text
|
85
|
-
p client.get_zone3_volume_text
|
86
|
-
when 'input'
|
87
|
-
which = ARGV.shift&.downcase
|
88
|
-
if %w(main zone2 zone3).include?(which)
|
89
|
-
method = "set_#{which}_input"
|
90
|
-
input = ARGV.shift
|
91
|
-
else
|
92
|
-
method = 'set_main_input'
|
93
|
-
input = which
|
94
|
-
end
|
95
|
-
client.public_send(method, input)
|
96
|
-
when 'program'
|
97
|
-
value = ARGV.shift.downcase
|
98
|
-
client.set_program(value)
|
99
|
-
when 'pure-direct'
|
100
|
-
state = parse_on_off(ARGV.shift)
|
101
|
-
client.set_pure_direct(state)
|
102
|
-
when 'status'
|
103
|
-
pp client.last_status
|
104
|
-
when 'status_string'
|
105
|
-
puts client.last_status_string
|
106
|
-
when 'test'
|
107
|
-
client.set_power(false)
|
108
|
-
[true, false].each do |main_state|
|
109
|
-
[true, false].each do |zone2_state|
|
110
|
-
[true, false].each do |zone3_state|
|
111
|
-
client.set_main_power(main_state)
|
112
|
-
client.set_zone2_power(zone2_state)
|
113
|
-
client.set_zone3_power(zone3_state)
|
114
|
-
puts "#{main_state ?1:0} #{zone2_state ?1:0} #{zone3_state ?1:0} #{client.status[:power]}"
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
else
|
119
|
-
raise ArgumentError, "Unknown command: #{cmd}"
|
120
|
-
end
|
12
|
+
Yamaha::Cmd.new(ARGV).run
|
data/lib/yamaha/client.rb
CHANGED
@@ -13,37 +13,45 @@ module Yamaha
|
|
13
13
|
class UnexpectedResponse < Error; end
|
14
14
|
class CommunicationTimeout < Error; end
|
15
15
|
|
16
|
-
RS232_TIMEOUT =
|
16
|
+
RS232_TIMEOUT = 3
|
17
17
|
DEFAULT_DEVICE_GLOB = '/dev/ttyUSB*'
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
found = device
|
35
|
-
end
|
19
|
+
module_function def detect_device(*patterns, logger: nil)
|
20
|
+
if patterns.empty?
|
21
|
+
patterns = [DEFAULT_DEVICE_GLOB]
|
22
|
+
end
|
23
|
+
devices = patterns.map do |pattern|
|
24
|
+
Dir.glob(pattern)
|
25
|
+
end.flatten.uniq
|
26
|
+
queue = Queue.new
|
27
|
+
threads = devices.map do |device|
|
28
|
+
Thread.new do
|
29
|
+
Timeout.timeout(RS232_TIMEOUT * 3) do
|
30
|
+
logger&.debug("Trying #{device}")
|
31
|
+
Client.new(device, logger: logger).last_status
|
32
|
+
logger&.debug("Found receiver at #{device}")
|
33
|
+
queue << device
|
36
34
|
end
|
35
|
+
rescue CommunicationTimeout, IOError, SystemCallError => exc
|
36
|
+
logger&.debug("Failed on #{device}: #{exc.class}: #{exc}")
|
37
37
|
end
|
38
|
+
end
|
39
|
+
wait_thread = Thread.new do
|
38
40
|
threads.map(&:join)
|
39
|
-
|
41
|
+
queue << nil
|
42
|
+
end
|
43
|
+
queue.shift.tap do
|
44
|
+
threads.map(&:kill)
|
45
|
+
wait_thread.kill
|
40
46
|
end
|
47
|
+
end
|
41
48
|
|
49
|
+
class Client
|
42
50
|
def initialize(device = nil, logger: nil)
|
43
51
|
@logger = logger
|
44
52
|
|
45
53
|
if device.nil?
|
46
|
-
device =
|
54
|
+
device = Yamaha.detect_device(logger: logger)
|
47
55
|
if device
|
48
56
|
logger&.info("Using #{device} as TTY device")
|
49
57
|
end
|
@@ -89,35 +97,59 @@ module Yamaha
|
|
89
97
|
last_status
|
90
98
|
end
|
91
99
|
|
100
|
+
# Turns the receiver on or off.
|
101
|
+
#
|
102
|
+
# @param [ true | false ] state Desired power state.
|
92
103
|
def set_power(state)
|
93
104
|
remote_command("7A1#{state ? 'D' : 'E'}")
|
94
105
|
end
|
95
106
|
|
107
|
+
# Turns main zone power on or off.
|
108
|
+
#
|
109
|
+
# @param [ true | false ] state Desired power state.
|
96
110
|
def set_main_power(state)
|
97
111
|
remote_command("7E7#{state ? 'E' : 'F'}")
|
98
112
|
end
|
99
113
|
|
114
|
+
# Turns zone 2 power on or off.
|
115
|
+
#
|
116
|
+
# @param [ true | false ] state Desired power state.
|
100
117
|
def set_zone2_power(state)
|
101
118
|
remote_command("7EB#{state ? 'A' : 'B'}")
|
102
119
|
end
|
103
120
|
|
121
|
+
# Turns zone 3 power on or off.
|
122
|
+
#
|
123
|
+
# @param [ true | false ] state Desired power state.
|
104
124
|
def set_zone3_power(state)
|
105
125
|
remote_command("7AE#{state ? 'D' : 'E'}")
|
106
126
|
end
|
107
127
|
|
128
|
+
# Sets main zone volume.
|
129
|
+
#
|
130
|
+
# @param [ Integer ] value The raw volume value.
|
108
131
|
def set_main_volume(value)
|
109
132
|
system_command("30#{'%02x' % value}")
|
110
133
|
end
|
111
134
|
|
135
|
+
# Sets main zone volume.
|
136
|
+
#
|
137
|
+
# @param [ Float ] volume The volume in decibels.
|
112
138
|
def set_main_volume_db(volume)
|
113
139
|
value = Integer((volume + 80) * 2 + 39)
|
114
140
|
set_main_volume(value)
|
115
141
|
end
|
116
142
|
|
143
|
+
# Sets zone 2 volume.
|
144
|
+
#
|
145
|
+
# @param [ Integer ] value The raw volume value.
|
117
146
|
def set_zone2_volume(value)
|
118
147
|
system_command("31#{'%02x' % value}")
|
119
148
|
end
|
120
149
|
|
150
|
+
# Sets zone 2 volume.
|
151
|
+
#
|
152
|
+
# @param [ Float ] volume The volume in decibels.
|
121
153
|
def set_zone2_volume_db(volume)
|
122
154
|
value = Integer(volume + 33 + 39)
|
123
155
|
set_zone2_volume(value)
|
@@ -131,6 +163,9 @@ module Yamaha
|
|
131
163
|
remote_command('7ADB')
|
132
164
|
end
|
133
165
|
|
166
|
+
# Sets zone 3 volume.
|
167
|
+
#
|
168
|
+
# @param [ Integer ] volume The raw volume value.
|
134
169
|
def set_zone3_volume(volume)
|
135
170
|
remote_command("234#{'%02x' % value}")
|
136
171
|
end
|
@@ -159,10 +194,35 @@ module Yamaha
|
|
159
194
|
extract_text(system_command("2005"))[3...].strip
|
160
195
|
end
|
161
196
|
|
197
|
+
# Turns pure direct mode on or off.
|
198
|
+
#
|
199
|
+
# @param [ true | false ] state Desired state.
|
162
200
|
def set_pure_direct(state)
|
163
201
|
dispatch("#{STX}07E8#{state ? '0' : '2'}#{ETX}")
|
164
202
|
end
|
165
203
|
|
204
|
+
def set_program(value)
|
205
|
+
program_code = PROGRAM_SET.fetch(value.downcase.gsub(/[^a-z]/, '_'))
|
206
|
+
remote_command("7E#{program_code}")
|
207
|
+
end
|
208
|
+
|
209
|
+
def set_main_input(source)
|
210
|
+
source_code = MAIN_INPUTS_SET.fetch(source.downcase.gsub(/[^a-z]/, '_'))
|
211
|
+
remote_command("7A#{source_code}")
|
212
|
+
end
|
213
|
+
|
214
|
+
def set_zone2_input(source)
|
215
|
+
source_code = ZONE2_INPUTS_SET.fetch(source.downcase.gsub(/[^a-z]/, '_'))
|
216
|
+
remote_command("7A#{source_code}")
|
217
|
+
end
|
218
|
+
|
219
|
+
def set_zone3_input(source)
|
220
|
+
source_code = ZONE3_INPUTS_SET.fetch(source.downcase.gsub(/[^a-z]/, '_'))
|
221
|
+
remote_command("7A#{source_code}")
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
|
166
226
|
PROGRAM_SET = {
|
167
227
|
'munich' => 'E1',
|
168
228
|
'vienna' => 'E5',
|
@@ -195,11 +255,6 @@ module Yamaha
|
|
195
255
|
'thx_game' => 'C8',
|
196
256
|
}.freeze
|
197
257
|
|
198
|
-
def set_program(value)
|
199
|
-
program_code = PROGRAM_SET.fetch(value.downcase.gsub(/[^a-z]/, '_'))
|
200
|
-
remote_command("7E#{program_code}")
|
201
|
-
end
|
202
|
-
|
203
258
|
MAIN_INPUTS_SET = {
|
204
259
|
'phono' => '14',
|
205
260
|
'cd' => '15',
|
@@ -216,11 +271,6 @@ module Yamaha
|
|
216
271
|
'xm' => 'B4',
|
217
272
|
}.freeze
|
218
273
|
|
219
|
-
def set_main_input(source)
|
220
|
-
source_code = MAIN_INPUTS_SET.fetch(source.downcase.gsub(/[^a-z]/, '_'))
|
221
|
-
remote_command("7A#{source_code}")
|
222
|
-
end
|
223
|
-
|
224
274
|
ZONE2_INPUTS_SET = {
|
225
275
|
'phono' => 'D0',
|
226
276
|
'cd' => 'D1',
|
@@ -236,11 +286,6 @@ module Yamaha
|
|
236
286
|
'xm' => 'B8',
|
237
287
|
}.freeze
|
238
288
|
|
239
|
-
def set_zone2_input(source)
|
240
|
-
source_code = ZONE2_INPUTS_SET.fetch(source.downcase.gsub(/[^a-z]/, '_'))
|
241
|
-
remote_command("7A#{source_code}")
|
242
|
-
end
|
243
|
-
|
244
289
|
ZONE3_INPUTS_SET = {
|
245
290
|
'phono' => 'F1',
|
246
291
|
'cd' => 'F2',
|
@@ -256,72 +301,6 @@ module Yamaha
|
|
256
301
|
'xm' => 'B9',
|
257
302
|
}.freeze
|
258
303
|
|
259
|
-
def set_zone3_input(source)
|
260
|
-
source_code = ZONE3_INPUTS_SET.fetch(source.downcase.gsub(/[^a-z]/, '_'))
|
261
|
-
remote_command("7A#{source_code}")
|
262
|
-
end
|
263
|
-
|
264
|
-
private
|
265
|
-
|
266
|
-
def open_device
|
267
|
-
@f = Backend::SerialPortBackend::Device.new(device, logger: logger)
|
268
|
-
|
269
|
-
tries = 0
|
270
|
-
begin
|
271
|
-
do_status
|
272
|
-
rescue CommunicationTimeout
|
273
|
-
tries += 1
|
274
|
-
if tries < 5
|
275
|
-
logger&.warn("Timeout handshaking with the receiver - will retry")
|
276
|
-
retry
|
277
|
-
else
|
278
|
-
raise
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
yield.tap do
|
283
|
-
@f.close
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
# ASCII table: https://www.asciitable.com/
|
288
|
-
DC1 = +?\x11
|
289
|
-
DC2 = +?\x12
|
290
|
-
ETX = +?\x03
|
291
|
-
STX = +?\x02
|
292
|
-
DEL = +?\x7f
|
293
|
-
|
294
|
-
STATUS_REQ = -"#{DC1}001#{ETX}"
|
295
|
-
|
296
|
-
ZERO_ORD = '0'.ord
|
297
|
-
|
298
|
-
def dispatch(cmd)
|
299
|
-
open_device do
|
300
|
-
do_dispatch(cmd)
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
def do_dispatch(cmd)
|
305
|
-
@f.syswrite(cmd.encode('ascii'))
|
306
|
-
read_response
|
307
|
-
end
|
308
|
-
|
309
|
-
def read_response
|
310
|
-
resp = +''
|
311
|
-
Timeout.timeout(2, CommunicationTimeout) do
|
312
|
-
loop do
|
313
|
-
ch = @f.sysread(1)
|
314
|
-
if ch
|
315
|
-
resp << ch
|
316
|
-
break if ch == ETX
|
317
|
-
else
|
318
|
-
sleep 0.1
|
319
|
-
end
|
320
|
-
end
|
321
|
-
end
|
322
|
-
resp
|
323
|
-
end
|
324
|
-
|
325
304
|
MAIN_INPUTS_GET = {
|
326
305
|
'0' => 'PHONO',
|
327
306
|
'1' => 'CD',
|
@@ -411,10 +390,69 @@ module Yamaha
|
|
411
390
|
'40' => 'Enhancer 2ch Low',
|
412
391
|
'41' => 'Enhancer 2ch High',
|
413
392
|
'42' => 'Enhancer 7ch Low',
|
414
|
-
'43' => 'Enhancer 7ch
|
393
|
+
'43' => 'Enhancer 7ch High',
|
415
394
|
'80' => 'Straight',
|
416
395
|
}.freeze
|
417
396
|
|
397
|
+
def open_device
|
398
|
+
@f = Backend::SerialPortBackend::Device.new(device, logger: logger)
|
399
|
+
|
400
|
+
tries = 0
|
401
|
+
begin
|
402
|
+
do_status
|
403
|
+
rescue CommunicationTimeout
|
404
|
+
tries += 1
|
405
|
+
if tries < 5
|
406
|
+
logger&.warn("Timeout handshaking with the receiver - will retry")
|
407
|
+
retry
|
408
|
+
else
|
409
|
+
raise
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
yield.tap do
|
414
|
+
@f.close
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
# ASCII table: https://www.asciitable.com/
|
419
|
+
DC1 = +?\x11
|
420
|
+
DC2 = +?\x12
|
421
|
+
ETX = +?\x03
|
422
|
+
STX = +?\x02
|
423
|
+
DEL = +?\x7f
|
424
|
+
|
425
|
+
STATUS_REQ = -"#{DC1}001#{ETX}"
|
426
|
+
|
427
|
+
ZERO_ORD = '0'.ord
|
428
|
+
|
429
|
+
def dispatch(cmd)
|
430
|
+
open_device do
|
431
|
+
do_dispatch(cmd)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
def do_dispatch(cmd)
|
436
|
+
@f.syswrite(cmd.encode('ascii'))
|
437
|
+
read_response
|
438
|
+
end
|
439
|
+
|
440
|
+
def read_response
|
441
|
+
resp = +''
|
442
|
+
Timeout.timeout(2, CommunicationTimeout) do
|
443
|
+
loop do
|
444
|
+
ch = @f.sysread(1)
|
445
|
+
if ch
|
446
|
+
resp << ch
|
447
|
+
break if ch == ETX
|
448
|
+
else
|
449
|
+
sleep 0.1
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
453
|
+
resp
|
454
|
+
end
|
455
|
+
|
418
456
|
def do_status
|
419
457
|
resp = nil
|
420
458
|
loop do
|
@@ -494,6 +532,34 @@ module Yamaha
|
|
494
532
|
@status
|
495
533
|
end
|
496
534
|
|
535
|
+
%i(
|
536
|
+
model_code firmware_version system_status power main_power zone2_power
|
537
|
+
zone3_power input input_name audio_select audio_select_name
|
538
|
+
main_volume main_volume_db zone2_volume zone2_volume_db
|
539
|
+
zone3_volume zone3_volume_db program program_name sleep night night_name
|
540
|
+
format sample_rate
|
541
|
+
).each do |meth|
|
542
|
+
define_method(meth) do
|
543
|
+
status.fetch(meth)
|
544
|
+
end
|
545
|
+
|
546
|
+
define_method("last_#{meth}") do
|
547
|
+
last_status.fetch(meth)
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
%i(
|
552
|
+
multi_ch_input effect pure_direct speaker_a speaker_b
|
553
|
+
).each do |meth|
|
554
|
+
define_method("#{meth}?") do
|
555
|
+
status.fetch(meth)
|
556
|
+
end
|
557
|
+
|
558
|
+
define_method("last_#{meth}?") do
|
559
|
+
last_status.fetch(meth)
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
497
563
|
def remote_command(cmd)
|
498
564
|
dispatch("#{STX}0#{cmd}#{ETX}")
|
499
565
|
end
|
data/lib/yamaha/cmd.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'logger'
|
3
|
+
require 'pp'
|
4
|
+
require 'yamaha/utils'
|
5
|
+
require 'yamaha/client'
|
6
|
+
|
7
|
+
module Yamaha
|
8
|
+
class Cmd
|
9
|
+
def initialize(args)
|
10
|
+
options = {}
|
11
|
+
OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: yamaha [-d device] command arg..."
|
13
|
+
|
14
|
+
opts.on("-d", "--device DEVICE", "TTY to use (default autodetect)") do |v|
|
15
|
+
options[:device] = v
|
16
|
+
end
|
17
|
+
end.parse!
|
18
|
+
|
19
|
+
@options = options
|
20
|
+
|
21
|
+
@logger = Logger.new(STDERR)
|
22
|
+
@client = Yamaha::Client.new(options[:device], logger: @logger)
|
23
|
+
|
24
|
+
@args = args
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :args
|
28
|
+
attr_reader :logger
|
29
|
+
|
30
|
+
def run
|
31
|
+
cmd = args.shift
|
32
|
+
unless cmd
|
33
|
+
raise ArgumentError, "No command given"
|
34
|
+
end
|
35
|
+
|
36
|
+
case cmd
|
37
|
+
when 'detect'
|
38
|
+
device = Yamaha::Client.detect_device(*args, logger: logger)
|
39
|
+
if device
|
40
|
+
puts device
|
41
|
+
exit 0
|
42
|
+
else
|
43
|
+
STDERR.puts("Yamaha receiver not found")
|
44
|
+
exit 3
|
45
|
+
end
|
46
|
+
when 'power'
|
47
|
+
which = ARGV.shift&.downcase
|
48
|
+
if %w(main zone2 zone3).include?(which)
|
49
|
+
method = "set_#{which}_power"
|
50
|
+
state = parse_on_off(ARGV.shift)
|
51
|
+
else
|
52
|
+
method = 'set_power'
|
53
|
+
state = parse_on_off(which)
|
54
|
+
end
|
55
|
+
client.public_send(method, state)
|
56
|
+
when 'volume'
|
57
|
+
which = ARGV.shift
|
58
|
+
if %w(main zone2 zone3).include?(which)
|
59
|
+
prefix = "set_#{which}"
|
60
|
+
value = ARGV.shift
|
61
|
+
else
|
62
|
+
prefix = 'set_main'
|
63
|
+
value = which
|
64
|
+
end
|
65
|
+
if %w(. -).include?(value)
|
66
|
+
method = "#{prefix}_mute"
|
67
|
+
value = true
|
68
|
+
else
|
69
|
+
method = "#{prefix}_volume_db"
|
70
|
+
if value[0] == ','
|
71
|
+
value = value[1..]
|
72
|
+
end
|
73
|
+
value = Float(value)
|
74
|
+
end
|
75
|
+
client.public_send(method, value)
|
76
|
+
p client.get_main_volume_text
|
77
|
+
p client.get_zone2_volume_text
|
78
|
+
p client.get_zone3_volume_text
|
79
|
+
when 'input'
|
80
|
+
which = ARGV.shift&.downcase
|
81
|
+
if %w(main zone2 zone3).include?(which)
|
82
|
+
method = "set_#{which}_input"
|
83
|
+
input = ARGV.shift
|
84
|
+
else
|
85
|
+
method = 'set_main_input'
|
86
|
+
input = which
|
87
|
+
end
|
88
|
+
client.public_send(method, input)
|
89
|
+
when 'program'
|
90
|
+
value = ARGV.shift.downcase
|
91
|
+
client.set_program(value)
|
92
|
+
when 'pure-direct'
|
93
|
+
state = parse_on_off(ARGV.shift)
|
94
|
+
client.set_pure_direct(state)
|
95
|
+
when 'status'
|
96
|
+
pp client.last_status
|
97
|
+
when 'status_string'
|
98
|
+
puts client.last_status_string
|
99
|
+
when 'test'
|
100
|
+
client.set_power(false)
|
101
|
+
[true, false].each do |main_state|
|
102
|
+
[true, false].each do |zone2_state|
|
103
|
+
[true, false].each do |zone3_state|
|
104
|
+
client.set_main_power(main_state)
|
105
|
+
client.set_zone2_power(zone2_state)
|
106
|
+
client.set_zone3_power(zone3_state)
|
107
|
+
puts "#{main_state ?1:0} #{zone2_state ?1:0} #{zone3_state ?1:0} #{client.status[:power]}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
else
|
112
|
+
raise ArgumentError, "Unknown command: #{cmd}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
attr_reader :client
|
119
|
+
end
|
120
|
+
end
|
data/lib/yamaha/version.rb
CHANGED
data/yamaha.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yamaha
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleg Pudeyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-11-
|
11
|
+
date: 2022-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Library for controlling Yamaha amplifiers via the serial port
|
14
14
|
email:
|
@@ -19,6 +19,7 @@ executables:
|
|
19
19
|
extensions: []
|
20
20
|
extra_rdoc_files: []
|
21
21
|
files:
|
22
|
+
- ".gitignore"
|
22
23
|
- LICENSE
|
23
24
|
- README.md
|
24
25
|
- bin/yamaha
|
@@ -28,6 +29,7 @@ files:
|
|
28
29
|
- lib/yamaha/backend/ffi.rb
|
29
30
|
- lib/yamaha/backend/serial_port.rb
|
30
31
|
- lib/yamaha/client.rb
|
32
|
+
- lib/yamaha/cmd.rb
|
31
33
|
- lib/yamaha/utils.rb
|
32
34
|
- lib/yamaha/version.rb
|
33
35
|
- yamaha.gemspec
|