seriamp 0.1.14 → 0.2.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 +4 -4
- data/Gemfile +8 -0
- data/Gemfile.lock +55 -0
- data/README.md +30 -4
- data/bin/integra +12 -0
- data/bin/sonamp +1 -1
- data/bin/sonamp-auto-power +56 -0
- data/bin/yamaha +1 -1
- data/lib/seriamp/all.rb +4 -0
- data/lib/seriamp/backend/serial_port.rb +1 -1
- data/lib/seriamp/backend.rb +6 -0
- data/lib/seriamp/faraday_facade.rb +89 -0
- data/lib/seriamp/integra/client.rb +192 -0
- data/lib/seriamp/integra/cmd.rb +81 -0
- data/lib/seriamp/integra/executor.rb +32 -0
- data/lib/seriamp/integra/protocol/constants.rb +13 -0
- data/lib/seriamp/integra/protocol/methods.rb +13 -0
- data/lib/seriamp/integra.rb +3 -0
- data/lib/seriamp/sonamp/app.rb +16 -3
- data/lib/seriamp/sonamp/auto_power.rb +167 -0
- data/lib/seriamp/sonamp/client.rb +19 -18
- data/lib/seriamp/sonamp/cmd.rb +8 -30
- data/lib/seriamp/sonamp/executor.rb +56 -0
- data/lib/seriamp/utils.rb +17 -0
- data/lib/seriamp/version.rb +1 -1
- data/lib/seriamp/yamaha/client.rb +19 -27
- data/lib/seriamp/yamaha/cmd.rb +4 -2
- data/lib/seriamp/yamaha/executor.rb +1 -0
- data/seriamp.gemspec +6 -2
- data/spec/sonamp/app_spec.rb +42 -0
- data/spec/sonamp/client_spec.rb +11 -0
- data/spec/sonamp/cmd_spec.rb +11 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/yamaha/app_spec.rb +37 -0
- data/spec/yamaha/client_spec.rb +11 -0
- data/spec/yamaha/cmd_spec.rb +82 -0
- metadata +78 -5
data/lib/seriamp/sonamp/app.rb
CHANGED
@@ -22,6 +22,13 @@ module Seriamp
|
|
22
22
|
render_json(client.get_zone_power)
|
23
23
|
end
|
24
24
|
|
25
|
+
post '/off' do
|
26
|
+
1.upto(4) do |zone|
|
27
|
+
client.set_zone_power(zone, false)
|
28
|
+
end
|
29
|
+
render_json(client.get_zone_power)
|
30
|
+
end
|
31
|
+
|
25
32
|
get '/volume' do
|
26
33
|
payload = {
|
27
34
|
zone_volume: client.get_zone_volume,
|
@@ -46,7 +53,7 @@ module Seriamp
|
|
46
53
|
end
|
47
54
|
|
48
55
|
put '/zone/:zone/volume' do |zone|
|
49
|
-
volume = request.body.read
|
56
|
+
volume = Integer(request.body.read)
|
50
57
|
client.set_zone_volume(Integer(zone), volume)
|
51
58
|
end
|
52
59
|
|
@@ -60,7 +67,7 @@ module Seriamp
|
|
60
67
|
end
|
61
68
|
|
62
69
|
put '/channel/:channel/volume' do |channel|
|
63
|
-
volume = request.body.read
|
70
|
+
volume = Integer(request.body.read)
|
64
71
|
client.set_channel_volume(Integer(channel), volume)
|
65
72
|
end
|
66
73
|
|
@@ -70,13 +77,19 @@ module Seriamp
|
|
70
77
|
end
|
71
78
|
|
72
79
|
post '/' do
|
80
|
+
executor = Executor.new
|
81
|
+
request.body.read.split("\n").each do |line|
|
82
|
+
args = line.strip.split(/\s+/)
|
83
|
+
executor.run_command(args)
|
84
|
+
end
|
85
|
+
render_json({})
|
73
86
|
end
|
74
87
|
|
75
88
|
private
|
76
89
|
|
77
90
|
def client
|
78
91
|
settings.client || begin
|
79
|
-
@client ||= Sonamp::Client.new(settings.device,
|
92
|
+
@client ||= Sonamp::Client.new(device: settings.device,
|
80
93
|
logger: settings.logger, retries: settings.retries, thread_safe: true)
|
81
94
|
end
|
82
95
|
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
autoload :JSON, 'json'
|
2
|
+
autoload :FileUtils, 'fileutils'
|
3
|
+
require 'seriamp/faraday_facade'
|
4
|
+
require 'seriamp/utils'
|
5
|
+
|
6
|
+
module Seriamp
|
7
|
+
module Sonamp
|
8
|
+
class AutoPower
|
9
|
+
def initialize(**opts)
|
10
|
+
@options = opts.dup.freeze
|
11
|
+
|
12
|
+
unless options[:sonamp_url]
|
13
|
+
raise ArgumentError, 'Sonamp URL is required'
|
14
|
+
end
|
15
|
+
unless options[:yamaha_url]
|
16
|
+
raise ArgumentError, 'Yamaha URL is required'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :options
|
21
|
+
|
22
|
+
def logger
|
23
|
+
options[:logger]
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
if (state_path = options[:state_path]) && File.exist?(state_path)
|
28
|
+
begin
|
29
|
+
@stored_sonamp_power = File.open(state_path) do |f|
|
30
|
+
JSON.load(f)
|
31
|
+
end
|
32
|
+
rescue JSON::ParserError => exc
|
33
|
+
logger&.warn("Failed to load state: #{exc.class}: #{exc}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
bump('application start')
|
38
|
+
|
39
|
+
prev_sonamp_power = nil
|
40
|
+
prev_sonamp_on = nil
|
41
|
+
handle_exceptions do
|
42
|
+
prev_sonamp_power = sonamp_client.get_json('power')
|
43
|
+
store_sonamp_power(prev_sonamp_power, prev_sonamp_power)
|
44
|
+
prev_sonamp_on = prev_sonamp_power.values.any? { |v| v == true }
|
45
|
+
end
|
46
|
+
|
47
|
+
loop do
|
48
|
+
sonamp_power = nil
|
49
|
+
sonamp_on = nil
|
50
|
+
handle_exceptions do
|
51
|
+
sonamp_power = sonamp_client.get_json('power')
|
52
|
+
sonamp_on = sonamp_power.values.any? { |v| v == true }
|
53
|
+
if sonamp_on && prev_sonamp_on == false
|
54
|
+
bump('amplifier turned on')
|
55
|
+
end
|
56
|
+
store_sonamp_power(prev_sonamp_power, sonamp_power)
|
57
|
+
prev_sonamp_power = sonamp_power
|
58
|
+
prev_sonamp_on = sonamp_on
|
59
|
+
end
|
60
|
+
|
61
|
+
# If we cannot query the receiver, assume it is on to prevent unintended
|
62
|
+
# turn-offs.
|
63
|
+
receiver_power = nil
|
64
|
+
handle_exceptions do
|
65
|
+
receiver_power = case resp = yamaha_client.get!('power')
|
66
|
+
when 'true'
|
67
|
+
true
|
68
|
+
when 'false'
|
69
|
+
false
|
70
|
+
else
|
71
|
+
raise "Unknown yamaha power response: #{resp}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
case receiver_power
|
75
|
+
when true
|
76
|
+
bump('receiver is on')
|
77
|
+
if sonamp_on == false
|
78
|
+
puts("turning on amplifier")
|
79
|
+
sonamp_set_on
|
80
|
+
end
|
81
|
+
when nil
|
82
|
+
bump('failed to communicate with receiver - assuming it is on')
|
83
|
+
end
|
84
|
+
|
85
|
+
delta = (@alive_through - Utils.monotime).to_i
|
86
|
+
if delta < 0 && sonamp_on
|
87
|
+
logger&.info("Turning amplifier off")
|
88
|
+
handle_exceptions do
|
89
|
+
sonamp_client.post!('off')
|
90
|
+
end
|
91
|
+
elsif ttl > 0
|
92
|
+
puts "TTL: #{delta / 60}:#{'%02d' % (delta % 60)}"
|
93
|
+
end
|
94
|
+
|
95
|
+
sleep 20
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
attr_reader :stored_sonamp_power
|
102
|
+
|
103
|
+
def handle_exceptions
|
104
|
+
yield
|
105
|
+
rescue Interrupt, SystemExit, NoMemoryError
|
106
|
+
raise
|
107
|
+
rescue => exc
|
108
|
+
logger&.warn("Unhandled exception: #{exc.class}: #{exc}")
|
109
|
+
end
|
110
|
+
|
111
|
+
def bump(reason)
|
112
|
+
if ttl > 0
|
113
|
+
logger&.debug("Bumping #{ttl} seconds: #{reason}")
|
114
|
+
end
|
115
|
+
@alive_through = Utils.monotime + ttl*60
|
116
|
+
end
|
117
|
+
|
118
|
+
def sonamp_client
|
119
|
+
@sonamp_client ||= FaradayFacade.new(
|
120
|
+
url: options.fetch(:sonamp_url),
|
121
|
+
timeout: options[:sonamp_timeout] || 3,
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
def yamaha_client
|
126
|
+
@yamaha_client ||= FaradayFacade.new(
|
127
|
+
url: options.fetch(:yamaha_url),
|
128
|
+
timeout: options[:yamaha_timeout] || 5,
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
def ttl
|
133
|
+
@ttl ||= begin
|
134
|
+
options[:ttl] || 0
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def store_sonamp_power(prev_sonamp_power, sonamp_power)
|
139
|
+
# Wait for the power state to be stable - both readings should be
|
140
|
+
# the same.
|
141
|
+
if prev_sonamp_power == sonamp_power && sonamp_power.values.any? { |v| v == true }
|
142
|
+
@stored_sonamp_power = sonamp_power
|
143
|
+
if state_path = options[:state_path]
|
144
|
+
File.open(state_path + '.part', 'w') do |f|
|
145
|
+
f << JSON.dump(sonamp_power)
|
146
|
+
end
|
147
|
+
FileUtils.mv(state_path + '.part', state_path)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def sonamp_set_on
|
153
|
+
if stored_sonamp_power
|
154
|
+
logger&.debug("sonamp on")
|
155
|
+
stored_sonamp_power.each do |zone, value|
|
156
|
+
if value
|
157
|
+
logger&.debug("Sonamp: zone #{zone} on")
|
158
|
+
sonamp_client.put!("zone/#{zone}/power", body: 'true')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
else
|
162
|
+
logger&.warn("No stored sonamp power")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'timeout'
|
4
4
|
require 'seriamp/error'
|
5
|
-
require 'seriamp/backend
|
5
|
+
require 'seriamp/backend'
|
6
6
|
|
7
7
|
module Seriamp
|
8
8
|
module Sonamp
|
@@ -224,14 +224,8 @@ module Seriamp
|
|
224
224
|
logger&.debug("Opening #{device}")
|
225
225
|
@io = Backend::SerialPortBackend::Device.new(device, logger: logger)
|
226
226
|
|
227
|
-
|
228
|
-
|
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
|
227
|
+
Utils.consume_data(@io.io, logger,
|
228
|
+
"Serial device readable after opening - unread previous response?")
|
235
229
|
|
236
230
|
begin
|
237
231
|
yield @io
|
@@ -275,9 +269,8 @@ module Seriamp
|
|
275
269
|
read_line(@io, cmd)
|
276
270
|
end
|
277
271
|
|
278
|
-
|
279
|
-
|
280
|
-
end
|
272
|
+
Utils.consume_data(@io.io, logger,
|
273
|
+
"Serial device readable after completely reading status response - concurrent access?")
|
281
274
|
|
282
275
|
if resp_lines_range_or_count == 1
|
283
276
|
resp.first
|
@@ -318,15 +311,23 @@ module Seriamp
|
|
318
311
|
def read_line(f, cmd)
|
319
312
|
with_timeout do
|
320
313
|
resp = +''
|
314
|
+
deadline = Utils.monotime + 1
|
321
315
|
loop do
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
316
|
+
begin
|
317
|
+
buf = f.read_nonblock(1024)
|
318
|
+
if buf
|
319
|
+
resp += buf
|
320
|
+
break if buf[-1] == ?\r
|
321
|
+
end
|
322
|
+
rescue IO::WaitReadable
|
323
|
+
budget = deadline - Utils.monotime
|
324
|
+
if budget < 0
|
325
|
+
raise CommunicationTimeout
|
326
|
+
end
|
327
|
+
IO.select([f.io], nil, nil, budget)
|
328
328
|
end
|
329
329
|
end
|
330
|
+
resp.strip!
|
330
331
|
if resp == 'ERR'
|
331
332
|
raise InvalidCommand, "Invalid command: #{cmd}"
|
332
333
|
elsif resp == 'N/A'
|
data/lib/seriamp/sonamp/cmd.rb
CHANGED
@@ -5,11 +5,12 @@ require 'logger'
|
|
5
5
|
require 'seriamp/utils'
|
6
6
|
require 'seriamp/detect'
|
7
7
|
require 'seriamp/sonamp/client'
|
8
|
+
require 'seriamp/sonamp/executor'
|
8
9
|
|
9
10
|
module Seriamp
|
10
11
|
module Sonamp
|
11
12
|
class Cmd
|
12
|
-
def initialize(args)
|
13
|
+
def initialize(args = ARGV, stdin = STDIN)
|
13
14
|
args = args.dup
|
14
15
|
|
15
16
|
options = {}
|
@@ -27,16 +28,18 @@ module Seriamp
|
|
27
28
|
@client = Sonamp::Client.new(device: options[:device], logger: @logger)
|
28
29
|
|
29
30
|
@args = args
|
31
|
+
@stdin = stdin
|
30
32
|
end
|
31
33
|
|
32
34
|
attr_reader :args
|
35
|
+
attr_reader :stdin
|
33
36
|
attr_reader :logger
|
34
37
|
|
35
38
|
def run
|
36
39
|
if args.any?
|
37
40
|
run_command(args)
|
38
41
|
else
|
39
|
-
|
42
|
+
stdin.each_line do |line|
|
40
43
|
line.strip!
|
41
44
|
line.sub!(/#.*/, '')
|
42
45
|
next if line.empty?
|
@@ -54,43 +57,18 @@ module Seriamp
|
|
54
57
|
|
55
58
|
case cmd
|
56
59
|
when 'detect'
|
57
|
-
device = Seriamp.detect_device(
|
60
|
+
device = Seriamp.detect_device(Yamaha, *args, logger: logger)
|
58
61
|
if device
|
59
62
|
puts device
|
60
63
|
exit 0
|
61
64
|
else
|
62
|
-
STDERR.puts("
|
65
|
+
STDERR.puts("Yamaha receiver not found")
|
63
66
|
exit 3
|
64
67
|
end
|
65
|
-
when 'off'
|
66
|
-
client.set_zone_power(1, false)
|
67
|
-
client.set_zone_power(2, false)
|
68
|
-
client.set_zone_power(3, false)
|
69
|
-
client.set_zone_power(4, false)
|
70
|
-
when 'power'
|
71
|
-
zone = args.shift.to_i
|
72
|
-
state = Utils.parse_on_off(args.shift)
|
73
|
-
client.set_zone_power(zone, state)
|
74
|
-
when 'zvol'
|
75
|
-
zone = args.shift.to_i
|
76
|
-
volume = args.shift.to_i
|
77
|
-
client.set_zone_volume(zone, volume)
|
78
|
-
when 'cvol'
|
79
|
-
channel = args.shift.to_i
|
80
|
-
volume = args.shift.to_i
|
81
|
-
client.set_channel_volume(channel, volume)
|
82
|
-
when 'zmute'
|
83
|
-
zone = args.shift.to_i
|
84
|
-
mute = args.shift.to_i
|
85
|
-
client.set_zone_mute(zone, mute)
|
86
|
-
when 'cmute'
|
87
|
-
channel = args.shift.to_i
|
88
|
-
mute = args.shift.to_i
|
89
|
-
client.set_channel_mute(channel, mute)
|
90
68
|
when 'status'
|
91
69
|
pp client.status
|
92
70
|
else
|
93
|
-
|
71
|
+
executor.run_command(cmd, *args)
|
94
72
|
end
|
95
73
|
end
|
96
74
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seriamp
|
4
|
+
module Sonamp
|
5
|
+
class Executor
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :client
|
11
|
+
|
12
|
+
def run_command(cmd, *args)
|
13
|
+
case cmd
|
14
|
+
when 'detect'
|
15
|
+
device = Seriamp.detect_device(Sonamp, *args, logger: logger)
|
16
|
+
if device
|
17
|
+
puts device
|
18
|
+
exit 0
|
19
|
+
else
|
20
|
+
STDERR.puts("Yamaha receiver not found")
|
21
|
+
exit 3
|
22
|
+
end
|
23
|
+
when 'off'
|
24
|
+
client.set_zone_power(1, false)
|
25
|
+
client.set_zone_power(2, false)
|
26
|
+
client.set_zone_power(3, false)
|
27
|
+
client.set_zone_power(4, false)
|
28
|
+
when 'power'
|
29
|
+
zone = Integer(args.shift)
|
30
|
+
state = Utils.parse_on_off(args.shift)
|
31
|
+
client.set_zone_power(zone, state)
|
32
|
+
when 'zvol'
|
33
|
+
zone = Integer(args.shift)
|
34
|
+
volume = Integer(args.shift)
|
35
|
+
client.set_zone_volume(zone, volume)
|
36
|
+
when 'cvol'
|
37
|
+
channel = Integer(args.shift)
|
38
|
+
volume = Integer(args.shift)
|
39
|
+
client.set_channel_volume(channel, volume)
|
40
|
+
when 'zmute'
|
41
|
+
zone = Integer(args.shift)
|
42
|
+
mute = Integer(args.shift)
|
43
|
+
client.set_zone_mute(zone, mute)
|
44
|
+
when 'cmute'
|
45
|
+
channel = Integer(args.shift)
|
46
|
+
mute = Integer(args.shift)
|
47
|
+
client.set_channel_mute(channel, mute)
|
48
|
+
when 'status'
|
49
|
+
client.status
|
50
|
+
else
|
51
|
+
raise ArgumentError, "Unknown command: #{cmd}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/seriamp/utils.rb
CHANGED
@@ -17,5 +17,22 @@ module Seriamp
|
|
17
17
|
module_function def monotime
|
18
18
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
19
19
|
end
|
20
|
+
|
21
|
+
module_function def consume_data(io, logger, msg)
|
22
|
+
warned = false
|
23
|
+
read_bytes = 0
|
24
|
+
while IO.select([io], nil, nil, 0)
|
25
|
+
unless warned
|
26
|
+
logger&.warn(msg)
|
27
|
+
warned = true
|
28
|
+
end
|
29
|
+
buf = io.read_nonblock(1024)
|
30
|
+
read_bytes += buf.length
|
31
|
+
end
|
32
|
+
if read_bytes > 0
|
33
|
+
logger&.warn("Consumed #{read_bytes} bytes")
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
end
|
20
37
|
end
|
21
38
|
end
|
data/lib/seriamp/version.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'timeout'
|
4
4
|
require 'seriamp/utils'
|
5
|
-
require 'seriamp/backend
|
5
|
+
require 'seriamp/backend'
|
6
6
|
require 'seriamp/yamaha/protocol/methods'
|
7
7
|
|
8
8
|
module Seriamp
|
@@ -197,14 +197,8 @@ module Seriamp
|
|
197
197
|
logger&.debug("Opening #{device}")
|
198
198
|
@io = Backend::SerialPortBackend::Device.new(device, logger: logger)
|
199
199
|
|
200
|
-
|
201
|
-
|
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
|
200
|
+
Utils.consume_data(@io.io, logger,
|
201
|
+
"Serial device readable after opening - unread previous response?")
|
208
202
|
|
209
203
|
begin
|
210
204
|
tries = 0
|
@@ -251,15 +245,20 @@ module Seriamp
|
|
251
245
|
|
252
246
|
def read_response
|
253
247
|
resp = +''
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
sleep 0.1
|
248
|
+
deadline = Utils.monotime + 1
|
249
|
+
loop do
|
250
|
+
begin
|
251
|
+
chunk = @io.read_nonblock(1000)
|
252
|
+
if chunk
|
253
|
+
resp += chunk
|
254
|
+
break if chunk[-1] == ETX
|
262
255
|
end
|
256
|
+
rescue IO::WaitReadable
|
257
|
+
budget = deadline - Utils.monotime
|
258
|
+
if budget < 0
|
259
|
+
raise CommunicationTimeout
|
260
|
+
end
|
261
|
+
IO.select([@io.io], nil, nil, budget)
|
263
262
|
end
|
264
263
|
end
|
265
264
|
resp
|
@@ -296,16 +295,9 @@ module Seriamp
|
|
296
295
|
def do_status
|
297
296
|
with_retry do
|
298
297
|
resp = nil
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
while @io && IO.select([@io.io], nil, nil, 0)
|
303
|
-
logger&.warn("Serial device readable after completely reading status response - concurrent access?")
|
304
|
-
read_response
|
305
|
-
again = true
|
306
|
-
end
|
307
|
-
break unless again
|
308
|
-
end
|
298
|
+
resp = dispatch(STATUS_REQ)
|
299
|
+
Utils.consume_data(@io.io, logger,
|
300
|
+
"Serial device readable after completely reading status response - concurrent access?")
|
309
301
|
if resp.length < 10
|
310
302
|
raise HandshakeFailure, "Broken status response: expected at least 10 bytes, got #{resp.length} bytes; concurrent operation on device?"
|
311
303
|
end
|
data/lib/seriamp/yamaha/cmd.rb
CHANGED
@@ -11,7 +11,7 @@ require 'seriamp/yamaha/executor'
|
|
11
11
|
module Seriamp
|
12
12
|
module Yamaha
|
13
13
|
class Cmd
|
14
|
-
def initialize(args)
|
14
|
+
def initialize(args = ARGV, stdin = STDIN)
|
15
15
|
options = {}
|
16
16
|
OptionParser.new do |opts|
|
17
17
|
opts.banner = "Usage: yamaha [-d device] command arg..."
|
@@ -27,16 +27,18 @@ module Seriamp
|
|
27
27
|
@client = Yamaha::Client.new(device: options[:device], logger: @logger)
|
28
28
|
|
29
29
|
@args = args
|
30
|
+
@stdin = stdin
|
30
31
|
end
|
31
32
|
|
32
33
|
attr_reader :args
|
34
|
+
attr_reader :stdin
|
33
35
|
attr_reader :logger
|
34
36
|
|
35
37
|
def run
|
36
38
|
if args.any?
|
37
39
|
run_command(args)
|
38
40
|
else
|
39
|
-
|
41
|
+
stdin.each_line do |line|
|
40
42
|
line.strip!
|
41
43
|
line.sub!(/#.*/, '')
|
42
44
|
next if line.empty?
|
data/seriamp.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = "seriamp"
|
5
|
-
spec.version = '0.
|
5
|
+
spec.version = '0.2.0'
|
6
6
|
spec.authors = ['Oleg Pudeyev']
|
7
7
|
spec.email = ['code@olegp.name']
|
8
8
|
spec.summary = %q{Serial control for amplifiers & A/V receivers}
|
@@ -15,7 +15,11 @@ Gem::Specification.new do |spec|
|
|
15
15
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
16
|
spec.require_paths = ["lib"]
|
17
17
|
|
18
|
-
spec.add_runtime_dependency 'serialport', '~> 1.
|
18
|
+
spec.add_runtime_dependency 'serialport', '~> 1.3'
|
19
|
+
|
20
|
+
spec.add_development_dependency 'rspec-core', '~> 3.12'
|
21
|
+
spec.add_development_dependency 'rspec-expectations', '~> 3.12'
|
22
|
+
spec.add_development_dependency 'rspec-mocks', '~> 3.12'
|
19
23
|
|
20
24
|
# Optional dependencies: sinatra for the web apps
|
21
25
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Seriamp::Sonamp::App do
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
describe '#initialize' do
|
9
|
+
it 'works' do
|
10
|
+
described_class.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:app) do
|
15
|
+
described_class.tap do |app|
|
16
|
+
app.client = client
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:client_cls) { Seriamp::Sonamp::Client }
|
21
|
+
let(:client) { double('sonamp client') }
|
22
|
+
|
23
|
+
describe '/off' do
|
24
|
+
let(:final_state) do
|
25
|
+
{'1' => false, '2' => false, '3' => false, '4' => false}
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'works' do
|
29
|
+
|
30
|
+
client.should receive(:set_zone_power).with(1, false)
|
31
|
+
client.should receive(:set_zone_power).with(2, false)
|
32
|
+
client.should receive(:set_zone_power).with(3, false)
|
33
|
+
client.should receive(:set_zone_power).with(4, false)
|
34
|
+
client.should receive(:get_zone_power).and_return(final_state)
|
35
|
+
|
36
|
+
post '/off'
|
37
|
+
|
38
|
+
last_response.status.should == 200
|
39
|
+
JSON.parse(last_response.body).should == final_state
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
ENV['RACK_ENV'] = 'test'
|
4
|
+
|
5
|
+
require 'byebug'
|
6
|
+
require 'seriamp/all'
|
7
|
+
require 'rack/test'
|
8
|
+
|
9
|
+
RSpec.configure do |rspec|
|
10
|
+
rspec.expect_with(:rspec) do |c|
|
11
|
+
c.syntax = [:should, :expect]
|
12
|
+
end
|
13
|
+
rspec.mock_with(:rspec) do |mocks|
|
14
|
+
mocks.syntax = [:should, :expect]
|
15
|
+
end
|
16
|
+
end
|