seriamp 0.1.11 → 0.1.13
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/README.md +37 -0
- data/bin/sonamp-web +4 -3
- data/bin/yamaha-web +4 -3
- data/lib/seriamp/detect.rb +1 -1
- data/lib/seriamp/sonamp/app.rb +3 -1
- data/lib/seriamp/sonamp/client.rb +37 -5
- data/lib/seriamp/version.rb +1 -1
- data/lib/seriamp/yamaha/app.rb +3 -1
- data/lib/seriamp/yamaha/client.rb +82 -29
- data/seriamp.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 608c49104ee16a8740439f09911464a51539d1a52907752bc7f0bf31fa0da85c
|
4
|
+
data.tar.gz: dc11ec89924b2c7b06a7a1a44be13e64c832de805c0c039f4a7bf3d66964a876
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98879e190d39e69030155b492c72d3543e1db219f5315721705909b3cc177c1c150b6b5c3d5b0d3cb58ffccc59ea8d0610a064d95d5159ed11a91c7289a2ba27
|
7
|
+
data.tar.gz: 8f766224a06c19e14a71d5aa290d4c035031fd89a6abe1be1bd962a6fa36306d720e2032ac1ab804425cb40dcdbb6b84c050a15ebc77d8bec3b51673b8428cb5
|
data/README.md
CHANGED
@@ -32,6 +32,39 @@ necessary.
|
|
32
32
|
- Null-modem cable required
|
33
33
|
- Receiver socket is male
|
34
34
|
|
35
|
+
The following table shows which Yamaha receivers have RS-232 connector
|
36
|
+
and which do not:
|
37
|
+
|
38
|
+
| Family | RS-232C Present | RS-232C Absent |
|
39
|
+
| -------- | ---------------------------- | -------------- |
|
40
|
+
| RX-Vx000 | RX-V3000, RX-V1000 | |
|
41
|
+
| | | HTR-5280 |
|
42
|
+
| RX-Vx200 | RX-V2200 | RX-V1200 |
|
43
|
+
| | | HTR-5490 |
|
44
|
+
| RX-Vx300 | RX-V2300 | RX-V1300 |
|
45
|
+
| | | HTR-5590 |
|
46
|
+
| | | HTR-5660 |
|
47
|
+
| RX-Vx400 | RX-V2400 | RX-V1400 |
|
48
|
+
| | | HTR-5790 |
|
49
|
+
| RX-Vx500 | RX-V2500, RX-V1500 | |
|
50
|
+
| | HTR-5890 | HTR-5860 |
|
51
|
+
| RX-Vx600 | RX-V2600, RX-V1600 | |
|
52
|
+
| | HTR-5990 | HTR-5960 |
|
53
|
+
| RX-Vx700 | RX-V2700, RX-V1700 | |
|
54
|
+
| | | HTR-6090 |
|
55
|
+
| RX-Vx800 | RX-V3800, RX-V1800 | |
|
56
|
+
| | HTR-6190 | HTR-6180 |
|
57
|
+
| RX-Vx900 | RX-V3900, RX-V1900 | |
|
58
|
+
| | | HTR-6290 |
|
59
|
+
| RX-Vx67 | RX-V3067, RX-V2067, RX-V1067 | RX-V867 |
|
60
|
+
|
61
|
+
RX-V2700, RX-V3800 and RX-V3900 have an Ethernet port in addition to
|
62
|
+
RS-232C and should be controllable via the Yamaha YNCA protocol via the
|
63
|
+
Ethernet port. Over time Yamaha has been adding networking functionality
|
64
|
+
to lower tier models, for example it is present in RX-V867, RX-V671 and RX-V475.
|
65
|
+
|
66
|
+
Models lower than 1000 level receivers have never had RS-232C to my knowledge.
|
67
|
+
|
35
68
|
### Denon AVR-2308CI
|
36
69
|
|
37
70
|
- Straight cable required
|
@@ -111,6 +144,10 @@ Serial port communication in Ruby:
|
|
111
144
|
- [rubyserial](https://github.com/hybridgroup/rubyserial)
|
112
145
|
- [Ruby/SerialPort](https://github.com/hparra/ruby-serialport)
|
113
146
|
|
147
|
+
Yamaha YNCA protocol:
|
148
|
+
|
149
|
+
- [yamaha_ynca](https://github.com/mvdwetering/yamaha_ynca)
|
150
|
+
|
114
151
|
## Helpful Links
|
115
152
|
|
116
153
|
- [Serial port programming in Ruby](https://www.thegeekdiary.com/serial-port-programming-reading-writing-status-of-control-lines-dtr-rts-cts-dsr/)
|
data/bin/sonamp-web
CHANGED
@@ -23,9 +23,10 @@ end.parse!
|
|
23
23
|
|
24
24
|
logger = Logger.new(STDERR)
|
25
25
|
|
26
|
-
#Sonamp::App.set :device, options[:device]
|
27
|
-
#Sonamp::App.set :logger, logger
|
28
|
-
Seriamp::Sonamp::App.set :client, Seriamp::Sonamp::Client.new(
|
26
|
+
#Seriamp::Sonamp::App.set :device, options[:device]
|
27
|
+
#Seriamp::Sonamp::App.set :logger, logger
|
28
|
+
Seriamp::Sonamp::App.set :client, Seriamp::Sonamp::Client.new(
|
29
|
+
device: options[:device], logger: logger, thread_safe: true)
|
29
30
|
|
30
31
|
options = Rack::Server::Options.new.parse!(ARGV)
|
31
32
|
Rack::Server.start(options.merge(app: Seriamp::Sonamp::App))
|
data/bin/yamaha-web
CHANGED
@@ -23,9 +23,10 @@ end.parse!
|
|
23
23
|
|
24
24
|
logger = Logger.new(STDERR)
|
25
25
|
|
26
|
-
#Yamaha::App.set :device, options[:device]
|
27
|
-
#Yamaha::App.set :logger, logger
|
28
|
-
Seriamp::Yamaha::App.set :client, Seriamp::Yamaha::Client.new(
|
26
|
+
#Seriamp::Yamaha::App.set :device, options[:device]
|
27
|
+
#Seriamp::Yamaha::App.set :logger, logger
|
28
|
+
Seriamp::Yamaha::App.set :client, Seriamp::Yamaha::Client.new(
|
29
|
+
device: options[:device], logger: logger, thread_safe: true)
|
29
30
|
|
30
31
|
options = Rack::Server::Options.new.parse!(ARGV)
|
31
32
|
Rack::Server.start(options.merge(app: Seriamp::Yamaha::App))
|
data/lib/seriamp/detect.rb
CHANGED
@@ -21,7 +21,7 @@ module Seriamp
|
|
21
21
|
Thread.new do
|
22
22
|
Timeout.timeout(timeout * 2, CommunicationTimeout) do
|
23
23
|
logger&.debug("Trying #{device}")
|
24
|
-
client_cls.new(device: device, logger: logger).present?
|
24
|
+
client_cls.new(device: device, logger: logger, retries: false).present?
|
25
25
|
logger&.debug("Found #{mod} device at #{device}")
|
26
26
|
queue << device
|
27
27
|
end
|
data/lib/seriamp/sonamp/app.rb
CHANGED
@@ -12,6 +12,7 @@ module Seriamp
|
|
12
12
|
set :device, nil
|
13
13
|
set :logger, nil
|
14
14
|
set :client, nil
|
15
|
+
set :retries, true
|
15
16
|
|
16
17
|
get '/' do
|
17
18
|
render_json(client.status)
|
@@ -75,7 +76,8 @@ module Seriamp
|
|
75
76
|
|
76
77
|
def client
|
77
78
|
settings.client || begin
|
78
|
-
@client ||= Sonamp::Client.new(settings.device,
|
79
|
+
@client ||= Sonamp::Client.new(settings.device,
|
80
|
+
logger: settings.logger, retries: settings.retries, thread_safe: true)
|
79
81
|
end
|
80
82
|
end
|
81
83
|
|
@@ -10,7 +10,7 @@ module Seriamp
|
|
10
10
|
RS232_TIMEOUT = 3
|
11
11
|
|
12
12
|
class Client
|
13
|
-
def initialize(device: nil, glob: nil, logger: nil, retries: true)
|
13
|
+
def initialize(device: nil, glob: nil, logger: nil, retries: true, thread_safe: false)
|
14
14
|
@logger = logger
|
15
15
|
|
16
16
|
@device = device
|
@@ -26,6 +26,11 @@ module Seriamp
|
|
26
26
|
else
|
27
27
|
raise ArgumentError, "retries must be an integer, true, false or nil: #{retries}"
|
28
28
|
end
|
29
|
+
@thread_safe = !!thread_safe
|
30
|
+
|
31
|
+
if thread_safe?
|
32
|
+
@lock = Mutex.new
|
33
|
+
end
|
29
34
|
end
|
30
35
|
|
31
36
|
attr_reader :device
|
@@ -33,6 +38,10 @@ module Seriamp
|
|
33
38
|
attr_reader :logger
|
34
39
|
attr_reader :retries
|
35
40
|
|
41
|
+
def thread_safe?
|
42
|
+
@thread_safe
|
43
|
+
end
|
44
|
+
|
36
45
|
def detect_device?
|
37
46
|
@detect_device
|
38
47
|
end
|
@@ -182,10 +191,22 @@ module Seriamp
|
|
182
191
|
private
|
183
192
|
|
184
193
|
def with_device(&block)
|
185
|
-
|
186
|
-
|
194
|
+
with_lock do
|
195
|
+
if @io
|
196
|
+
yield @io
|
197
|
+
else
|
198
|
+
open_device(&block)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def with_lock
|
204
|
+
if thread_safe?
|
205
|
+
@lock.synchronize do
|
206
|
+
yield
|
207
|
+
end
|
187
208
|
else
|
188
|
-
|
209
|
+
yield
|
189
210
|
end
|
190
211
|
end
|
191
212
|
|
@@ -203,6 +224,15 @@ module Seriamp
|
|
203
224
|
logger&.debug("Opening #{device}")
|
204
225
|
@io = Backend::SerialPortBackend::Device.new(device, logger: logger)
|
205
226
|
|
227
|
+
warned = false
|
228
|
+
while IO.select([@io.io], nil, nil, 0)
|
229
|
+
unless warned
|
230
|
+
logger&.warn("Serial device readable after opening - unread previous response?")
|
231
|
+
warned = true
|
232
|
+
end
|
233
|
+
IO.read(1)
|
234
|
+
end
|
235
|
+
|
206
236
|
begin
|
207
237
|
yield @io
|
208
238
|
ensure
|
@@ -219,7 +249,9 @@ module Seriamp
|
|
219
249
|
if try <= retries
|
220
250
|
logger&.warn("Error during operation: #{exc.class}: #{exc} - will retry")
|
221
251
|
try += 1
|
222
|
-
|
252
|
+
if detect_device?
|
253
|
+
@device = nil
|
254
|
+
end
|
223
255
|
retry
|
224
256
|
else
|
225
257
|
raise
|
data/lib/seriamp/version.rb
CHANGED
data/lib/seriamp/yamaha/app.rb
CHANGED
@@ -13,6 +13,7 @@ module Seriamp
|
|
13
13
|
set :device, nil
|
14
14
|
set :logger, nil
|
15
15
|
set :client, nil
|
16
|
+
set :retries, true
|
16
17
|
|
17
18
|
get '/' do
|
18
19
|
clear_cache
|
@@ -153,7 +154,8 @@ module Seriamp
|
|
153
154
|
|
154
155
|
def client
|
155
156
|
settings.client || begin
|
156
|
-
@client ||= Yamaha::Client.new(settings.device,
|
157
|
+
@client ||= Yamaha::Client.new(settings.device,
|
158
|
+
logger: settings.logger, retries: settings.retries, thread_safe: true)
|
157
159
|
end
|
158
160
|
end
|
159
161
|
|
@@ -14,7 +14,7 @@ module Seriamp
|
|
14
14
|
class Client
|
15
15
|
include Protocol::Methods
|
16
16
|
|
17
|
-
def initialize(device: nil, glob: nil, logger: nil, retries: true)
|
17
|
+
def initialize(device: nil, glob: nil, logger: nil, retries: true, thread_safe: false)
|
18
18
|
@logger = logger
|
19
19
|
|
20
20
|
@device = device
|
@@ -30,6 +30,11 @@ module Seriamp
|
|
30
30
|
else
|
31
31
|
raise ArgumentError, "retries must be an integer, true, false or nil: #{retries}"
|
32
32
|
end
|
33
|
+
@thread_safe = !!thread_safe
|
34
|
+
|
35
|
+
if thread_safe?
|
36
|
+
@lock = Mutex.new
|
37
|
+
end
|
33
38
|
|
34
39
|
if block_given?
|
35
40
|
begin
|
@@ -45,6 +50,10 @@ module Seriamp
|
|
45
50
|
attr_reader :logger
|
46
51
|
attr_reader :retries
|
47
52
|
|
53
|
+
def thread_safe?
|
54
|
+
@thread_safe
|
55
|
+
end
|
56
|
+
|
48
57
|
def detect_device?
|
49
58
|
@detect_device
|
50
59
|
end
|
@@ -56,18 +65,29 @@ module Seriamp
|
|
56
65
|
|
57
66
|
def last_status
|
58
67
|
unless @status
|
59
|
-
|
60
|
-
|
61
|
-
|
68
|
+
with_lock do
|
69
|
+
with_retry do
|
70
|
+
with_device do
|
71
|
+
unless @status
|
72
|
+
do_status
|
73
|
+
end
|
74
|
+
end
|
62
75
|
end
|
63
76
|
end
|
64
77
|
end
|
78
|
+
if @status.nil?
|
79
|
+
raise "This should not happen"
|
80
|
+
end
|
65
81
|
@status.dup
|
66
82
|
end
|
67
83
|
|
68
84
|
def last_status_string
|
69
85
|
unless @status_string
|
70
|
-
|
86
|
+
with_lock do
|
87
|
+
with_retry do
|
88
|
+
with_device do
|
89
|
+
end
|
90
|
+
end
|
71
91
|
end
|
72
92
|
end
|
73
93
|
@status_string.dup
|
@@ -121,6 +141,16 @@ module Seriamp
|
|
121
141
|
end
|
122
142
|
end
|
123
143
|
|
144
|
+
def with_lock
|
145
|
+
if thread_safe?
|
146
|
+
@lock.synchronize do
|
147
|
+
yield
|
148
|
+
end
|
149
|
+
else
|
150
|
+
yield
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
124
154
|
# Shows a message via the on-screen display. The message must be 16
|
125
155
|
# characters or fewer. The message is NOT displayed on the front panel,
|
126
156
|
# it is shown only on the connected TV's OSD.
|
@@ -134,13 +164,15 @@ module Seriamp
|
|
134
164
|
raise ArgumentError, "Message must be no more than 16 characters, #{msg.length} given"
|
135
165
|
end
|
136
166
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
167
|
+
with_lock do
|
168
|
+
with_retry do
|
169
|
+
with_device do
|
170
|
+
@io.syswrite("#{STX}21000#{ETX}".encode('ascii'))
|
171
|
+
@io.syswrite("#{STX}3#{msg[0..3]}#{ETX}".encode('ascii'))
|
172
|
+
@io.syswrite("#{STX}3#{msg[4..7]}#{ETX}".encode('ascii'))
|
173
|
+
@io.syswrite("#{STX}3#{msg[8..11]}#{ETX}".encode('ascii'))
|
174
|
+
@io.syswrite("#{STX}3#{msg[12..15]}#{ETX}".encode('ascii'))
|
175
|
+
end
|
144
176
|
end
|
145
177
|
end
|
146
178
|
|
@@ -165,6 +197,15 @@ module Seriamp
|
|
165
197
|
logger&.debug("Opening #{device}")
|
166
198
|
@io = Backend::SerialPortBackend::Device.new(device, logger: logger)
|
167
199
|
|
200
|
+
warned = false
|
201
|
+
while IO.select([@io.io], nil, nil, 0)
|
202
|
+
unless warned
|
203
|
+
logger&.warn("Serial device readable after opening - unread previous response?")
|
204
|
+
warned = true
|
205
|
+
end
|
206
|
+
IO.read(1)
|
207
|
+
end
|
208
|
+
|
168
209
|
begin
|
169
210
|
tries = 0
|
170
211
|
begin
|
@@ -265,9 +306,12 @@ module Seriamp
|
|
265
306
|
end
|
266
307
|
break unless again
|
267
308
|
end
|
309
|
+
if resp.length < 10
|
310
|
+
raise HandshakeFailure, "Broken status response: expected at least 10 bytes, got #{resp.length} bytes; concurrent operation on device?"
|
311
|
+
end
|
268
312
|
payload = resp[1...-1]
|
269
|
-
|
270
|
-
|
313
|
+
model_code = payload[0..4]
|
314
|
+
version = payload[5]
|
271
315
|
length = payload[6..7].to_i(16)
|
272
316
|
data = payload[8...-2]
|
273
317
|
if data.length != length
|
@@ -276,11 +320,11 @@ module Seriamp
|
|
276
320
|
unless data.start_with?('@E01900')
|
277
321
|
raise HandshakeFailure, "Broken status response: expected to start with @E01900, actual #{data[0..6]}"
|
278
322
|
end
|
279
|
-
|
280
|
-
|
281
|
-
model_code:
|
282
|
-
model_name: MODEL_NAMES[
|
283
|
-
firmware_version:
|
323
|
+
status_string = data
|
324
|
+
status = {
|
325
|
+
model_code: model_code,
|
326
|
+
model_name: MODEL_NAMES[model_code],
|
327
|
+
firmware_version: version,
|
284
328
|
system_status: data[7].ord - ZERO_ORD,
|
285
329
|
power: power = data[8].ord - ZERO_ORD,
|
286
330
|
main_power: [1, 4, 5, 2].include?(power),
|
@@ -288,7 +332,7 @@ module Seriamp
|
|
288
332
|
zone3_power: [1, 5, 3, 7].include?(power),
|
289
333
|
}
|
290
334
|
if data.length > 9
|
291
|
-
|
335
|
+
status.update(
|
292
336
|
input: input = data[9],
|
293
337
|
input_name: MAIN_INPUTS_GET.fetch(input),
|
294
338
|
multi_ch_input: data[10] == '1',
|
@@ -319,34 +363,41 @@ module Seriamp
|
|
319
363
|
sleep: SLEEP_GET.fetch(data[24]),
|
320
364
|
night: night = data[27],
|
321
365
|
night_name: NIGHT_GET.fetch(night),
|
322
|
-
pure_direct: data[PURE_DIRECT_FIELD.fetch(
|
366
|
+
pure_direct: data[PURE_DIRECT_FIELD.fetch(model_code)] == '1',
|
323
367
|
speaker_a: data[29] == '1',
|
324
368
|
speaker_b: data[30] == '1',
|
325
369
|
# 2 positions on RX-Vx700
|
326
370
|
#format: data[31..32],
|
327
371
|
#sampling: data[33..34],
|
328
372
|
)
|
329
|
-
if
|
330
|
-
|
373
|
+
if model_code == 'R0178'
|
374
|
+
status.update(
|
331
375
|
input_mode: INPUT_MODE_R0178.fetch(data[11]),
|
332
376
|
sampling: data[32],
|
333
377
|
sample_rate: SAMPLE_RATE_R0178.fetch(data[32]),
|
334
378
|
)
|
335
379
|
end
|
336
380
|
end
|
337
|
-
|
381
|
+
|
382
|
+
@model_code, @version, @status_string =
|
383
|
+
model_code, version, status_string
|
384
|
+
@status = status
|
338
385
|
end
|
339
386
|
end
|
340
387
|
|
341
388
|
def remote_command(cmd)
|
342
|
-
|
343
|
-
|
389
|
+
with_lock do
|
390
|
+
with_retry do
|
391
|
+
dispatch("#{STX}0#{cmd}#{ETX}")
|
392
|
+
end
|
344
393
|
end
|
345
394
|
end
|
346
395
|
|
347
396
|
def system_command(cmd)
|
348
|
-
|
349
|
-
|
397
|
+
with_lock do
|
398
|
+
with_retry do
|
399
|
+
dispatch("#{STX}2#{cmd}#{ETX}")
|
400
|
+
end
|
350
401
|
end
|
351
402
|
end
|
352
403
|
|
@@ -379,7 +430,9 @@ module Seriamp
|
|
379
430
|
if try <= retries
|
380
431
|
logger&.warn("Error during operation: #{exc.class}: #{exc} - will retry")
|
381
432
|
try += 1
|
382
|
-
|
433
|
+
if detect_device?
|
434
|
+
@device = nil
|
435
|
+
end
|
383
436
|
retry
|
384
437
|
else
|
385
438
|
raise
|
data/seriamp.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seriamp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleg Pudeyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-01-
|
11
|
+
date: 2023-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: serialport
|